msi: Implement the RemoveEnvironmentStrings standard action.
[wine.git] / dlls / msi / action.c
blob08cf975a2002ef7bec6273b270fbb0794d66b617
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 * consts and values used
51 static const WCHAR c_colon[] = {'C',':','\\',0};
53 static const WCHAR szCreateFolders[] =
54 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
55 static const WCHAR szCostFinalize[] =
56 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
57 static const WCHAR szWriteRegistryValues[] =
58 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
59 static const WCHAR szCostInitialize[] =
60 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
61 static const WCHAR szFileCost[] =
62 {'F','i','l','e','C','o','s','t',0};
63 static const WCHAR szInstallInitialize[] =
64 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
65 static const WCHAR szInstallValidate[] =
66 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
67 static const WCHAR szLaunchConditions[] =
68 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
69 static const WCHAR szProcessComponents[] =
70 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
71 static const WCHAR szRegisterTypeLibraries[] =
72 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
73 static const WCHAR szCreateShortcuts[] =
74 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
75 static const WCHAR szPublishProduct[] =
76 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
77 static const WCHAR szWriteIniValues[] =
78 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
79 static const WCHAR szSelfRegModules[] =
80 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
81 static const WCHAR szPublishFeatures[] =
82 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
83 static const WCHAR szRegisterProduct[] =
84 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
85 static const WCHAR szInstallExecute[] =
86 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
87 static const WCHAR szInstallExecuteAgain[] =
88 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
89 static const WCHAR szInstallFinalize[] =
90 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
91 static const WCHAR szForceReboot[] =
92 {'F','o','r','c','e','R','e','b','o','o','t',0};
93 static const WCHAR szResolveSource[] =
94 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
95 static const WCHAR szAppSearch[] =
96 {'A','p','p','S','e','a','r','c','h',0};
97 static const WCHAR szAllocateRegistrySpace[] =
98 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
99 static const WCHAR szBindImage[] =
100 {'B','i','n','d','I','m','a','g','e',0};
101 static const WCHAR szCCPSearch[] =
102 {'C','C','P','S','e','a','r','c','h',0};
103 static const WCHAR szDeleteServices[] =
104 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
105 static const WCHAR szDisableRollback[] =
106 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
107 static const WCHAR szExecuteAction[] =
108 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
109 static const WCHAR szInstallAdminPackage[] =
110 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
111 static const WCHAR szInstallSFPCatalogFile[] =
112 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
113 static const WCHAR szIsolateComponents[] =
114 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
115 static const WCHAR szMigrateFeatureStates[] =
116 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
117 static const WCHAR szMoveFiles[] =
118 {'M','o','v','e','F','i','l','e','s',0};
119 static const WCHAR szMsiPublishAssemblies[] =
120 {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
121 static const WCHAR szMsiUnpublishAssemblies[] =
122 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
123 static const WCHAR szInstallODBC[] =
124 {'I','n','s','t','a','l','l','O','D','B','C',0};
125 static const WCHAR szInstallServices[] =
126 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
127 static const WCHAR szPatchFiles[] =
128 {'P','a','t','c','h','F','i','l','e','s',0};
129 static const WCHAR szPublishComponents[] =
130 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
131 static const WCHAR szRegisterComPlus[] =
132 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
133 static const WCHAR szRegisterFonts[] =
134 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
135 static const WCHAR szRegisterUser[] =
136 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
137 static const WCHAR szRemoveEnvironmentStrings[] =
138 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
139 static const WCHAR szRemoveExistingProducts[] =
140 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
141 static const WCHAR szRemoveFolders[] =
142 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
143 static const WCHAR szRemoveIniValues[] =
144 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
145 static const WCHAR szRemoveODBC[] =
146 {'R','e','m','o','v','e','O','D','B','C',0};
147 static const WCHAR szRemoveRegistryValues[] =
148 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
149 static const WCHAR szRemoveShortcuts[] =
150 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
151 static const WCHAR szRMCCPSearch[] =
152 {'R','M','C','C','P','S','e','a','r','c','h',0};
153 static const WCHAR szScheduleReboot[] =
154 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
155 static const WCHAR szSelfUnregModules[] =
156 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
157 static const WCHAR szSetODBCFolders[] =
158 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
159 static const WCHAR szStartServices[] =
160 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
161 static const WCHAR szStopServices[] =
162 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
163 static const WCHAR szUnpublishComponents[] =
164 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
165 static const WCHAR szUnpublishFeatures[] =
166 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
167 static const WCHAR szUnregisterClassInfo[] =
168 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
169 static const WCHAR szUnregisterComPlus[] =
170 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
171 static const WCHAR szUnregisterExtensionInfo[] =
172 {'U','n','r','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0};
173 static const WCHAR szUnregisterFonts[] =
174 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
175 static const WCHAR szUnregisterMIMEInfo[] =
176 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
177 static const WCHAR szUnregisterProgIdInfo[] =
178 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
179 static const WCHAR szUnregisterTypeLibraries[] =
180 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
181 static const WCHAR szValidateProductID[] =
182 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
183 static const WCHAR szWriteEnvironmentStrings[] =
184 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
186 /********************************************************
187 * helper functions
188 ********************************************************/
190 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
192 static const WCHAR Query_t[] =
193 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
194 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
195 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
196 ' ','\'','%','s','\'',0};
197 MSIRECORD * row;
199 row = MSI_QueryGetRecord( package->db, Query_t, action );
200 if (!row)
201 return;
202 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
203 msiobj_release(&row->hdr);
206 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
207 UINT rc)
209 MSIRECORD * row;
210 static const WCHAR template_s[]=
211 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
212 '%','s', '.',0};
213 static const WCHAR template_e[]=
214 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
215 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
216 '%','i','.',0};
217 static const WCHAR format[] =
218 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
219 WCHAR message[1024];
220 WCHAR timet[0x100];
222 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
223 if (start)
224 sprintfW(message,template_s,timet,action);
225 else
226 sprintfW(message,template_e,timet,action,rc);
228 row = MSI_CreateRecord(1);
229 MSI_RecordSetStringW(row,1,message);
231 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
232 msiobj_release(&row->hdr);
235 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
236 BOOL preserve_case )
238 LPCWSTR ptr,ptr2;
239 BOOL quote;
240 DWORD len;
241 LPWSTR prop = NULL, val = NULL;
243 if (!szCommandLine)
244 return ERROR_SUCCESS;
246 ptr = szCommandLine;
248 while (*ptr)
250 if (*ptr==' ')
252 ptr++;
253 continue;
256 TRACE("Looking at %s\n",debugstr_w(ptr));
258 ptr2 = strchrW(ptr,'=');
259 if (!ptr2)
261 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
262 break;
265 quote = FALSE;
267 len = ptr2-ptr;
268 prop = msi_alloc((len+1)*sizeof(WCHAR));
269 memcpy(prop,ptr,len*sizeof(WCHAR));
270 prop[len]=0;
272 if (!preserve_case)
273 struprW(prop);
275 ptr2++;
277 len = 0;
278 ptr = ptr2;
279 while (*ptr && (quote || (!quote && *ptr!=' ')))
281 if (*ptr == '"')
282 quote = !quote;
283 ptr++;
284 len++;
287 if (*ptr2=='"')
289 ptr2++;
290 len -= 2;
292 val = msi_alloc((len+1)*sizeof(WCHAR));
293 memcpy(val,ptr2,len*sizeof(WCHAR));
294 val[len] = 0;
296 if (lstrlenW(prop) > 0)
298 TRACE("Found commandline property (%s) = (%s)\n",
299 debugstr_w(prop), debugstr_w(val));
300 MSI_SetPropertyW(package,prop,val);
302 msi_free(val);
303 msi_free(prop);
306 return ERROR_SUCCESS;
310 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
312 LPCWSTR pc;
313 LPWSTR p, *ret = NULL;
314 UINT count = 0;
316 if (!str)
317 return ret;
319 /* count the number of substrings */
320 for ( pc = str, count = 0; pc; count++ )
322 pc = strchrW( pc, sep );
323 if (pc)
324 pc++;
327 /* allocate space for an array of substring pointers and the substrings */
328 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
329 (lstrlenW(str)+1) * sizeof(WCHAR) );
330 if (!ret)
331 return ret;
333 /* copy the string and set the pointers */
334 p = (LPWSTR) &ret[count+1];
335 lstrcpyW( p, str );
336 for( count = 0; (ret[count] = p); count++ )
338 p = strchrW( p, sep );
339 if (p)
340 *p++ = 0;
343 return ret;
346 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
348 static const WCHAR szSystemLanguageID[] =
349 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
351 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
352 UINT ret = ERROR_FUNCTION_FAILED;
354 prod_code = msi_dup_property( package, szProductCode );
355 patch_product = msi_get_suminfo_product( patch );
357 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
359 if ( strstrW( patch_product, prod_code ) )
361 MSISUMMARYINFO *si;
362 const WCHAR *p;
364 si = MSI_GetSummaryInformationW( patch, 0 );
365 if (!si)
367 ERR("no summary information!\n");
368 goto end;
371 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
372 if (!template)
374 ERR("no template property!\n");
375 msiobj_release( &si->hdr );
376 goto end;
379 if (!template[0])
381 ret = ERROR_SUCCESS;
382 msiobj_release( &si->hdr );
383 goto end;
386 langid = msi_dup_property( package, szSystemLanguageID );
387 if (!langid)
389 msiobj_release( &si->hdr );
390 goto end;
393 p = strchrW( template, ';' );
394 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
396 TRACE("applicable transform\n");
397 ret = ERROR_SUCCESS;
400 /* FIXME: check platform */
402 msiobj_release( &si->hdr );
405 end:
406 msi_free( patch_product );
407 msi_free( prod_code );
408 msi_free( template );
409 msi_free( langid );
411 return ret;
414 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
415 MSIDATABASE *patch_db, LPCWSTR name )
417 UINT ret = ERROR_FUNCTION_FAILED;
418 IStorage *stg = NULL;
419 HRESULT r;
421 TRACE("%p %s\n", package, debugstr_w(name) );
423 if (*name++ != ':')
425 ERR("expected a colon in %s\n", debugstr_w(name));
426 return ERROR_FUNCTION_FAILED;
429 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
430 if (SUCCEEDED(r))
432 ret = msi_check_transform_applicable( package, stg );
433 if (ret == ERROR_SUCCESS)
434 msi_table_apply_transform( package->db, stg );
435 else
436 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
437 IStorage_Release( stg );
439 else
440 ERR("failed to open substorage %s\n", debugstr_w(name));
442 return ERROR_SUCCESS;
445 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
447 LPWSTR guid_list, *guids, product_code;
448 UINT i, ret = ERROR_FUNCTION_FAILED;
450 product_code = msi_dup_property( package, szProductCode );
451 if (!product_code)
453 /* FIXME: the property ProductCode should be written into the DB somewhere */
454 ERR("no product code to check\n");
455 return ERROR_SUCCESS;
458 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
459 guids = msi_split_string( guid_list, ';' );
460 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
462 if (!lstrcmpW( guids[i], product_code ))
463 ret = ERROR_SUCCESS;
465 msi_free( guids );
466 msi_free( guid_list );
467 msi_free( product_code );
469 return ret;
472 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
474 MSIQUERY *view;
475 MSIRECORD *rec = NULL;
476 LPWSTR patch;
477 LPCWSTR prop;
478 UINT r;
480 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
481 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
482 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
483 '`','S','o','u','r','c','e','`',' ','I','S',' ',
484 'N','O','T',' ','N','U','L','L',0};
486 r = MSI_DatabaseOpenViewW(package->db, query, &view);
487 if (r != ERROR_SUCCESS)
488 return r;
490 r = MSI_ViewExecute(view, 0);
491 if (r != ERROR_SUCCESS)
492 goto done;
494 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
496 prop = MSI_RecordGetString(rec, 1);
497 patch = msi_dup_property(package, szPatch);
498 MSI_SetPropertyW(package, prop, patch);
499 msi_free(patch);
502 done:
503 if (rec) msiobj_release(&rec->hdr);
504 msiobj_release(&view->hdr);
506 return r;
509 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
511 MSISUMMARYINFO *si;
512 LPWSTR str, *substorage;
513 UINT i, r = ERROR_SUCCESS;
515 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
516 if (!si)
517 return ERROR_FUNCTION_FAILED;
519 if (msi_check_patch_applicable( package, si ) != ERROR_SUCCESS)
521 TRACE("Patch not applicable\n");
522 return ERROR_SUCCESS;
525 package->patch = msi_alloc(sizeof(MSIPATCHINFO));
526 if (!package->patch)
527 return ERROR_OUTOFMEMORY;
529 package->patch->patchcode = msi_suminfo_dup_string(si, PID_REVNUMBER);
530 if (!package->patch->patchcode)
531 return ERROR_OUTOFMEMORY;
533 /* enumerate the substorage */
534 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
535 package->patch->transforms = str;
537 substorage = msi_split_string( str, ';' );
538 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
539 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
541 msi_free( substorage );
542 msiobj_release( &si->hdr );
544 msi_set_media_source_prop(package);
546 return r;
549 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
551 MSIDATABASE *patch_db = NULL;
552 UINT r;
554 TRACE("%p %s\n", package, debugstr_w( file ) );
556 /* FIXME:
557 * We probably want to make sure we only open a patch collection here.
558 * Patch collections (.msp) and databases (.msi) have different GUIDs
559 * but currently MSI_OpenDatabaseW will accept both.
561 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
562 if ( r != ERROR_SUCCESS )
564 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
565 return r;
568 msi_parse_patch_summary( package, patch_db );
571 * There might be a CAB file in the patch package,
572 * so append it to the list of storage to search for streams.
574 append_storage_to_db( package->db, patch_db->storage );
576 msiobj_release( &patch_db->hdr );
578 return ERROR_SUCCESS;
581 /* get the PATCH property, and apply all the patches it specifies */
582 static UINT msi_apply_patches( MSIPACKAGE *package )
584 LPWSTR patch_list, *patches;
585 UINT i, r = ERROR_SUCCESS;
587 patch_list = msi_dup_property( package, szPatch );
589 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
591 patches = msi_split_string( patch_list, ';' );
592 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
593 r = msi_apply_patch_package( package, patches[i] );
595 msi_free( patches );
596 msi_free( patch_list );
598 return r;
601 static UINT msi_apply_transforms( MSIPACKAGE *package )
603 static const WCHAR szTransforms[] = {
604 'T','R','A','N','S','F','O','R','M','S',0 };
605 LPWSTR xform_list, *xforms;
606 UINT i, r = ERROR_SUCCESS;
608 xform_list = msi_dup_property( package, szTransforms );
609 xforms = msi_split_string( xform_list, ';' );
611 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
613 if (xforms[i][0] == ':')
614 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
615 else
616 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
619 msi_free( xforms );
620 msi_free( xform_list );
622 return r;
625 static BOOL ui_sequence_exists( MSIPACKAGE *package )
627 MSIQUERY *view;
628 UINT rc;
630 static const WCHAR ExecSeqQuery [] =
631 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
632 '`','I','n','s','t','a','l','l',
633 'U','I','S','e','q','u','e','n','c','e','`',
634 ' ','W','H','E','R','E',' ',
635 '`','S','e','q','u','e','n','c','e','`',' ',
636 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
637 '`','S','e','q','u','e','n','c','e','`',0};
639 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
640 if (rc == ERROR_SUCCESS)
642 msiobj_release(&view->hdr);
643 return TRUE;
646 return FALSE;
649 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
651 LPWSTR p, db;
652 LPWSTR source, check;
653 DWORD len;
655 static const WCHAR szOriginalDatabase[] =
656 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
658 db = msi_dup_property( package, szOriginalDatabase );
659 if (!db)
660 return ERROR_OUTOFMEMORY;
662 p = strrchrW( db, '\\' );
663 if (!p)
665 p = strrchrW( db, '/' );
666 if (!p)
668 msi_free(db);
669 return ERROR_SUCCESS;
673 len = p - db + 2;
674 source = msi_alloc( len * sizeof(WCHAR) );
675 lstrcpynW( source, db, len );
677 check = msi_dup_property( package, cszSourceDir );
678 if (!check || replace)
679 MSI_SetPropertyW( package, cszSourceDir, source );
681 msi_free( check );
683 check = msi_dup_property( package, cszSOURCEDIR );
684 if (!check || replace)
685 MSI_SetPropertyW( package, cszSOURCEDIR, source );
687 msi_free( check );
688 msi_free( source );
689 msi_free( db );
691 return ERROR_SUCCESS;
694 static BOOL needs_ui_sequence(MSIPACKAGE *package)
696 INT level = msi_get_property_int(package, szUILevel, 0);
697 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
700 static UINT msi_set_context(MSIPACKAGE *package)
702 WCHAR val[10];
703 DWORD sz = 10;
704 DWORD num;
705 UINT r;
707 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
709 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
710 if (r == ERROR_SUCCESS)
712 num = atolW(val);
713 if (num == 1 || num == 2)
714 package->Context = MSIINSTALLCONTEXT_MACHINE;
717 return ERROR_SUCCESS;
720 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
722 UINT rc;
723 LPCWSTR cond, action;
724 MSIPACKAGE *package = param;
726 action = MSI_RecordGetString(row,1);
727 if (!action)
729 ERR("Error is retrieving action name\n");
730 return ERROR_FUNCTION_FAILED;
733 /* check conditions */
734 cond = MSI_RecordGetString(row,2);
736 /* this is a hack to skip errors in the condition code */
737 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
739 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
740 return ERROR_SUCCESS;
743 if (needs_ui_sequence(package))
744 rc = ACTION_PerformUIAction(package, action, -1);
745 else
746 rc = ACTION_PerformAction(package, action, -1, FALSE);
748 msi_dialog_check_messages( NULL );
750 if (package->CurrentInstallState != ERROR_SUCCESS)
751 rc = package->CurrentInstallState;
753 if (rc == ERROR_FUNCTION_NOT_CALLED)
754 rc = ERROR_SUCCESS;
756 if (rc != ERROR_SUCCESS)
757 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
759 return rc;
762 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
764 MSIQUERY * view;
765 UINT r;
766 static const WCHAR query[] =
767 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
768 '`','%','s','`',
769 ' ','W','H','E','R','E',' ',
770 '`','S','e','q','u','e','n','c','e','`',' ',
771 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
772 '`','S','e','q','u','e','n','c','e','`',0};
774 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
776 r = MSI_OpenQuery( package->db, &view, query, szTable );
777 if (r == ERROR_SUCCESS)
779 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
780 msiobj_release(&view->hdr);
783 return r;
786 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
788 MSIQUERY * view;
789 UINT rc;
790 static const WCHAR ExecSeqQuery[] =
791 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
792 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
793 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
794 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
795 'O','R','D','E','R',' ', 'B','Y',' ',
796 '`','S','e','q','u','e','n','c','e','`',0 };
797 static const WCHAR IVQuery[] =
798 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
799 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
800 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
801 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
802 ' ','\'', 'I','n','s','t','a','l','l',
803 'V','a','l','i','d','a','t','e','\'', 0};
804 INT seq = 0;
806 if (package->script->ExecuteSequenceRun)
808 TRACE("Execute Sequence already Run\n");
809 return ERROR_SUCCESS;
812 package->script->ExecuteSequenceRun = TRUE;
814 /* get the sequence number */
815 if (UIran)
817 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
818 if( !row )
819 return ERROR_FUNCTION_FAILED;
820 seq = MSI_RecordGetInteger(row,1);
821 msiobj_release(&row->hdr);
824 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
825 if (rc == ERROR_SUCCESS)
827 TRACE("Running the actions\n");
829 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
830 msiobj_release(&view->hdr);
833 return rc;
836 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
838 MSIQUERY * view;
839 UINT rc;
840 static const WCHAR ExecSeqQuery [] =
841 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
842 '`','I','n','s','t','a','l','l',
843 'U','I','S','e','q','u','e','n','c','e','`',
844 ' ','W','H','E','R','E',' ',
845 '`','S','e','q','u','e','n','c','e','`',' ',
846 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
847 '`','S','e','q','u','e','n','c','e','`',0};
849 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
850 if (rc == ERROR_SUCCESS)
852 TRACE("Running the actions\n");
854 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
855 msiobj_release(&view->hdr);
858 return rc;
861 /********************************************************
862 * ACTION helper functions and functions that perform the actions
863 *******************************************************/
864 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
865 UINT* rc, UINT script, BOOL force )
867 BOOL ret=FALSE;
868 UINT arc;
870 arc = ACTION_CustomAction(package, action, script, force);
872 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
874 *rc = arc;
875 ret = TRUE;
877 return ret;
881 * Actual Action Handlers
884 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
886 MSIPACKAGE *package = param;
887 LPCWSTR dir, component;
888 LPWSTR full_path;
889 MSIRECORD *uirow;
890 MSIFOLDER *folder;
891 MSICOMPONENT *comp;
893 component = MSI_RecordGetString(row, 2);
894 comp = get_loaded_component(package, component);
895 if (!comp)
896 return ERROR_SUCCESS;
898 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
900 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
901 comp->Action = comp->Installed;
902 return ERROR_SUCCESS;
904 comp->Action = INSTALLSTATE_LOCAL;
906 dir = MSI_RecordGetString(row,1);
907 if (!dir)
909 ERR("Unable to get folder id\n");
910 return ERROR_SUCCESS;
913 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
914 if (!full_path)
916 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
917 return ERROR_SUCCESS;
920 TRACE("Folder is %s\n",debugstr_w(full_path));
922 /* UI stuff */
923 uirow = MSI_CreateRecord(1);
924 MSI_RecordSetStringW(uirow,1,full_path);
925 ui_actiondata(package,szCreateFolders,uirow);
926 msiobj_release( &uirow->hdr );
928 if (folder->State == 0)
929 create_full_pathW(full_path);
931 folder->State = 3;
933 msi_free(full_path);
934 return ERROR_SUCCESS;
937 /* FIXME: probably should merge this with the above function */
938 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
940 UINT rc = ERROR_SUCCESS;
941 MSIFOLDER *folder;
942 LPWSTR install_path;
944 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
945 if (!install_path)
946 return ERROR_FUNCTION_FAILED;
948 /* create the path */
949 if (folder->State == 0)
951 create_full_pathW(install_path);
952 folder->State = 2;
954 msi_free(install_path);
956 return rc;
959 UINT msi_create_component_directories( MSIPACKAGE *package )
961 MSICOMPONENT *comp;
963 /* create all the folders required by the components are going to install */
964 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
966 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
967 continue;
968 msi_create_directory( package, comp->Directory );
971 return ERROR_SUCCESS;
974 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
976 static const WCHAR ExecSeqQuery[] =
977 {'S','E','L','E','C','T',' ',
978 '`','D','i','r','e','c','t','o','r','y','_','`',
979 ' ','F','R','O','M',' ',
980 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
981 UINT rc;
982 MSIQUERY *view;
984 /* create all the empty folders specified in the CreateFolder table */
985 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
986 if (rc != ERROR_SUCCESS)
987 return ERROR_SUCCESS;
989 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
990 msiobj_release(&view->hdr);
992 return rc;
995 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
997 MSIPACKAGE *package = param;
998 LPCWSTR dir, component;
999 LPWSTR full_path;
1000 MSIRECORD *uirow;
1001 MSIFOLDER *folder;
1002 MSICOMPONENT *comp;
1004 component = MSI_RecordGetString(row, 2);
1005 comp = get_loaded_component(package, component);
1006 if (!comp)
1007 return ERROR_SUCCESS;
1009 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1011 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1012 comp->Action = comp->Installed;
1013 return ERROR_SUCCESS;
1015 comp->Action = INSTALLSTATE_ABSENT;
1017 dir = MSI_RecordGetString( row, 1 );
1018 if (!dir)
1020 ERR("Unable to get folder id\n");
1021 return ERROR_SUCCESS;
1024 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1025 if (!full_path)
1027 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1028 return ERROR_SUCCESS;
1031 TRACE("folder is %s\n", debugstr_w(full_path));
1033 uirow = MSI_CreateRecord( 1 );
1034 MSI_RecordSetStringW( uirow, 1, full_path );
1035 ui_actiondata( package, szRemoveFolders, uirow );
1036 msiobj_release( &uirow->hdr );
1038 RemoveDirectoryW( full_path );
1039 folder->State = 0;
1041 msi_free( full_path );
1042 return ERROR_SUCCESS;
1045 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1047 static const WCHAR query[] =
1048 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1049 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1051 MSIQUERY *view;
1052 UINT rc;
1054 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1055 if (rc != ERROR_SUCCESS)
1056 return ERROR_SUCCESS;
1058 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1059 msiobj_release( &view->hdr );
1061 return rc;
1064 static UINT load_component( MSIRECORD *row, LPVOID param )
1066 MSIPACKAGE *package = param;
1067 MSICOMPONENT *comp;
1069 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1070 if (!comp)
1071 return ERROR_FUNCTION_FAILED;
1073 list_add_tail( &package->components, &comp->entry );
1075 /* fill in the data */
1076 comp->Component = msi_dup_record_field( row, 1 );
1078 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1080 comp->ComponentId = msi_dup_record_field( row, 2 );
1081 comp->Directory = msi_dup_record_field( row, 3 );
1082 comp->Attributes = MSI_RecordGetInteger(row,4);
1083 comp->Condition = msi_dup_record_field( row, 5 );
1084 comp->KeyPath = msi_dup_record_field( row, 6 );
1086 comp->Installed = INSTALLSTATE_UNKNOWN;
1087 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1089 return ERROR_SUCCESS;
1092 static UINT load_all_components( MSIPACKAGE *package )
1094 static const WCHAR query[] = {
1095 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1096 '`','C','o','m','p','o','n','e','n','t','`',0 };
1097 MSIQUERY *view;
1098 UINT r;
1100 if (!list_empty(&package->components))
1101 return ERROR_SUCCESS;
1103 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1104 if (r != ERROR_SUCCESS)
1105 return r;
1107 r = MSI_IterateRecords(view, NULL, load_component, package);
1108 msiobj_release(&view->hdr);
1109 return r;
1112 typedef struct {
1113 MSIPACKAGE *package;
1114 MSIFEATURE *feature;
1115 } _ilfs;
1117 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1119 ComponentList *cl;
1121 cl = msi_alloc( sizeof (*cl) );
1122 if ( !cl )
1123 return ERROR_NOT_ENOUGH_MEMORY;
1124 cl->component = comp;
1125 list_add_tail( &feature->Components, &cl->entry );
1127 return ERROR_SUCCESS;
1130 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1132 FeatureList *fl;
1134 fl = msi_alloc( sizeof(*fl) );
1135 if ( !fl )
1136 return ERROR_NOT_ENOUGH_MEMORY;
1137 fl->feature = child;
1138 list_add_tail( &parent->Children, &fl->entry );
1140 return ERROR_SUCCESS;
1143 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1145 _ilfs* ilfs = param;
1146 LPCWSTR component;
1147 MSICOMPONENT *comp;
1149 component = MSI_RecordGetString(row,1);
1151 /* check to see if the component is already loaded */
1152 comp = get_loaded_component( ilfs->package, component );
1153 if (!comp)
1155 ERR("unknown component %s\n", debugstr_w(component));
1156 return ERROR_FUNCTION_FAILED;
1159 add_feature_component( ilfs->feature, comp );
1160 comp->Enabled = TRUE;
1162 return ERROR_SUCCESS;
1165 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1167 MSIFEATURE *feature;
1169 if ( !name )
1170 return NULL;
1172 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1174 if ( !lstrcmpW( feature->Feature, name ) )
1175 return feature;
1178 return NULL;
1181 static UINT load_feature(MSIRECORD * row, LPVOID param)
1183 MSIPACKAGE* package = param;
1184 MSIFEATURE* feature;
1185 static const WCHAR Query1[] =
1186 {'S','E','L','E','C','T',' ',
1187 '`','C','o','m','p','o','n','e','n','t','_','`',
1188 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1189 'C','o','m','p','o','n','e','n','t','s','`',' ',
1190 'W','H','E','R','E',' ',
1191 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1192 MSIQUERY * view;
1193 UINT rc;
1194 _ilfs ilfs;
1196 /* fill in the data */
1198 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1199 if (!feature)
1200 return ERROR_NOT_ENOUGH_MEMORY;
1202 list_init( &feature->Children );
1203 list_init( &feature->Components );
1205 feature->Feature = msi_dup_record_field( row, 1 );
1207 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1209 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1210 feature->Title = msi_dup_record_field( row, 3 );
1211 feature->Description = msi_dup_record_field( row, 4 );
1213 if (!MSI_RecordIsNull(row,5))
1214 feature->Display = MSI_RecordGetInteger(row,5);
1216 feature->Level= MSI_RecordGetInteger(row,6);
1217 feature->Directory = msi_dup_record_field( row, 7 );
1218 feature->Attributes = MSI_RecordGetInteger(row,8);
1220 feature->Installed = INSTALLSTATE_UNKNOWN;
1221 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1223 list_add_tail( &package->features, &feature->entry );
1225 /* load feature components */
1227 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1228 if (rc != ERROR_SUCCESS)
1229 return ERROR_SUCCESS;
1231 ilfs.package = package;
1232 ilfs.feature = feature;
1234 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1235 msiobj_release(&view->hdr);
1237 return ERROR_SUCCESS;
1240 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1242 MSIPACKAGE* package = param;
1243 MSIFEATURE *parent, *child;
1245 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1246 if (!child)
1247 return ERROR_FUNCTION_FAILED;
1249 if (!child->Feature_Parent)
1250 return ERROR_SUCCESS;
1252 parent = find_feature_by_name( package, child->Feature_Parent );
1253 if (!parent)
1254 return ERROR_FUNCTION_FAILED;
1256 add_feature_child( parent, child );
1257 return ERROR_SUCCESS;
1260 static UINT load_all_features( MSIPACKAGE *package )
1262 static const WCHAR query[] = {
1263 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1264 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1265 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1266 MSIQUERY *view;
1267 UINT r;
1269 if (!list_empty(&package->features))
1270 return ERROR_SUCCESS;
1272 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1273 if (r != ERROR_SUCCESS)
1274 return r;
1276 r = MSI_IterateRecords( view, NULL, load_feature, package );
1277 if (r != ERROR_SUCCESS)
1278 return r;
1280 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1281 msiobj_release( &view->hdr );
1283 return r;
1286 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1288 if (!p)
1289 return p;
1290 p = strchrW(p, ch);
1291 if (!p)
1292 return p;
1293 *p = 0;
1294 return p+1;
1297 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1299 static const WCHAR query[] = {
1300 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1301 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1302 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1303 MSIQUERY *view = NULL;
1304 MSIRECORD *row = NULL;
1305 UINT r;
1307 TRACE("%s\n", debugstr_w(file->File));
1309 r = MSI_OpenQuery(package->db, &view, query, file->File);
1310 if (r != ERROR_SUCCESS)
1311 goto done;
1313 r = MSI_ViewExecute(view, NULL);
1314 if (r != ERROR_SUCCESS)
1315 goto done;
1317 r = MSI_ViewFetch(view, &row);
1318 if (r != ERROR_SUCCESS)
1319 goto done;
1321 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1322 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1323 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1324 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1325 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1327 done:
1328 if (view) msiobj_release(&view->hdr);
1329 if (row) msiobj_release(&row->hdr);
1330 return r;
1333 static UINT load_file(MSIRECORD *row, LPVOID param)
1335 MSIPACKAGE* package = param;
1336 LPCWSTR component;
1337 MSIFILE *file;
1339 /* fill in the data */
1341 file = msi_alloc_zero( sizeof (MSIFILE) );
1342 if (!file)
1343 return ERROR_NOT_ENOUGH_MEMORY;
1345 file->File = msi_dup_record_field( row, 1 );
1347 component = MSI_RecordGetString( row, 2 );
1348 file->Component = get_loaded_component( package, component );
1350 if (!file->Component)
1352 WARN("Component not found: %s\n", debugstr_w(component));
1353 msi_free(file->File);
1354 msi_free(file);
1355 return ERROR_SUCCESS;
1358 file->FileName = msi_dup_record_field( row, 3 );
1359 reduce_to_longfilename( file->FileName );
1361 file->ShortName = msi_dup_record_field( row, 3 );
1362 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1364 file->FileSize = MSI_RecordGetInteger( row, 4 );
1365 file->Version = msi_dup_record_field( row, 5 );
1366 file->Language = msi_dup_record_field( row, 6 );
1367 file->Attributes = MSI_RecordGetInteger( row, 7 );
1368 file->Sequence = MSI_RecordGetInteger( row, 8 );
1370 file->state = msifs_invalid;
1372 /* if the compressed bits are not set in the file attributes,
1373 * then read the information from the package word count property
1375 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1377 file->IsCompressed = FALSE;
1379 else if (file->Attributes &
1380 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1382 file->IsCompressed = TRUE;
1384 else if (file->Attributes & msidbFileAttributesNoncompressed)
1386 file->IsCompressed = FALSE;
1388 else
1390 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1393 load_file_hash(package, file);
1395 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1397 list_add_tail( &package->files, &file->entry );
1399 return ERROR_SUCCESS;
1402 static UINT load_all_files(MSIPACKAGE *package)
1404 MSIQUERY * view;
1405 UINT rc;
1406 static const WCHAR Query[] =
1407 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1408 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1409 '`','S','e','q','u','e','n','c','e','`', 0};
1411 if (!list_empty(&package->files))
1412 return ERROR_SUCCESS;
1414 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1415 if (rc != ERROR_SUCCESS)
1416 return ERROR_SUCCESS;
1418 rc = MSI_IterateRecords(view, NULL, load_file, package);
1419 msiobj_release(&view->hdr);
1421 return ERROR_SUCCESS;
1424 static UINT load_folder( MSIRECORD *row, LPVOID param )
1426 MSIPACKAGE *package = param;
1427 static WCHAR szEmpty[] = { 0 };
1428 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1429 MSIFOLDER *folder;
1431 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1432 if (!folder)
1433 return ERROR_NOT_ENOUGH_MEMORY;
1435 folder->Directory = msi_dup_record_field( row, 1 );
1437 TRACE("%s\n", debugstr_w(folder->Directory));
1439 p = msi_dup_record_field(row, 3);
1441 /* split src and target dir */
1442 tgt_short = p;
1443 src_short = folder_split_path( p, ':' );
1445 /* split the long and short paths */
1446 tgt_long = folder_split_path( tgt_short, '|' );
1447 src_long = folder_split_path( src_short, '|' );
1449 /* check for no-op dirs */
1450 if (!lstrcmpW(szDot, tgt_short))
1451 tgt_short = szEmpty;
1452 if (!lstrcmpW(szDot, src_short))
1453 src_short = szEmpty;
1455 if (!tgt_long)
1456 tgt_long = tgt_short;
1458 if (!src_short) {
1459 src_short = tgt_short;
1460 src_long = tgt_long;
1463 if (!src_long)
1464 src_long = src_short;
1466 /* FIXME: use the target short path too */
1467 folder->TargetDefault = strdupW(tgt_long);
1468 folder->SourceShortPath = strdupW(src_short);
1469 folder->SourceLongPath = strdupW(src_long);
1470 msi_free(p);
1472 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1473 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1474 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1476 folder->Parent = msi_dup_record_field( row, 2 );
1478 folder->Property = msi_dup_property( package, folder->Directory );
1480 list_add_tail( &package->folders, &folder->entry );
1482 TRACE("returning %p\n", folder);
1484 return ERROR_SUCCESS;
1487 static UINT load_all_folders( MSIPACKAGE *package )
1489 static const WCHAR query[] = {
1490 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1491 '`','D','i','r','e','c','t','o','r','y','`',0 };
1492 MSIQUERY *view;
1493 UINT r;
1495 if (!list_empty(&package->folders))
1496 return ERROR_SUCCESS;
1498 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1499 if (r != ERROR_SUCCESS)
1500 return r;
1502 r = MSI_IterateRecords(view, NULL, load_folder, package);
1503 msiobj_release(&view->hdr);
1504 return r;
1508 * I am not doing any of the costing functionality yet.
1509 * Mostly looking at doing the Component and Feature loading
1511 * The native MSI does A LOT of modification to tables here. Mostly adding
1512 * a lot of temporary columns to the Feature and Component tables.
1514 * note: Native msi also tracks the short filename. But I am only going to
1515 * track the long ones. Also looking at this directory table
1516 * it appears that the directory table does not get the parents
1517 * resolved base on property only based on their entries in the
1518 * directory table.
1520 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1522 static const WCHAR szCosting[] =
1523 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1525 MSI_SetPropertyW(package, szCosting, szZero);
1526 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1528 load_all_folders( package );
1529 load_all_components( package );
1530 load_all_features( package );
1531 load_all_files( package );
1533 return ERROR_SUCCESS;
1536 static UINT execute_script(MSIPACKAGE *package, UINT script )
1538 UINT i;
1539 UINT rc = ERROR_SUCCESS;
1541 TRACE("Executing Script %i\n",script);
1543 if (!package->script)
1545 ERR("no script!\n");
1546 return ERROR_FUNCTION_FAILED;
1549 for (i = 0; i < package->script->ActionCount[script]; i++)
1551 LPWSTR action;
1552 action = package->script->Actions[script][i];
1553 ui_actionstart(package, action);
1554 TRACE("Executing Action (%s)\n",debugstr_w(action));
1555 rc = ACTION_PerformAction(package, action, script, TRUE);
1556 if (rc != ERROR_SUCCESS)
1557 break;
1559 msi_free_action_script(package, script);
1560 return rc;
1563 static UINT ACTION_FileCost(MSIPACKAGE *package)
1565 return ERROR_SUCCESS;
1568 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1570 MSICOMPONENT *comp;
1571 INSTALLSTATE state;
1572 UINT r;
1574 state = MsiQueryProductStateW(package->ProductCode);
1576 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1578 if (!comp->ComponentId)
1579 continue;
1581 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1582 comp->Installed = INSTALLSTATE_ABSENT;
1583 else
1585 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1586 package->Context, comp->ComponentId,
1587 &comp->Installed);
1588 if (r != ERROR_SUCCESS)
1589 comp->Installed = INSTALLSTATE_ABSENT;
1594 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1596 MSIFEATURE *feature;
1597 INSTALLSTATE state;
1599 state = MsiQueryProductStateW(package->ProductCode);
1601 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1603 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1604 feature->Installed = INSTALLSTATE_ABSENT;
1605 else
1607 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1608 feature->Feature);
1613 static BOOL process_state_property(MSIPACKAGE* package, int level,
1614 LPCWSTR property, INSTALLSTATE state)
1616 LPWSTR override;
1617 MSIFEATURE *feature;
1619 override = msi_dup_property( package, property );
1620 if (!override)
1621 return FALSE;
1623 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1625 if (lstrcmpW(property, szRemove) &&
1626 (feature->Level <= 0 || feature->Level > level))
1627 continue;
1629 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1631 if (strcmpiW(override, szAll)==0)
1632 msi_feature_set_state(package, feature, state);
1633 else
1635 LPWSTR ptr = override;
1636 LPWSTR ptr2 = strchrW(override,',');
1638 while (ptr)
1640 int len = ptr2 - ptr;
1642 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1643 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1645 msi_feature_set_state(package, feature, state);
1646 break;
1648 if (ptr2)
1650 ptr=ptr2+1;
1651 ptr2 = strchrW(ptr,',');
1653 else
1654 break;
1658 msi_free(override);
1660 return TRUE;
1663 static BOOL process_overrides( MSIPACKAGE *package, int level )
1665 static const WCHAR szAddLocal[] =
1666 {'A','D','D','L','O','C','A','L',0};
1667 static const WCHAR szAddSource[] =
1668 {'A','D','D','S','O','U','R','C','E',0};
1669 static const WCHAR szAdvertise[] =
1670 {'A','D','V','E','R','T','I','S','E',0};
1671 BOOL ret = FALSE;
1673 /* all these activation/deactivation things happen in order and things
1674 * later on the list override things earlier on the list.
1676 * 0 INSTALLLEVEL processing
1677 * 1 ADDLOCAL
1678 * 2 REMOVE
1679 * 3 ADDSOURCE
1680 * 4 ADDDEFAULT
1681 * 5 REINSTALL
1682 * 6 ADVERTISE
1683 * 7 COMPADDLOCAL
1684 * 8 COMPADDSOURCE
1685 * 9 FILEADDLOCAL
1686 * 10 FILEADDSOURCE
1687 * 11 FILEADDDEFAULT
1689 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1690 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1691 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1692 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1693 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1695 if (ret)
1696 MSI_SetPropertyW( package, szPreselected, szOne );
1698 return ret;
1701 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1703 int level;
1704 static const WCHAR szlevel[] =
1705 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1706 MSICOMPONENT* component;
1707 MSIFEATURE *feature;
1709 TRACE("Checking Install Level\n");
1711 level = msi_get_property_int(package, szlevel, 1);
1713 if (!msi_get_property_int( package, szPreselected, 0 ))
1715 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1717 BOOL feature_state = ((feature->Level > 0) &&
1718 (feature->Level <= level));
1720 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1722 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1723 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1724 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1725 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1726 else
1727 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1731 /* disable child features of unselected parent features */
1732 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1734 FeatureList *fl;
1736 if (feature->Level > 0 && feature->Level <= level)
1737 continue;
1739 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1740 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1745 * now we want to enable or disable components base on feature
1748 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1750 ComponentList *cl;
1752 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1753 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1755 if (!feature->Level)
1756 continue;
1758 /* features with components that have compressed files are made local */
1759 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1761 if (cl->component->Enabled &&
1762 cl->component->ForceLocalState &&
1763 feature->Action == INSTALLSTATE_SOURCE)
1765 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1766 break;
1770 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1772 component = cl->component;
1774 if (!component->Enabled)
1775 continue;
1777 switch (feature->Action)
1779 case INSTALLSTATE_ABSENT:
1780 component->anyAbsent = 1;
1781 break;
1782 case INSTALLSTATE_ADVERTISED:
1783 component->hasAdvertiseFeature = 1;
1784 break;
1785 case INSTALLSTATE_SOURCE:
1786 component->hasSourceFeature = 1;
1787 break;
1788 case INSTALLSTATE_LOCAL:
1789 component->hasLocalFeature = 1;
1790 break;
1791 case INSTALLSTATE_DEFAULT:
1792 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1793 component->hasAdvertiseFeature = 1;
1794 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1795 component->hasSourceFeature = 1;
1796 else
1797 component->hasLocalFeature = 1;
1798 break;
1799 default:
1800 break;
1805 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1807 /* if the component isn't enabled, leave it alone */
1808 if (!component->Enabled)
1809 continue;
1811 /* check if it's local or source */
1812 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1813 (component->hasLocalFeature || component->hasSourceFeature))
1815 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1816 !component->ForceLocalState)
1817 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1818 else
1819 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1820 continue;
1823 /* if any feature is local, the component must be local too */
1824 if (component->hasLocalFeature)
1826 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1827 continue;
1830 if (component->hasSourceFeature)
1832 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1833 continue;
1836 if (component->hasAdvertiseFeature)
1838 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1839 continue;
1842 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1843 if (component->anyAbsent)
1844 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1847 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1849 if (component->Action == INSTALLSTATE_DEFAULT)
1851 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1852 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1855 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1856 debugstr_w(component->Component), component->Installed, component->Action);
1860 return ERROR_SUCCESS;
1863 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1865 MSIPACKAGE *package = param;
1866 LPCWSTR name;
1867 LPWSTR path;
1868 MSIFOLDER *f;
1870 name = MSI_RecordGetString(row,1);
1872 f = get_loaded_folder(package, name);
1873 if (!f) return ERROR_SUCCESS;
1875 /* reset the ResolvedTarget */
1876 msi_free(f->ResolvedTarget);
1877 f->ResolvedTarget = NULL;
1879 /* This helper function now does ALL the work */
1880 TRACE("Dir %s ...\n",debugstr_w(name));
1881 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1882 TRACE("resolves to %s\n",debugstr_w(path));
1883 msi_free(path);
1885 return ERROR_SUCCESS;
1888 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1890 MSIPACKAGE *package = param;
1891 LPCWSTR name;
1892 MSIFEATURE *feature;
1894 name = MSI_RecordGetString( row, 1 );
1896 feature = get_loaded_feature( package, name );
1897 if (!feature)
1898 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1899 else
1901 LPCWSTR Condition;
1902 Condition = MSI_RecordGetString(row,3);
1904 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1906 int level = MSI_RecordGetInteger(row,2);
1907 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1908 feature->Level = level;
1911 return ERROR_SUCCESS;
1914 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1916 static const WCHAR name_fmt[] =
1917 {'%','u','.','%','u','.','%','u','.','%','u',0};
1918 static const WCHAR name[] = {'\\',0};
1919 VS_FIXEDFILEINFO *lpVer;
1920 WCHAR filever[0x100];
1921 LPVOID version;
1922 DWORD versize;
1923 DWORD handle;
1924 UINT sz;
1926 TRACE("%s\n", debugstr_w(filename));
1928 versize = GetFileVersionInfoSizeW( filename, &handle );
1929 if (!versize)
1930 return NULL;
1932 version = msi_alloc( versize );
1933 GetFileVersionInfoW( filename, 0, versize, version );
1935 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1937 msi_free( version );
1938 return NULL;
1941 sprintfW( filever, name_fmt,
1942 HIWORD(lpVer->dwFileVersionMS),
1943 LOWORD(lpVer->dwFileVersionMS),
1944 HIWORD(lpVer->dwFileVersionLS),
1945 LOWORD(lpVer->dwFileVersionLS));
1947 msi_free( version );
1949 return strdupW( filever );
1952 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1954 LPWSTR file_version;
1955 MSIFILE *file;
1957 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1959 MSICOMPONENT* comp = file->Component;
1960 LPWSTR p;
1962 if (!comp)
1963 continue;
1965 if (file->IsCompressed)
1966 comp->ForceLocalState = TRUE;
1968 /* calculate target */
1969 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1971 msi_free(file->TargetPath);
1973 TRACE("file %s is named %s\n",
1974 debugstr_w(file->File), debugstr_w(file->FileName));
1976 file->TargetPath = build_directory_name(2, p, file->FileName);
1978 msi_free(p);
1980 TRACE("file %s resolves to %s\n",
1981 debugstr_w(file->File), debugstr_w(file->TargetPath));
1983 /* don't check files of components that aren't installed */
1984 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1985 comp->Installed == INSTALLSTATE_ABSENT)
1987 file->state = msifs_missing; /* assume files are missing */
1988 continue;
1991 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1993 file->state = msifs_missing;
1994 comp->Cost += file->FileSize;
1995 continue;
1998 if (file->Version &&
1999 (file_version = msi_get_disk_file_version( file->TargetPath )))
2001 TRACE("new %s old %s\n", debugstr_w(file->Version),
2002 debugstr_w(file_version));
2003 /* FIXME: seems like a bad way to compare version numbers */
2004 if (lstrcmpiW(file_version, file->Version)<0)
2006 file->state = msifs_overwrite;
2007 comp->Cost += file->FileSize;
2009 else
2010 file->state = msifs_present;
2011 msi_free( file_version );
2013 else
2014 file->state = msifs_present;
2017 return ERROR_SUCCESS;
2021 * A lot is done in this function aside from just the costing.
2022 * The costing needs to be implemented at some point but for now I am going
2023 * to focus on the directory building
2026 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2028 static const WCHAR ExecSeqQuery[] =
2029 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2030 '`','D','i','r','e','c','t','o','r','y','`',0};
2031 static const WCHAR ConditionQuery[] =
2032 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2033 '`','C','o','n','d','i','t','i','o','n','`',0};
2034 static const WCHAR szCosting[] =
2035 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2036 static const WCHAR szlevel[] =
2037 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2038 static const WCHAR szOutOfDiskSpace[] =
2039 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2040 MSICOMPONENT *comp;
2041 UINT rc = ERROR_SUCCESS;
2042 MSIQUERY * view;
2043 LPWSTR level;
2045 TRACE("Building Directory properties\n");
2047 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2048 if (rc == ERROR_SUCCESS)
2050 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2051 package);
2052 msiobj_release(&view->hdr);
2055 /* read components states from the registry */
2056 ACTION_GetComponentInstallStates(package);
2057 ACTION_GetFeatureInstallStates(package);
2059 TRACE("File calculations\n");
2060 msi_check_file_install_states( package );
2062 if (!process_overrides( package, msi_get_property_int( package, szlevel, 1 ) ))
2064 TRACE("Evaluating Condition Table\n");
2066 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2067 if (rc == ERROR_SUCCESS)
2069 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2070 msiobj_release( &view->hdr );
2073 TRACE("Enabling or Disabling Components\n");
2074 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2076 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2078 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2079 comp->Enabled = FALSE;
2081 else
2082 comp->Enabled = TRUE;
2086 MSI_SetPropertyW(package,szCosting,szOne);
2087 /* set default run level if not set */
2088 level = msi_dup_property( package, szlevel );
2089 if (!level)
2090 MSI_SetPropertyW(package,szlevel, szOne);
2091 msi_free(level);
2093 /* FIXME: check volume disk space */
2094 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2096 return MSI_SetFeatureStates(package);
2099 /* OK this value is "interpreted" and then formatted based on the
2100 first few characters */
2101 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2102 DWORD *size)
2104 LPSTR data = NULL;
2106 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2108 if (value[1]=='x')
2110 LPWSTR ptr;
2111 CHAR byte[5];
2112 LPWSTR deformated = NULL;
2113 int count;
2115 deformat_string(package, &value[2], &deformated);
2117 /* binary value type */
2118 ptr = deformated;
2119 *type = REG_BINARY;
2120 if (strlenW(ptr)%2)
2121 *size = (strlenW(ptr)/2)+1;
2122 else
2123 *size = strlenW(ptr)/2;
2125 data = msi_alloc(*size);
2127 byte[0] = '0';
2128 byte[1] = 'x';
2129 byte[4] = 0;
2130 count = 0;
2131 /* if uneven pad with a zero in front */
2132 if (strlenW(ptr)%2)
2134 byte[2]= '0';
2135 byte[3]= *ptr;
2136 ptr++;
2137 data[count] = (BYTE)strtol(byte,NULL,0);
2138 count ++;
2139 TRACE("Uneven byte count\n");
2141 while (*ptr)
2143 byte[2]= *ptr;
2144 ptr++;
2145 byte[3]= *ptr;
2146 ptr++;
2147 data[count] = (BYTE)strtol(byte,NULL,0);
2148 count ++;
2150 msi_free(deformated);
2152 TRACE("Data %i bytes(%i)\n",*size,count);
2154 else
2156 LPWSTR deformated;
2157 LPWSTR p;
2158 DWORD d = 0;
2159 deformat_string(package, &value[1], &deformated);
2161 *type=REG_DWORD;
2162 *size = sizeof(DWORD);
2163 data = msi_alloc(*size);
2164 p = deformated;
2165 if (*p == '-')
2166 p++;
2167 while (*p)
2169 if ( (*p < '0') || (*p > '9') )
2170 break;
2171 d *= 10;
2172 d += (*p - '0');
2173 p++;
2175 if (deformated[0] == '-')
2176 d = -d;
2177 *(LPDWORD)data = d;
2178 TRACE("DWORD %i\n",*(LPDWORD)data);
2180 msi_free(deformated);
2183 else
2185 static const WCHAR szMulti[] = {'[','~',']',0};
2186 LPCWSTR ptr;
2187 *type=REG_SZ;
2189 if (value[0]=='#')
2191 if (value[1]=='%')
2193 ptr = &value[2];
2194 *type=REG_EXPAND_SZ;
2196 else
2197 ptr = &value[1];
2199 else
2200 ptr=value;
2202 if (strstrW(value,szMulti))
2203 *type = REG_MULTI_SZ;
2205 /* remove initial delimiter */
2206 if (!strncmpW(value, szMulti, 3))
2207 ptr = value + 3;
2209 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2211 /* add double NULL terminator */
2212 if (*type == REG_MULTI_SZ)
2214 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2215 data = msi_realloc_zero(data, *size);
2218 return data;
2221 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2223 const WCHAR *ret;
2225 switch (root)
2227 case -1:
2228 if (msi_get_property_int( package, szAllUsers, 0 ))
2230 *root_key = HKEY_LOCAL_MACHINE;
2231 ret = szHLM;
2233 else
2235 *root_key = HKEY_CURRENT_USER;
2236 ret = szHCU;
2238 break;
2239 case 0:
2240 *root_key = HKEY_CLASSES_ROOT;
2241 ret = szHCR;
2242 break;
2243 case 1:
2244 *root_key = HKEY_CURRENT_USER;
2245 ret = szHCU;
2246 break;
2247 case 2:
2248 *root_key = HKEY_LOCAL_MACHINE;
2249 ret = szHLM;
2250 break;
2251 case 3:
2252 *root_key = HKEY_USERS;
2253 ret = szHU;
2254 break;
2255 default:
2256 ERR("Unknown root %i\n", root);
2257 return NULL;
2260 return ret;
2263 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2265 MSIPACKAGE *package = param;
2266 LPSTR value_data = NULL;
2267 HKEY root_key, hkey;
2268 DWORD type,size;
2269 LPWSTR deformated;
2270 LPCWSTR szRoot, component, name, key, value;
2271 MSICOMPONENT *comp;
2272 MSIRECORD * uirow;
2273 LPWSTR uikey;
2274 INT root;
2275 BOOL check_first = FALSE;
2276 UINT rc;
2278 ui_progress(package,2,0,0,0);
2280 value = NULL;
2281 key = NULL;
2282 uikey = NULL;
2283 name = NULL;
2285 component = MSI_RecordGetString(row, 6);
2286 comp = get_loaded_component(package,component);
2287 if (!comp)
2288 return ERROR_SUCCESS;
2290 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2292 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2293 comp->Action = comp->Installed;
2294 return ERROR_SUCCESS;
2296 comp->Action = INSTALLSTATE_LOCAL;
2298 name = MSI_RecordGetString(row, 4);
2299 if( MSI_RecordIsNull(row,5) && name )
2301 /* null values can have special meanings */
2302 if (name[0]=='-' && name[1] == 0)
2303 return ERROR_SUCCESS;
2304 else if ((name[0]=='+' && name[1] == 0) ||
2305 (name[0] == '*' && name[1] == 0))
2306 name = NULL;
2307 check_first = TRUE;
2310 root = MSI_RecordGetInteger(row,2);
2311 key = MSI_RecordGetString(row, 3);
2313 szRoot = get_root_key( package, root, &root_key );
2314 if (!szRoot)
2315 return ERROR_SUCCESS;
2317 deformat_string(package, key , &deformated);
2318 size = strlenW(deformated) + strlenW(szRoot) + 1;
2319 uikey = msi_alloc(size*sizeof(WCHAR));
2320 strcpyW(uikey,szRoot);
2321 strcatW(uikey,deformated);
2323 if (RegCreateKeyW( root_key, deformated, &hkey))
2325 ERR("Could not create key %s\n",debugstr_w(deformated));
2326 msi_free(deformated);
2327 msi_free(uikey);
2328 return ERROR_SUCCESS;
2330 msi_free(deformated);
2332 value = MSI_RecordGetString(row,5);
2333 if (value)
2334 value_data = parse_value(package, value, &type, &size);
2335 else
2337 value_data = (LPSTR)strdupW(szEmpty);
2338 size = sizeof(szEmpty);
2339 type = REG_SZ;
2342 deformat_string(package, name, &deformated);
2344 if (!check_first)
2346 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2347 debugstr_w(uikey));
2348 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2350 else
2352 DWORD sz = 0;
2353 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2354 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2356 TRACE("value %s of %s checked already exists\n",
2357 debugstr_w(deformated), debugstr_w(uikey));
2359 else
2361 TRACE("Checked and setting value %s of %s\n",
2362 debugstr_w(deformated), debugstr_w(uikey));
2363 if (deformated || size)
2364 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2367 RegCloseKey(hkey);
2369 uirow = MSI_CreateRecord(3);
2370 MSI_RecordSetStringW(uirow,2,deformated);
2371 MSI_RecordSetStringW(uirow,1,uikey);
2373 if (type == REG_SZ)
2374 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2375 else
2376 MSI_RecordSetStringW(uirow,3,value);
2378 ui_actiondata(package,szWriteRegistryValues,uirow);
2379 msiobj_release( &uirow->hdr );
2381 msi_free(value_data);
2382 msi_free(deformated);
2383 msi_free(uikey);
2385 return ERROR_SUCCESS;
2388 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2390 UINT rc;
2391 MSIQUERY * view;
2392 static const WCHAR ExecSeqQuery[] =
2393 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2394 '`','R','e','g','i','s','t','r','y','`',0 };
2396 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2397 if (rc != ERROR_SUCCESS)
2398 return ERROR_SUCCESS;
2400 /* increment progress bar each time action data is sent */
2401 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2403 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2405 msiobj_release(&view->hdr);
2406 return rc;
2409 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2411 LONG res;
2412 HKEY hkey;
2413 DWORD num_subkeys, num_values;
2415 if (delete_key)
2417 if ((res = RegDeleteTreeW( hkey_root, key )))
2419 WARN("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2421 return;
2424 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2426 if ((res = RegDeleteValueW( hkey, value )))
2428 WARN("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2430 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2431 NULL, NULL, NULL, NULL );
2432 RegCloseKey( hkey );
2434 if (!res && !num_subkeys && !num_values)
2436 TRACE("Removing empty key %s\n", debugstr_w(key));
2437 RegDeleteKeyW( hkey_root, key );
2439 return;
2441 WARN("Failed to open key %s (%d)\n", debugstr_w(key), res);
2445 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2447 MSIPACKAGE *package = param;
2448 LPCWSTR component, name, key_str, root_key_str;
2449 LPWSTR deformated_key, deformated_name, ui_key_str;
2450 MSICOMPONENT *comp;
2451 MSIRECORD *uirow;
2452 BOOL delete_key = FALSE;
2453 HKEY hkey_root;
2454 UINT size;
2455 INT root;
2457 ui_progress( package, 2, 0, 0, 0 );
2459 component = MSI_RecordGetString( row, 6 );
2460 comp = get_loaded_component( package, component );
2461 if (!comp)
2462 return ERROR_SUCCESS;
2464 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2466 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2467 comp->Action = comp->Installed;
2468 return ERROR_SUCCESS;
2470 comp->Action = INSTALLSTATE_ABSENT;
2472 name = MSI_RecordGetString( row, 4 );
2473 if (MSI_RecordIsNull( row, 5 ) && name )
2475 if (name[0] == '+' && !name[1])
2476 return ERROR_SUCCESS;
2477 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2479 delete_key = TRUE;
2480 name = NULL;
2484 root = MSI_RecordGetInteger( row, 2 );
2485 key_str = MSI_RecordGetString( row, 3 );
2487 root_key_str = get_root_key( package, root, &hkey_root );
2488 if (!root_key_str)
2489 return ERROR_SUCCESS;
2491 deformat_string( package, key_str, &deformated_key );
2492 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2493 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2494 strcpyW( ui_key_str, root_key_str );
2495 strcatW( ui_key_str, deformated_key );
2497 deformat_string( package, name, &deformated_name );
2499 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2500 msi_free( deformated_key );
2502 uirow = MSI_CreateRecord( 2 );
2503 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2504 MSI_RecordSetStringW( uirow, 2, deformated_name );
2506 ui_actiondata( package, szRemoveRegistryValues, uirow );
2507 msiobj_release( &uirow->hdr );
2509 msi_free( ui_key_str );
2510 msi_free( deformated_name );
2511 return ERROR_SUCCESS;
2514 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2516 MSIPACKAGE *package = param;
2517 LPCWSTR component, name, key_str, root_key_str;
2518 LPWSTR deformated_key, deformated_name, ui_key_str;
2519 MSICOMPONENT *comp;
2520 MSIRECORD *uirow;
2521 BOOL delete_key = FALSE;
2522 HKEY hkey_root;
2523 UINT size;
2524 INT root;
2526 ui_progress( package, 2, 0, 0, 0 );
2528 component = MSI_RecordGetString( row, 5 );
2529 comp = get_loaded_component( package, component );
2530 if (!comp)
2531 return ERROR_SUCCESS;
2533 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2535 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2536 comp->Action = comp->Installed;
2537 return ERROR_SUCCESS;
2539 comp->Action = INSTALLSTATE_LOCAL;
2541 if ((name = MSI_RecordGetString( row, 4 )))
2543 if (name[0] == '-' && !name[1])
2545 delete_key = TRUE;
2546 name = NULL;
2550 root = MSI_RecordGetInteger( row, 2 );
2551 key_str = MSI_RecordGetString( row, 3 );
2553 root_key_str = get_root_key( package, root, &hkey_root );
2554 if (!root_key_str)
2555 return ERROR_SUCCESS;
2557 deformat_string( package, key_str, &deformated_key );
2558 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2559 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2560 strcpyW( ui_key_str, root_key_str );
2561 strcatW( ui_key_str, deformated_key );
2563 deformat_string( package, name, &deformated_name );
2565 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2566 msi_free( deformated_key );
2568 uirow = MSI_CreateRecord( 2 );
2569 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2570 MSI_RecordSetStringW( uirow, 2, deformated_name );
2572 ui_actiondata( package, szRemoveRegistryValues, uirow );
2573 msiobj_release( &uirow->hdr );
2575 msi_free( ui_key_str );
2576 msi_free( deformated_name );
2577 return ERROR_SUCCESS;
2580 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2582 UINT rc;
2583 MSIQUERY *view;
2584 static const WCHAR registry_query[] =
2585 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2586 '`','R','e','g','i','s','t','r','y','`',0 };
2587 static const WCHAR remove_registry_query[] =
2588 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2589 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2591 /* increment progress bar each time action data is sent */
2592 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2594 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2595 if (rc == ERROR_SUCCESS)
2597 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2598 msiobj_release( &view->hdr );
2599 if (rc != ERROR_SUCCESS)
2600 return rc;
2603 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2604 if (rc == ERROR_SUCCESS)
2606 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2607 msiobj_release( &view->hdr );
2608 if (rc != ERROR_SUCCESS)
2609 return rc;
2612 return ERROR_SUCCESS;
2615 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2617 package->script->CurrentlyScripting = TRUE;
2619 return ERROR_SUCCESS;
2623 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2625 MSICOMPONENT *comp;
2626 DWORD progress = 0;
2627 DWORD total = 0;
2628 static const WCHAR q1[]=
2629 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2630 '`','R','e','g','i','s','t','r','y','`',0};
2631 UINT rc;
2632 MSIQUERY * view;
2633 MSIFEATURE *feature;
2634 MSIFILE *file;
2636 TRACE("InstallValidate\n");
2638 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2639 if (rc == ERROR_SUCCESS)
2641 MSI_IterateRecords( view, &progress, NULL, package );
2642 msiobj_release( &view->hdr );
2643 total += progress * REG_PROGRESS_VALUE;
2646 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2647 total += COMPONENT_PROGRESS_VALUE;
2649 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2650 total += file->FileSize;
2652 ui_progress(package,0,total,0,0);
2654 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2656 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2657 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2658 feature->ActionRequest);
2661 return ERROR_SUCCESS;
2664 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2666 MSIPACKAGE* package = param;
2667 LPCWSTR cond = NULL;
2668 LPCWSTR message = NULL;
2669 UINT r;
2671 static const WCHAR title[]=
2672 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2674 cond = MSI_RecordGetString(row,1);
2676 r = MSI_EvaluateConditionW(package,cond);
2677 if (r == MSICONDITION_FALSE)
2679 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2681 LPWSTR deformated;
2682 message = MSI_RecordGetString(row,2);
2683 deformat_string(package,message,&deformated);
2684 MessageBoxW(NULL,deformated,title,MB_OK);
2685 msi_free(deformated);
2688 return ERROR_INSTALL_FAILURE;
2691 return ERROR_SUCCESS;
2694 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2696 UINT rc;
2697 MSIQUERY * view = NULL;
2698 static const WCHAR ExecSeqQuery[] =
2699 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2700 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2702 TRACE("Checking launch conditions\n");
2704 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2705 if (rc != ERROR_SUCCESS)
2706 return ERROR_SUCCESS;
2708 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2709 msiobj_release(&view->hdr);
2711 return rc;
2714 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2717 if (!cmp->KeyPath)
2718 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2720 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2722 MSIRECORD * row = 0;
2723 UINT root,len;
2724 LPWSTR deformated,buffer,deformated_name;
2725 LPCWSTR key,name;
2726 static const WCHAR ExecSeqQuery[] =
2727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2728 '`','R','e','g','i','s','t','r','y','`',' ',
2729 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2730 ' ','=',' ' ,'\'','%','s','\'',0 };
2731 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2732 static const WCHAR fmt2[]=
2733 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2735 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2736 if (!row)
2737 return NULL;
2739 root = MSI_RecordGetInteger(row,2);
2740 key = MSI_RecordGetString(row, 3);
2741 name = MSI_RecordGetString(row, 4);
2742 deformat_string(package, key , &deformated);
2743 deformat_string(package, name, &deformated_name);
2745 len = strlenW(deformated) + 6;
2746 if (deformated_name)
2747 len+=strlenW(deformated_name);
2749 buffer = msi_alloc( len *sizeof(WCHAR));
2751 if (deformated_name)
2752 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2753 else
2754 sprintfW(buffer,fmt,root,deformated);
2756 msi_free(deformated);
2757 msi_free(deformated_name);
2758 msiobj_release(&row->hdr);
2760 return buffer;
2762 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2764 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2765 return NULL;
2767 else
2769 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2771 if (file)
2772 return strdupW( file->TargetPath );
2774 return NULL;
2777 static HKEY openSharedDLLsKey(void)
2779 HKEY hkey=0;
2780 static const WCHAR path[] =
2781 {'S','o','f','t','w','a','r','e','\\',
2782 'M','i','c','r','o','s','o','f','t','\\',
2783 'W','i','n','d','o','w','s','\\',
2784 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2785 'S','h','a','r','e','d','D','L','L','s',0};
2787 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2788 return hkey;
2791 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2793 HKEY hkey;
2794 DWORD count=0;
2795 DWORD type;
2796 DWORD sz = sizeof(count);
2797 DWORD rc;
2799 hkey = openSharedDLLsKey();
2800 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2801 if (rc != ERROR_SUCCESS)
2802 count = 0;
2803 RegCloseKey(hkey);
2804 return count;
2807 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2809 HKEY hkey;
2811 hkey = openSharedDLLsKey();
2812 if (count > 0)
2813 msi_reg_set_val_dword( hkey, path, count );
2814 else
2815 RegDeleteValueW(hkey,path);
2816 RegCloseKey(hkey);
2817 return count;
2821 * Return TRUE if the count should be written out and FALSE if not
2823 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2825 MSIFEATURE *feature;
2826 INT count = 0;
2827 BOOL write = FALSE;
2829 /* only refcount DLLs */
2830 if (comp->KeyPath == NULL ||
2831 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2832 comp->Attributes & msidbComponentAttributesODBCDataSource)
2833 write = FALSE;
2834 else
2836 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2837 write = (count > 0);
2839 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2840 write = TRUE;
2843 /* increment counts */
2844 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2846 ComponentList *cl;
2848 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2849 continue;
2851 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2853 if ( cl->component == comp )
2854 count++;
2858 /* decrement counts */
2859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2861 ComponentList *cl;
2863 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2864 continue;
2866 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2868 if ( cl->component == comp )
2869 count--;
2873 /* ref count all the files in the component */
2874 if (write)
2876 MSIFILE *file;
2878 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2880 if (file->Component == comp)
2881 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2885 /* add a count for permanent */
2886 if (comp->Attributes & msidbComponentAttributesPermanent)
2887 count ++;
2889 comp->RefCount = count;
2891 if (write)
2892 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2895 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2897 WCHAR squished_pc[GUID_SIZE];
2898 WCHAR squished_cc[GUID_SIZE];
2899 UINT rc;
2900 MSICOMPONENT *comp;
2901 HKEY hkey;
2903 TRACE("\n");
2905 squash_guid(package->ProductCode,squished_pc);
2906 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2908 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2910 MSIRECORD * uirow;
2912 ui_progress(package,2,0,0,0);
2913 if (!comp->ComponentId)
2914 continue;
2916 squash_guid(comp->ComponentId,squished_cc);
2918 msi_free(comp->FullKeypath);
2919 comp->FullKeypath = resolve_keypath( package, comp );
2921 ACTION_RefCountComponent( package, comp );
2923 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2924 debugstr_w(comp->Component),
2925 debugstr_w(squished_cc),
2926 debugstr_w(comp->FullKeypath),
2927 comp->RefCount);
2929 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
2930 comp->ActionRequest == INSTALLSTATE_SOURCE)
2932 if (!comp->FullKeypath)
2933 continue;
2935 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2936 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2937 &hkey, TRUE);
2938 else
2939 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2940 &hkey, TRUE);
2942 if (rc != ERROR_SUCCESS)
2943 continue;
2945 if (comp->Attributes & msidbComponentAttributesPermanent)
2947 static const WCHAR szPermKey[] =
2948 { '0','0','0','0','0','0','0','0','0','0','0','0',
2949 '0','0','0','0','0','0','0','0','0','0','0','0',
2950 '0','0','0','0','0','0','0','0',0 };
2952 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2955 if (comp->Action == INSTALLSTATE_LOCAL)
2956 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2957 else
2959 MSIFILE *file;
2960 MSIRECORD *row;
2961 LPWSTR ptr, ptr2;
2962 WCHAR source[MAX_PATH];
2963 WCHAR base[MAX_PATH];
2964 LPWSTR sourcepath;
2966 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2967 static const WCHAR query[] = {
2968 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2969 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2970 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2971 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2972 '`','D','i','s','k','I','d','`',0};
2974 file = get_loaded_file(package, comp->KeyPath);
2975 if (!file)
2976 continue;
2978 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2979 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2980 ptr2 = strrchrW(source, '\\') + 1;
2981 msiobj_release(&row->hdr);
2983 lstrcpyW(base, package->PackagePath);
2984 ptr = strrchrW(base, '\\');
2985 *(ptr + 1) = '\0';
2987 sourcepath = resolve_file_source(package, file);
2988 ptr = sourcepath + lstrlenW(base);
2989 lstrcpyW(ptr2, ptr);
2990 msi_free(sourcepath);
2992 msi_reg_set_val_str(hkey, squished_pc, source);
2994 RegCloseKey(hkey);
2996 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
2998 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2999 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3000 else
3001 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3004 /* UI stuff */
3005 uirow = MSI_CreateRecord(3);
3006 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3007 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3008 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3009 ui_actiondata(package,szProcessComponents,uirow);
3010 msiobj_release( &uirow->hdr );
3013 return ERROR_SUCCESS;
3016 typedef struct {
3017 CLSID clsid;
3018 LPWSTR source;
3020 LPWSTR path;
3021 ITypeLib *ptLib;
3022 } typelib_struct;
3024 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3025 LPWSTR lpszName, LONG_PTR lParam)
3027 TLIBATTR *attr;
3028 typelib_struct *tl_struct = (typelib_struct*) lParam;
3029 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3030 int sz;
3031 HRESULT res;
3033 if (!IS_INTRESOURCE(lpszName))
3035 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3036 return TRUE;
3039 sz = strlenW(tl_struct->source)+4;
3040 sz *= sizeof(WCHAR);
3042 if ((INT_PTR)lpszName == 1)
3043 tl_struct->path = strdupW(tl_struct->source);
3044 else
3046 tl_struct->path = msi_alloc(sz);
3047 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3050 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3051 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3052 if (FAILED(res))
3054 msi_free(tl_struct->path);
3055 tl_struct->path = NULL;
3057 return TRUE;
3060 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3061 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3063 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3064 return FALSE;
3067 msi_free(tl_struct->path);
3068 tl_struct->path = NULL;
3070 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3071 ITypeLib_Release(tl_struct->ptLib);
3073 return TRUE;
3076 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3078 MSIPACKAGE* package = param;
3079 LPCWSTR component;
3080 MSICOMPONENT *comp;
3081 MSIFILE *file;
3082 typelib_struct tl_struct;
3083 ITypeLib *tlib;
3084 HMODULE module;
3085 HRESULT hr;
3087 component = MSI_RecordGetString(row,3);
3088 comp = get_loaded_component(package,component);
3089 if (!comp)
3090 return ERROR_SUCCESS;
3092 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3094 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3095 comp->Action = comp->Installed;
3096 return ERROR_SUCCESS;
3098 comp->Action = INSTALLSTATE_LOCAL;
3100 file = get_loaded_file( package, comp->KeyPath );
3101 if (!file)
3102 return ERROR_SUCCESS;
3104 ui_actiondata( package, szRegisterTypeLibraries, row );
3106 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3107 if (module)
3109 LPCWSTR guid;
3110 guid = MSI_RecordGetString(row,1);
3111 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3112 tl_struct.source = strdupW( file->TargetPath );
3113 tl_struct.path = NULL;
3115 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3116 (LONG_PTR)&tl_struct);
3118 if (tl_struct.path)
3120 LPWSTR help = NULL;
3121 LPCWSTR helpid;
3122 HRESULT res;
3124 helpid = MSI_RecordGetString(row,6);
3126 if (helpid)
3127 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3128 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3129 msi_free(help);
3131 if (FAILED(res))
3132 ERR("Failed to register type library %s\n",
3133 debugstr_w(tl_struct.path));
3134 else
3135 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3137 ITypeLib_Release(tl_struct.ptLib);
3138 msi_free(tl_struct.path);
3140 else
3141 ERR("Failed to load type library %s\n",
3142 debugstr_w(tl_struct.source));
3144 FreeLibrary(module);
3145 msi_free(tl_struct.source);
3147 else
3149 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3150 if (FAILED(hr))
3152 ERR("Failed to load type library: %08x\n", hr);
3153 return ERROR_INSTALL_FAILURE;
3156 ITypeLib_Release(tlib);
3159 return ERROR_SUCCESS;
3162 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3165 * OK this is a bit confusing.. I am given a _Component key and I believe
3166 * that the file that is being registered as a type library is the "key file
3167 * of that component" which I interpret to mean "The file in the KeyPath of
3168 * that component".
3170 UINT rc;
3171 MSIQUERY * view;
3172 static const WCHAR Query[] =
3173 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3174 '`','T','y','p','e','L','i','b','`',0};
3176 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3177 if (rc != ERROR_SUCCESS)
3178 return ERROR_SUCCESS;
3180 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3181 msiobj_release(&view->hdr);
3182 return rc;
3185 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3187 MSIPACKAGE *package = param;
3188 LPCWSTR component, guid;
3189 MSICOMPONENT *comp;
3190 GUID libid;
3191 UINT version;
3192 LCID language;
3193 SYSKIND syskind;
3194 HRESULT hr;
3196 component = MSI_RecordGetString( row, 3 );
3197 comp = get_loaded_component( package, component );
3198 if (!comp)
3199 return ERROR_SUCCESS;
3201 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3203 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3204 comp->Action = comp->Installed;
3205 return ERROR_SUCCESS;
3207 comp->Action = INSTALLSTATE_ABSENT;
3209 ui_actiondata( package, szUnregisterTypeLibraries, row );
3211 guid = MSI_RecordGetString( row, 1 );
3212 CLSIDFromString( (LPWSTR)guid, &libid );
3213 version = MSI_RecordGetInteger( row, 4 );
3214 language = MSI_RecordGetInteger( row, 2 );
3216 #ifdef _WIN64
3217 syskind = SYS_WIN64;
3218 #else
3219 syskind = SYS_WIN32;
3220 #endif
3222 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3223 if (FAILED(hr))
3225 WARN("Failed to unregister typelib: %08x\n", hr);
3228 return ERROR_SUCCESS;
3231 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3233 UINT rc;
3234 MSIQUERY *view;
3235 static const WCHAR query[] =
3236 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3237 '`','T','y','p','e','L','i','b','`',0};
3239 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3240 if (rc != ERROR_SUCCESS)
3241 return ERROR_SUCCESS;
3243 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3244 msiobj_release( &view->hdr );
3245 return rc;
3248 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3250 static const WCHAR szlnk[] = {'.','l','n','k',0};
3251 LPCWSTR directory, extension;
3252 LPWSTR link_folder, link_file, filename;
3254 directory = MSI_RecordGetString( row, 2 );
3255 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3257 /* may be needed because of a bug somewhere else */
3258 create_full_pathW( link_folder );
3260 filename = msi_dup_record_field( row, 3 );
3261 reduce_to_longfilename( filename );
3263 extension = strchrW( filename, '.' );
3264 if (!extension || strcmpiW( extension, szlnk ))
3266 int len = strlenW( filename );
3267 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3268 memcpy( filename + len, szlnk, sizeof(szlnk) );
3270 link_file = build_directory_name( 2, link_folder, filename );
3271 msi_free( link_folder );
3272 msi_free( filename );
3274 return link_file;
3277 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3279 MSIPACKAGE *package = param;
3280 LPWSTR link_file, deformated, path;
3281 LPCWSTR component, target;
3282 MSICOMPONENT *comp;
3283 IShellLinkW *sl = NULL;
3284 IPersistFile *pf = NULL;
3285 HRESULT res;
3287 component = MSI_RecordGetString(row, 4);
3288 comp = get_loaded_component(package, component);
3289 if (!comp)
3290 return ERROR_SUCCESS;
3292 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3294 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3295 comp->Action = comp->Installed;
3296 return ERROR_SUCCESS;
3298 comp->Action = INSTALLSTATE_LOCAL;
3300 ui_actiondata(package,szCreateShortcuts,row);
3302 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3303 &IID_IShellLinkW, (LPVOID *) &sl );
3305 if (FAILED( res ))
3307 ERR("CLSID_ShellLink not available\n");
3308 goto err;
3311 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3312 if (FAILED( res ))
3314 ERR("QueryInterface(IID_IPersistFile) failed\n");
3315 goto err;
3318 target = MSI_RecordGetString(row, 5);
3319 if (strchrW(target, '['))
3321 deformat_string(package, target, &deformated);
3322 IShellLinkW_SetPath(sl,deformated);
3323 msi_free(deformated);
3325 else
3327 FIXME("poorly handled shortcut format, advertised shortcut\n");
3328 IShellLinkW_SetPath(sl,comp->FullKeypath);
3331 if (!MSI_RecordIsNull(row,6))
3333 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3334 deformat_string(package, arguments, &deformated);
3335 IShellLinkW_SetArguments(sl,deformated);
3336 msi_free(deformated);
3339 if (!MSI_RecordIsNull(row,7))
3341 LPCWSTR description = MSI_RecordGetString(row, 7);
3342 IShellLinkW_SetDescription(sl, description);
3345 if (!MSI_RecordIsNull(row,8))
3346 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3348 if (!MSI_RecordIsNull(row,9))
3350 INT index;
3351 LPCWSTR icon = MSI_RecordGetString(row, 9);
3353 path = build_icon_path(package, icon);
3354 index = MSI_RecordGetInteger(row,10);
3356 /* no value means 0 */
3357 if (index == MSI_NULL_INTEGER)
3358 index = 0;
3360 IShellLinkW_SetIconLocation(sl, path, index);
3361 msi_free(path);
3364 if (!MSI_RecordIsNull(row,11))
3365 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3367 if (!MSI_RecordIsNull(row,12))
3369 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3370 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3371 if (path)
3372 IShellLinkW_SetWorkingDirectory(sl, path);
3373 msi_free(path);
3376 link_file = get_link_file(package, row);
3378 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3379 IPersistFile_Save(pf, link_file, FALSE);
3381 msi_free(link_file);
3383 err:
3384 if (pf)
3385 IPersistFile_Release( pf );
3386 if (sl)
3387 IShellLinkW_Release( sl );
3389 return ERROR_SUCCESS;
3392 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3394 UINT rc;
3395 HRESULT res;
3396 MSIQUERY * view;
3397 static const WCHAR Query[] =
3398 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3399 '`','S','h','o','r','t','c','u','t','`',0};
3401 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3402 if (rc != ERROR_SUCCESS)
3403 return ERROR_SUCCESS;
3405 res = CoInitialize( NULL );
3407 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3408 msiobj_release(&view->hdr);
3410 if (SUCCEEDED(res))
3411 CoUninitialize();
3413 return rc;
3416 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3418 MSIPACKAGE *package = param;
3419 LPWSTR link_file;
3420 LPCWSTR component;
3421 MSICOMPONENT *comp;
3423 component = MSI_RecordGetString( row, 4 );
3424 comp = get_loaded_component( package, component );
3425 if (!comp)
3426 return ERROR_SUCCESS;
3428 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3430 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3431 comp->Action = comp->Installed;
3432 return ERROR_SUCCESS;
3434 comp->Action = INSTALLSTATE_ABSENT;
3436 ui_actiondata( package, szRemoveShortcuts, row );
3438 link_file = get_link_file( package, row );
3440 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3441 if (!DeleteFileW( link_file ))
3443 WARN("Failed to remove shortcut file %u\n", GetLastError());
3445 msi_free( link_file );
3447 return ERROR_SUCCESS;
3450 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3452 UINT rc;
3453 MSIQUERY *view;
3454 static const WCHAR query[] =
3455 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3456 '`','S','h','o','r','t','c','u','t','`',0};
3458 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3459 if (rc != ERROR_SUCCESS)
3460 return ERROR_SUCCESS;
3462 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3463 msiobj_release( &view->hdr );
3465 return rc;
3468 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3470 MSIPACKAGE* package = param;
3471 HANDLE the_file;
3472 LPWSTR FilePath;
3473 LPCWSTR FileName;
3474 CHAR buffer[1024];
3475 DWORD sz;
3476 UINT rc;
3477 MSIRECORD *uirow;
3479 FileName = MSI_RecordGetString(row,1);
3480 if (!FileName)
3482 ERR("Unable to get FileName\n");
3483 return ERROR_SUCCESS;
3486 FilePath = build_icon_path(package,FileName);
3488 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3490 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3491 FILE_ATTRIBUTE_NORMAL, NULL);
3493 if (the_file == INVALID_HANDLE_VALUE)
3495 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3496 msi_free(FilePath);
3497 return ERROR_SUCCESS;
3502 DWORD write;
3503 sz = 1024;
3504 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3505 if (rc != ERROR_SUCCESS)
3507 ERR("Failed to get stream\n");
3508 CloseHandle(the_file);
3509 DeleteFileW(FilePath);
3510 break;
3512 WriteFile(the_file,buffer,sz,&write,NULL);
3513 } while (sz == 1024);
3515 msi_free(FilePath);
3517 CloseHandle(the_file);
3519 uirow = MSI_CreateRecord(1);
3520 MSI_RecordSetStringW(uirow,1,FileName);
3521 ui_actiondata(package,szPublishProduct,uirow);
3522 msiobj_release( &uirow->hdr );
3524 return ERROR_SUCCESS;
3527 static UINT msi_publish_icons(MSIPACKAGE *package)
3529 UINT r;
3530 MSIQUERY *view;
3532 static const WCHAR query[]= {
3533 'S','E','L','E','C','T',' ','*',' ',
3534 'F','R','O','M',' ','`','I','c','o','n','`',0};
3536 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3537 if (r == ERROR_SUCCESS)
3539 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3540 msiobj_release(&view->hdr);
3543 return ERROR_SUCCESS;
3546 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3548 UINT r;
3549 HKEY source;
3550 LPWSTR buffer;
3551 MSIMEDIADISK *disk;
3552 MSISOURCELISTINFO *info;
3554 r = RegCreateKeyW(hkey, szSourceList, &source);
3555 if (r != ERROR_SUCCESS)
3556 return r;
3558 RegCloseKey(source);
3560 buffer = strrchrW(package->PackagePath, '\\') + 1;
3561 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3562 package->Context, MSICODE_PRODUCT,
3563 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3564 if (r != ERROR_SUCCESS)
3565 return r;
3567 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3568 package->Context, MSICODE_PRODUCT,
3569 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3570 if (r != ERROR_SUCCESS)
3571 return r;
3573 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3574 package->Context, MSICODE_PRODUCT,
3575 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3576 if (r != ERROR_SUCCESS)
3577 return r;
3579 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3581 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3582 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3583 info->options, info->value);
3584 else
3585 MsiSourceListSetInfoW(package->ProductCode, NULL,
3586 info->context, info->options,
3587 info->property, info->value);
3590 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3592 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3593 disk->context, disk->options,
3594 disk->disk_id, disk->volume_label, disk->disk_prompt);
3597 return ERROR_SUCCESS;
3600 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3602 MSIHANDLE hdb, suminfo;
3603 WCHAR guids[MAX_PATH];
3604 WCHAR packcode[SQUISH_GUID_SIZE];
3605 LPWSTR buffer;
3606 LPWSTR ptr;
3607 DWORD langid;
3608 DWORD size;
3609 UINT r;
3611 static const WCHAR szProductLanguage[] =
3612 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3613 static const WCHAR szARPProductIcon[] =
3614 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3615 static const WCHAR szProductVersion[] =
3616 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3617 static const WCHAR szAssignment[] =
3618 {'A','s','s','i','g','n','m','e','n','t',0};
3619 static const WCHAR szAdvertiseFlags[] =
3620 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3621 static const WCHAR szClients[] =
3622 {'C','l','i','e','n','t','s',0};
3623 static const WCHAR szColon[] = {':',0};
3625 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3626 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3627 msi_free(buffer);
3629 langid = msi_get_property_int(package, szProductLanguage, 0);
3630 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3632 /* FIXME */
3633 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3635 buffer = msi_dup_property(package, szARPProductIcon);
3636 if (buffer)
3638 LPWSTR path = build_icon_path(package,buffer);
3639 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3640 msi_free(path);
3641 msi_free(buffer);
3644 buffer = msi_dup_property(package, szProductVersion);
3645 if (buffer)
3647 DWORD verdword = msi_version_str_to_dword(buffer);
3648 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3649 msi_free(buffer);
3652 msi_reg_set_val_dword(hkey, szAssignment, 0);
3653 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3654 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3655 msi_reg_set_val_str(hkey, szClients, szColon);
3657 hdb = alloc_msihandle(&package->db->hdr);
3658 if (!hdb)
3659 return ERROR_NOT_ENOUGH_MEMORY;
3661 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3662 MsiCloseHandle(hdb);
3663 if (r != ERROR_SUCCESS)
3664 goto done;
3666 size = MAX_PATH;
3667 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3668 NULL, guids, &size);
3669 if (r != ERROR_SUCCESS)
3670 goto done;
3672 ptr = strchrW(guids, ';');
3673 if (ptr) *ptr = 0;
3674 squash_guid(guids, packcode);
3675 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3677 done:
3678 MsiCloseHandle(suminfo);
3679 return ERROR_SUCCESS;
3682 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3684 UINT r;
3685 HKEY hkey;
3686 LPWSTR upgrade;
3687 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3689 static const WCHAR szUpgradeCode[] =
3690 {'U','p','g','r','a','d','e','C','o','d','e',0};
3692 upgrade = msi_dup_property(package, szUpgradeCode);
3693 if (!upgrade)
3694 return ERROR_SUCCESS;
3696 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3698 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3699 if (r != ERROR_SUCCESS)
3700 goto done;
3702 else
3704 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3705 if (r != ERROR_SUCCESS)
3706 goto done;
3709 squash_guid(package->ProductCode, squashed_pc);
3710 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3712 RegCloseKey(hkey);
3714 done:
3715 msi_free(upgrade);
3716 return r;
3719 static BOOL msi_check_publish(MSIPACKAGE *package)
3721 MSIFEATURE *feature;
3723 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3725 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3726 return TRUE;
3729 return FALSE;
3732 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3734 MSIFEATURE *feature;
3736 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3738 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3739 return FALSE;
3742 return TRUE;
3745 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3747 WCHAR patch_squashed[GUID_SIZE];
3748 HKEY patches;
3749 LONG res;
3750 UINT r = ERROR_FUNCTION_FAILED;
3752 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3753 &patches, NULL);
3754 if (res != ERROR_SUCCESS)
3755 return ERROR_FUNCTION_FAILED;
3757 squash_guid(package->patch->patchcode, patch_squashed);
3759 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3760 (const BYTE *)patch_squashed,
3761 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3762 if (res != ERROR_SUCCESS)
3763 goto done;
3765 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3766 (const BYTE *)package->patch->transforms,
3767 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3768 if (res == ERROR_SUCCESS)
3769 r = ERROR_SUCCESS;
3771 done:
3772 RegCloseKey(patches);
3773 return r;
3777 * 99% of the work done here is only done for
3778 * advertised installs. However this is where the
3779 * Icon table is processed and written out
3780 * so that is what I am going to do here.
3782 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3784 UINT rc;
3785 HKEY hukey=0;
3786 HKEY hudkey=0;
3788 /* FIXME: also need to publish if the product is in advertise mode */
3789 if (!msi_check_publish(package))
3790 return ERROR_SUCCESS;
3792 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3793 &hukey, TRUE);
3794 if (rc != ERROR_SUCCESS)
3795 goto end;
3797 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3798 NULL, &hudkey, TRUE);
3799 if (rc != ERROR_SUCCESS)
3800 goto end;
3802 rc = msi_publish_upgrade_code(package);
3803 if (rc != ERROR_SUCCESS)
3804 goto end;
3806 if (package->patch)
3808 rc = msi_publish_patch(package, hukey, hudkey);
3809 if (rc != ERROR_SUCCESS)
3810 goto end;
3813 rc = msi_publish_product_properties(package, hukey);
3814 if (rc != ERROR_SUCCESS)
3815 goto end;
3817 rc = msi_publish_sourcelist(package, hukey);
3818 if (rc != ERROR_SUCCESS)
3819 goto end;
3821 rc = msi_publish_icons(package);
3823 end:
3824 RegCloseKey(hukey);
3825 RegCloseKey(hudkey);
3827 return rc;
3830 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
3832 WCHAR *filename, *ptr, *folder, *ret;
3833 const WCHAR *dirprop;
3835 filename = msi_dup_record_field( row, 2 );
3836 if (filename && (ptr = strchrW( filename, '|' )))
3837 ptr++;
3838 else
3839 ptr = filename;
3841 dirprop = MSI_RecordGetString( row, 3 );
3842 if (dirprop)
3844 folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
3845 if (!folder)
3846 folder = msi_dup_property( package, dirprop );
3848 else
3849 folder = msi_dup_property( package, szWindowsFolder );
3851 if (!folder)
3853 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
3854 msi_free( filename );
3855 return NULL;
3858 ret = build_directory_name( 2, folder, ptr );
3860 msi_free( filename );
3861 msi_free( folder );
3862 return ret;
3865 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3867 MSIPACKAGE *package = param;
3868 LPCWSTR component, section, key, value, identifier;
3869 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
3870 MSIRECORD * uirow;
3871 INT action;
3872 MSICOMPONENT *comp;
3874 component = MSI_RecordGetString(row, 8);
3875 comp = get_loaded_component(package,component);
3876 if (!comp)
3877 return ERROR_SUCCESS;
3879 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3881 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3882 comp->Action = comp->Installed;
3883 return ERROR_SUCCESS;
3885 comp->Action = INSTALLSTATE_LOCAL;
3887 identifier = MSI_RecordGetString(row,1);
3888 section = MSI_RecordGetString(row,4);
3889 key = MSI_RecordGetString(row,5);
3890 value = MSI_RecordGetString(row,6);
3891 action = MSI_RecordGetInteger(row,7);
3893 deformat_string(package,section,&deformated_section);
3894 deformat_string(package,key,&deformated_key);
3895 deformat_string(package,value,&deformated_value);
3897 fullname = get_ini_file_name(package, row);
3899 if (action == 0)
3901 TRACE("Adding value %s to section %s in %s\n",
3902 debugstr_w(deformated_key), debugstr_w(deformated_section),
3903 debugstr_w(fullname));
3904 WritePrivateProfileStringW(deformated_section, deformated_key,
3905 deformated_value, fullname);
3907 else if (action == 1)
3909 WCHAR returned[10];
3910 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3911 returned, 10, fullname);
3912 if (returned[0] == 0)
3914 TRACE("Adding value %s to section %s in %s\n",
3915 debugstr_w(deformated_key), debugstr_w(deformated_section),
3916 debugstr_w(fullname));
3918 WritePrivateProfileStringW(deformated_section, deformated_key,
3919 deformated_value, fullname);
3922 else if (action == 3)
3923 FIXME("Append to existing section not yet implemented\n");
3925 uirow = MSI_CreateRecord(4);
3926 MSI_RecordSetStringW(uirow,1,identifier);
3927 MSI_RecordSetStringW(uirow,2,deformated_section);
3928 MSI_RecordSetStringW(uirow,3,deformated_key);
3929 MSI_RecordSetStringW(uirow,4,deformated_value);
3930 ui_actiondata(package,szWriteIniValues,uirow);
3931 msiobj_release( &uirow->hdr );
3933 msi_free(fullname);
3934 msi_free(deformated_key);
3935 msi_free(deformated_value);
3936 msi_free(deformated_section);
3937 return ERROR_SUCCESS;
3940 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3942 UINT rc;
3943 MSIQUERY * view;
3944 static const WCHAR ExecSeqQuery[] =
3945 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3946 '`','I','n','i','F','i','l','e','`',0};
3948 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3949 if (rc != ERROR_SUCCESS)
3951 TRACE("no IniFile table\n");
3952 return ERROR_SUCCESS;
3955 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3956 msiobj_release(&view->hdr);
3957 return rc;
3960 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
3962 MSIPACKAGE *package = param;
3963 LPCWSTR component, section, key, value, identifier;
3964 LPWSTR deformated_section, deformated_key, deformated_value, filename;
3965 MSICOMPONENT *comp;
3966 MSIRECORD *uirow;
3967 INT action;
3969 component = MSI_RecordGetString( row, 8 );
3970 comp = get_loaded_component( package, component );
3971 if (!comp)
3972 return ERROR_SUCCESS;
3974 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3976 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3977 comp->Action = comp->Installed;
3978 return ERROR_SUCCESS;
3980 comp->Action = INSTALLSTATE_ABSENT;
3982 identifier = MSI_RecordGetString( row, 1 );
3983 section = MSI_RecordGetString( row, 4 );
3984 key = MSI_RecordGetString( row, 5 );
3985 value = MSI_RecordGetString( row, 6 );
3986 action = MSI_RecordGetInteger( row, 7 );
3988 deformat_string( package, section, &deformated_section );
3989 deformat_string( package, key, &deformated_key );
3990 deformat_string( package, value, &deformated_value );
3992 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
3994 filename = get_ini_file_name( package, row );
3996 TRACE("Removing key %s from section %s in %s\n",
3997 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
3999 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4001 WARN("Unable to remove key %u\n", GetLastError());
4003 msi_free( filename );
4005 else
4006 FIXME("Unsupported action %d\n", action);
4009 uirow = MSI_CreateRecord( 4 );
4010 MSI_RecordSetStringW( uirow, 1, identifier );
4011 MSI_RecordSetStringW( uirow, 2, deformated_section );
4012 MSI_RecordSetStringW( uirow, 3, deformated_key );
4013 MSI_RecordSetStringW( uirow, 4, deformated_value );
4014 ui_actiondata( package, szRemoveIniValues, uirow );
4015 msiobj_release( &uirow->hdr );
4017 msi_free( deformated_key );
4018 msi_free( deformated_value );
4019 msi_free( deformated_section );
4020 return ERROR_SUCCESS;
4023 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4025 MSIPACKAGE *package = param;
4026 LPCWSTR component, section, key, value, identifier;
4027 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4028 MSICOMPONENT *comp;
4029 MSIRECORD *uirow;
4030 INT action;
4032 component = MSI_RecordGetString( row, 8 );
4033 comp = get_loaded_component( package, component );
4034 if (!comp)
4035 return ERROR_SUCCESS;
4037 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4039 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4040 comp->Action = comp->Installed;
4041 return ERROR_SUCCESS;
4043 comp->Action = INSTALLSTATE_LOCAL;
4045 identifier = MSI_RecordGetString( row, 1 );
4046 section = MSI_RecordGetString( row, 4 );
4047 key = MSI_RecordGetString( row, 5 );
4048 value = MSI_RecordGetString( row, 6 );
4049 action = MSI_RecordGetInteger( row, 7 );
4051 deformat_string( package, section, &deformated_section );
4052 deformat_string( package, key, &deformated_key );
4053 deformat_string( package, value, &deformated_value );
4055 if (action == msidbIniFileActionRemoveLine)
4057 filename = get_ini_file_name( package, row );
4059 TRACE("Removing key %s from section %s in %s\n",
4060 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4062 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4064 WARN("Unable to remove key %u\n", GetLastError());
4066 msi_free( filename );
4068 else
4069 FIXME("Unsupported action %d\n", action);
4071 uirow = MSI_CreateRecord( 4 );
4072 MSI_RecordSetStringW( uirow, 1, identifier );
4073 MSI_RecordSetStringW( uirow, 2, deformated_section );
4074 MSI_RecordSetStringW( uirow, 3, deformated_key );
4075 MSI_RecordSetStringW( uirow, 4, deformated_value );
4076 ui_actiondata( package, szRemoveIniValues, uirow );
4077 msiobj_release( &uirow->hdr );
4079 msi_free( deformated_key );
4080 msi_free( deformated_value );
4081 msi_free( deformated_section );
4082 return ERROR_SUCCESS;
4085 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4087 UINT rc;
4088 MSIQUERY *view;
4089 static const WCHAR query[] =
4090 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4091 '`','I','n','i','F','i','l','e','`',0};
4092 static const WCHAR remove_query[] =
4093 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4094 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4096 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4097 if (rc == ERROR_SUCCESS)
4099 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4100 msiobj_release( &view->hdr );
4101 if (rc != ERROR_SUCCESS)
4102 return rc;
4105 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4106 if (rc == ERROR_SUCCESS)
4108 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4109 msiobj_release( &view->hdr );
4110 if (rc != ERROR_SUCCESS)
4111 return rc;
4114 return ERROR_SUCCESS;
4117 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4119 MSIPACKAGE *package = param;
4120 LPCWSTR filename;
4121 LPWSTR FullName;
4122 MSIFILE *file;
4123 DWORD len;
4124 static const WCHAR ExeStr[] =
4125 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
4126 static const WCHAR close[] = {'\"',0};
4127 STARTUPINFOW si;
4128 PROCESS_INFORMATION info;
4129 BOOL brc;
4130 MSIRECORD *uirow;
4131 LPWSTR uipath, p;
4133 memset(&si,0,sizeof(STARTUPINFOW));
4135 filename = MSI_RecordGetString(row,1);
4136 file = get_loaded_file( package, filename );
4138 if (!file)
4140 ERR("Unable to find file id %s\n",debugstr_w(filename));
4141 return ERROR_SUCCESS;
4144 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
4146 FullName = msi_alloc(len*sizeof(WCHAR));
4147 strcpyW(FullName,ExeStr);
4148 strcatW( FullName, file->TargetPath );
4149 strcatW(FullName,close);
4151 TRACE("Registering %s\n",debugstr_w(FullName));
4152 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
4153 &si, &info);
4155 if (brc)
4157 CloseHandle(info.hThread);
4158 msi_dialog_check_messages(info.hProcess);
4159 CloseHandle(info.hProcess);
4162 msi_free(FullName);
4164 /* the UI chunk */
4165 uirow = MSI_CreateRecord( 2 );
4166 uipath = strdupW( file->TargetPath );
4167 p = strrchrW(uipath,'\\');
4168 if (p)
4169 p[0]=0;
4170 MSI_RecordSetStringW( uirow, 1, &p[1] );
4171 MSI_RecordSetStringW( uirow, 2, uipath);
4172 ui_actiondata( package, szSelfRegModules, uirow);
4173 msiobj_release( &uirow->hdr );
4174 msi_free( uipath );
4175 /* FIXME: call ui_progress? */
4177 return ERROR_SUCCESS;
4180 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4182 UINT rc;
4183 MSIQUERY * view;
4184 static const WCHAR ExecSeqQuery[] =
4185 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4186 '`','S','e','l','f','R','e','g','`',0};
4188 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4189 if (rc != ERROR_SUCCESS)
4191 TRACE("no SelfReg table\n");
4192 return ERROR_SUCCESS;
4195 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4196 msiobj_release(&view->hdr);
4198 return ERROR_SUCCESS;
4201 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4203 static const WCHAR regsvr32[] =
4204 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
4205 static const WCHAR close[] = {'\"',0};
4206 MSIPACKAGE *package = param;
4207 LPCWSTR filename;
4208 LPWSTR cmdline;
4209 MSIFILE *file;
4210 DWORD len;
4211 STARTUPINFOW si;
4212 PROCESS_INFORMATION pi;
4213 BOOL ret;
4214 MSIRECORD *uirow;
4215 LPWSTR uipath, p;
4217 memset( &si, 0, sizeof(STARTUPINFOW) );
4219 filename = MSI_RecordGetString( row, 1 );
4220 file = get_loaded_file( package, filename );
4222 if (!file)
4224 ERR("Unable to find file id %s\n", debugstr_w(filename));
4225 return ERROR_SUCCESS;
4228 len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
4230 cmdline = msi_alloc( len * sizeof(WCHAR) );
4231 strcpyW( cmdline, regsvr32 );
4232 strcatW( cmdline, file->TargetPath );
4233 strcatW( cmdline, close );
4235 TRACE("Unregistering %s\n", debugstr_w(cmdline));
4237 ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
4238 if (ret)
4240 CloseHandle( pi.hThread );
4241 msi_dialog_check_messages( pi.hProcess );
4242 CloseHandle( pi.hProcess );
4245 msi_free( cmdline );
4247 uirow = MSI_CreateRecord( 2 );
4248 uipath = strdupW( file->TargetPath );
4249 if ((p = strrchrW( uipath, '\\' )))
4251 *p = 0;
4252 MSI_RecordSetStringW( uirow, 1, ++p );
4254 MSI_RecordSetStringW( uirow, 2, uipath );
4255 ui_actiondata( package, szSelfUnregModules, uirow );
4256 msiobj_release( &uirow->hdr );
4257 msi_free( uipath );
4258 /* FIXME call ui_progress? */
4260 return ERROR_SUCCESS;
4263 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4265 UINT rc;
4266 MSIQUERY *view;
4267 static const WCHAR query[] =
4268 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4269 '`','S','e','l','f','R','e','g','`',0};
4271 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4272 if (rc != ERROR_SUCCESS)
4274 TRACE("no SelfReg table\n");
4275 return ERROR_SUCCESS;
4278 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4279 msiobj_release( &view->hdr );
4281 return ERROR_SUCCESS;
4284 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4286 MSIFEATURE *feature;
4287 UINT rc;
4288 HKEY hkey;
4289 HKEY userdata = NULL;
4291 if (!msi_check_publish(package))
4292 return ERROR_SUCCESS;
4294 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4295 &hkey, TRUE);
4296 if (rc != ERROR_SUCCESS)
4297 goto end;
4299 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4300 &userdata, TRUE);
4301 if (rc != ERROR_SUCCESS)
4302 goto end;
4304 /* here the guids are base 85 encoded */
4305 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4307 ComponentList *cl;
4308 LPWSTR data = NULL;
4309 GUID clsid;
4310 INT size;
4311 BOOL absent = FALSE;
4312 MSIRECORD *uirow;
4314 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4315 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4316 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4318 size = 1;
4319 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4321 size += 21;
4323 if (feature->Feature_Parent)
4324 size += strlenW( feature->Feature_Parent )+2;
4326 data = msi_alloc(size * sizeof(WCHAR));
4328 data[0] = 0;
4329 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4331 MSICOMPONENT* component = cl->component;
4332 WCHAR buf[21];
4334 buf[0] = 0;
4335 if (component->ComponentId)
4337 TRACE("From %s\n",debugstr_w(component->ComponentId));
4338 CLSIDFromString(component->ComponentId, &clsid);
4339 encode_base85_guid(&clsid,buf);
4340 TRACE("to %s\n",debugstr_w(buf));
4341 strcatW(data,buf);
4345 if (feature->Feature_Parent)
4347 static const WCHAR sep[] = {'\2',0};
4348 strcatW(data,sep);
4349 strcatW(data,feature->Feature_Parent);
4352 msi_reg_set_val_str( userdata, feature->Feature, data );
4353 msi_free(data);
4355 size = 0;
4356 if (feature->Feature_Parent)
4357 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4358 if (!absent)
4360 size += sizeof(WCHAR);
4361 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4362 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4364 else
4366 size += 2*sizeof(WCHAR);
4367 data = msi_alloc(size);
4368 data[0] = 0x6;
4369 data[1] = 0;
4370 if (feature->Feature_Parent)
4371 strcpyW( &data[1], feature->Feature_Parent );
4372 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4373 (LPBYTE)data,size);
4374 msi_free(data);
4377 /* the UI chunk */
4378 uirow = MSI_CreateRecord( 1 );
4379 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4380 ui_actiondata( package, szPublishFeatures, uirow);
4381 msiobj_release( &uirow->hdr );
4382 /* FIXME: call ui_progress? */
4385 end:
4386 RegCloseKey(hkey);
4387 RegCloseKey(userdata);
4388 return rc;
4391 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4393 UINT r;
4394 HKEY hkey;
4396 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4398 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4399 &hkey, FALSE);
4400 if (r == ERROR_SUCCESS)
4402 RegDeleteValueW(hkey, feature->Feature);
4403 RegCloseKey(hkey);
4406 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4407 &hkey, FALSE);
4408 if (r == ERROR_SUCCESS)
4410 RegDeleteValueW(hkey, feature->Feature);
4411 RegCloseKey(hkey);
4414 return ERROR_SUCCESS;
4417 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4419 MSIFEATURE *feature;
4421 if (!msi_check_unpublish(package))
4422 return ERROR_SUCCESS;
4424 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4426 msi_unpublish_feature(package, feature);
4429 return ERROR_SUCCESS;
4432 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4434 LPWSTR prop, val, key;
4435 SYSTEMTIME systime;
4436 DWORD size, langid;
4437 WCHAR date[9];
4438 LPWSTR buffer;
4440 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4441 static const WCHAR szWindowsInstaller[] =
4442 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4443 static const WCHAR modpath_fmt[] =
4444 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4445 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4446 static const WCHAR szModifyPath[] =
4447 {'M','o','d','i','f','y','P','a','t','h',0};
4448 static const WCHAR szUninstallString[] =
4449 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4450 static const WCHAR szEstimatedSize[] =
4451 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4452 static const WCHAR szProductLanguage[] =
4453 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4454 static const WCHAR szProductVersion[] =
4455 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4456 static const WCHAR szProductName[] =
4457 {'P','r','o','d','u','c','t','N','a','m','e',0};
4458 static const WCHAR szDisplayName[] =
4459 {'D','i','s','p','l','a','y','N','a','m','e',0};
4460 static const WCHAR szDisplayVersion[] =
4461 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4462 static const WCHAR szManufacturer[] =
4463 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4465 static const LPCSTR propval[] = {
4466 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4467 "ARPCONTACT", "Contact",
4468 "ARPCOMMENTS", "Comments",
4469 "ProductName", "DisplayName",
4470 "ProductVersion", "DisplayVersion",
4471 "ARPHELPLINK", "HelpLink",
4472 "ARPHELPTELEPHONE", "HelpTelephone",
4473 "ARPINSTALLLOCATION", "InstallLocation",
4474 "SourceDir", "InstallSource",
4475 "Manufacturer", "Publisher",
4476 "ARPREADME", "Readme",
4477 "ARPSIZE", "Size",
4478 "ARPURLINFOABOUT", "URLInfoAbout",
4479 "ARPURLUPDATEINFO", "URLUpdateInfo",
4480 NULL,
4482 const LPCSTR *p = propval;
4484 while (*p)
4486 prop = strdupAtoW(*p++);
4487 key = strdupAtoW(*p++);
4488 val = msi_dup_property(package, prop);
4489 msi_reg_set_val_str(hkey, key, val);
4490 msi_free(val);
4491 msi_free(key);
4492 msi_free(prop);
4495 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4497 size = deformat_string(package, modpath_fmt, &buffer);
4498 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4499 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4500 msi_free(buffer);
4502 /* FIXME: Write real Estimated Size when we have it */
4503 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4505 buffer = msi_dup_property(package, szProductName);
4506 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4507 msi_free(buffer);
4509 buffer = msi_dup_property(package, cszSourceDir);
4510 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4511 msi_free(buffer);
4513 buffer = msi_dup_property(package, szManufacturer);
4514 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4515 msi_free(buffer);
4517 GetLocalTime(&systime);
4518 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4519 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4521 langid = msi_get_property_int(package, szProductLanguage, 0);
4522 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4524 buffer = msi_dup_property(package, szProductVersion);
4525 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4526 if (buffer)
4528 DWORD verdword = msi_version_str_to_dword(buffer);
4530 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4531 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4532 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4533 msi_free(buffer);
4536 return ERROR_SUCCESS;
4539 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4541 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4542 LPWSTR upgrade_code;
4543 HKEY hkey, props;
4544 HKEY upgrade;
4545 UINT rc;
4547 static const WCHAR szUpgradeCode[] = {
4548 'U','p','g','r','a','d','e','C','o','d','e',0};
4550 /* FIXME: also need to publish if the product is in advertise mode */
4551 if (!msi_check_publish(package))
4552 return ERROR_SUCCESS;
4554 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4555 if (rc != ERROR_SUCCESS)
4556 return rc;
4558 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4559 NULL, &props, TRUE);
4560 if (rc != ERROR_SUCCESS)
4561 goto done;
4563 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4564 msi_free( package->db->localfile );
4565 package->db->localfile = NULL;
4567 rc = msi_publish_install_properties(package, hkey);
4568 if (rc != ERROR_SUCCESS)
4569 goto done;
4571 rc = msi_publish_install_properties(package, props);
4572 if (rc != ERROR_SUCCESS)
4573 goto done;
4575 upgrade_code = msi_dup_property(package, szUpgradeCode);
4576 if (upgrade_code)
4578 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4579 squash_guid(package->ProductCode, squashed_pc);
4580 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4581 RegCloseKey(upgrade);
4582 msi_free(upgrade_code);
4585 done:
4586 RegCloseKey(hkey);
4588 return ERROR_SUCCESS;
4591 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4593 return execute_script(package,INSTALL_SCRIPT);
4596 static UINT msi_unpublish_product(MSIPACKAGE *package)
4598 LPWSTR upgrade;
4599 LPWSTR remove = NULL;
4600 LPWSTR *features = NULL;
4601 BOOL full_uninstall = TRUE;
4602 MSIFEATURE *feature;
4604 static const WCHAR szUpgradeCode[] =
4605 {'U','p','g','r','a','d','e','C','o','d','e',0};
4607 remove = msi_dup_property(package, szRemove);
4608 if (!remove)
4609 return ERROR_SUCCESS;
4611 features = msi_split_string(remove, ',');
4612 if (!features)
4614 msi_free(remove);
4615 ERR("REMOVE feature list is empty!\n");
4616 return ERROR_FUNCTION_FAILED;
4619 if (!lstrcmpW(features[0], szAll))
4620 full_uninstall = TRUE;
4621 else
4623 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4625 if (feature->Action != INSTALLSTATE_ABSENT)
4626 full_uninstall = FALSE;
4630 if (!full_uninstall)
4631 goto done;
4633 MSIREG_DeleteProductKey(package->ProductCode);
4634 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4635 MSIREG_DeleteUninstallKey(package->ProductCode);
4637 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4639 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4640 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4642 else
4644 MSIREG_DeleteUserProductKey(package->ProductCode);
4645 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4648 upgrade = msi_dup_property(package, szUpgradeCode);
4649 if (upgrade)
4651 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4652 msi_free(upgrade);
4655 done:
4656 msi_free(remove);
4657 msi_free(features);
4658 return ERROR_SUCCESS;
4661 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4663 UINT rc;
4665 rc = msi_unpublish_product(package);
4666 if (rc != ERROR_SUCCESS)
4667 return rc;
4669 /* turn off scheduling */
4670 package->script->CurrentlyScripting= FALSE;
4672 /* first do the same as an InstallExecute */
4673 rc = ACTION_InstallExecute(package);
4674 if (rc != ERROR_SUCCESS)
4675 return rc;
4677 /* then handle Commit Actions */
4678 rc = execute_script(package,COMMIT_SCRIPT);
4680 return rc;
4683 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4685 static const WCHAR RunOnce[] = {
4686 'S','o','f','t','w','a','r','e','\\',
4687 'M','i','c','r','o','s','o','f','t','\\',
4688 'W','i','n','d','o','w','s','\\',
4689 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4690 'R','u','n','O','n','c','e',0};
4691 static const WCHAR InstallRunOnce[] = {
4692 'S','o','f','t','w','a','r','e','\\',
4693 'M','i','c','r','o','s','o','f','t','\\',
4694 'W','i','n','d','o','w','s','\\',
4695 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4696 'I','n','s','t','a','l','l','e','r','\\',
4697 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4699 static const WCHAR msiexec_fmt[] = {
4700 '%','s',
4701 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4702 '\"','%','s','\"',0};
4703 static const WCHAR install_fmt[] = {
4704 '/','I',' ','\"','%','s','\"',' ',
4705 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4706 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4707 WCHAR buffer[256], sysdir[MAX_PATH];
4708 HKEY hkey;
4709 WCHAR squished_pc[100];
4711 squash_guid(package->ProductCode,squished_pc);
4713 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4714 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4715 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4716 squished_pc);
4718 msi_reg_set_val_str( hkey, squished_pc, buffer );
4719 RegCloseKey(hkey);
4721 TRACE("Reboot command %s\n",debugstr_w(buffer));
4723 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4724 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4726 msi_reg_set_val_str( hkey, squished_pc, buffer );
4727 RegCloseKey(hkey);
4729 return ERROR_INSTALL_SUSPEND;
4732 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4734 DWORD attrib;
4735 UINT rc;
4738 * We are currently doing what should be done here in the top level Install
4739 * however for Administrative and uninstalls this step will be needed
4741 if (!package->PackagePath)
4742 return ERROR_SUCCESS;
4744 msi_set_sourcedir_props(package, TRUE);
4746 attrib = GetFileAttributesW(package->db->path);
4747 if (attrib == INVALID_FILE_ATTRIBUTES)
4749 LPWSTR prompt;
4750 LPWSTR msg;
4751 DWORD size = 0;
4753 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4754 package->Context, MSICODE_PRODUCT,
4755 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4756 if (rc == ERROR_MORE_DATA)
4758 prompt = msi_alloc(size * sizeof(WCHAR));
4759 MsiSourceListGetInfoW(package->ProductCode, NULL,
4760 package->Context, MSICODE_PRODUCT,
4761 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4763 else
4764 prompt = strdupW(package->db->path);
4766 msg = generate_error_string(package,1302,1,prompt);
4767 while(attrib == INVALID_FILE_ATTRIBUTES)
4769 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4770 if (rc == IDCANCEL)
4772 rc = ERROR_INSTALL_USEREXIT;
4773 break;
4775 attrib = GetFileAttributesW(package->db->path);
4777 msi_free(prompt);
4778 rc = ERROR_SUCCESS;
4780 else
4781 return ERROR_SUCCESS;
4783 return rc;
4786 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4788 HKEY hkey=0;
4789 LPWSTR buffer;
4790 LPWSTR productid;
4791 UINT rc,i;
4793 static const WCHAR szPropKeys[][80] =
4795 {'P','r','o','d','u','c','t','I','D',0},
4796 {'U','S','E','R','N','A','M','E',0},
4797 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4798 {0},
4801 static const WCHAR szRegKeys[][80] =
4803 {'P','r','o','d','u','c','t','I','D',0},
4804 {'R','e','g','O','w','n','e','r',0},
4805 {'R','e','g','C','o','m','p','a','n','y',0},
4806 {0},
4809 if (msi_check_unpublish(package))
4811 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4812 return ERROR_SUCCESS;
4815 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4816 if (!productid)
4817 return ERROR_SUCCESS;
4819 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4820 NULL, &hkey, TRUE);
4821 if (rc != ERROR_SUCCESS)
4822 goto end;
4824 for( i = 0; szPropKeys[i][0]; i++ )
4826 buffer = msi_dup_property( package, szPropKeys[i] );
4827 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4828 msi_free( buffer );
4831 end:
4832 msi_free(productid);
4833 RegCloseKey(hkey);
4835 /* FIXME: call ui_actiondata */
4837 return rc;
4841 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4843 UINT rc;
4845 package->script->InWhatSequence |= SEQUENCE_EXEC;
4846 rc = ACTION_ProcessExecSequence(package,FALSE);
4847 return rc;
4851 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4853 MSIPACKAGE *package = param;
4854 LPCWSTR compgroupid, component, feature, qualifier, text;
4855 LPWSTR advertise = NULL, output = NULL;
4856 HKEY hkey;
4857 UINT rc;
4858 MSICOMPONENT *comp;
4859 MSIFEATURE *feat;
4860 DWORD sz;
4861 MSIRECORD *uirow;
4863 feature = MSI_RecordGetString(rec, 5);
4864 feat = get_loaded_feature(package, feature);
4865 if (!feat)
4866 return ERROR_SUCCESS;
4868 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
4869 feat->ActionRequest != INSTALLSTATE_SOURCE &&
4870 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
4872 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
4873 feat->Action = feat->Installed;
4874 return ERROR_SUCCESS;
4877 component = MSI_RecordGetString(rec, 3);
4878 comp = get_loaded_component(package, component);
4879 if (!comp)
4880 return ERROR_SUCCESS;
4882 compgroupid = MSI_RecordGetString(rec,1);
4883 qualifier = MSI_RecordGetString(rec,2);
4885 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4886 if (rc != ERROR_SUCCESS)
4887 goto end;
4889 text = MSI_RecordGetString(rec,4);
4890 advertise = create_component_advertise_string(package, comp, feature);
4892 sz = strlenW(advertise);
4894 if (text)
4895 sz += lstrlenW(text);
4897 sz+=3;
4898 sz *= sizeof(WCHAR);
4900 output = msi_alloc_zero(sz);
4901 strcpyW(output,advertise);
4902 msi_free(advertise);
4904 if (text)
4905 strcatW(output,text);
4907 msi_reg_set_val_multi_str( hkey, qualifier, output );
4909 end:
4910 RegCloseKey(hkey);
4911 msi_free(output);
4913 /* the UI chunk */
4914 uirow = MSI_CreateRecord( 2 );
4915 MSI_RecordSetStringW( uirow, 1, compgroupid );
4916 MSI_RecordSetStringW( uirow, 2, qualifier);
4917 ui_actiondata( package, szPublishComponents, uirow);
4918 msiobj_release( &uirow->hdr );
4919 /* FIXME: call ui_progress? */
4921 return rc;
4925 * At present I am ignorning the advertised components part of this and only
4926 * focusing on the qualified component sets
4928 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4930 UINT rc;
4931 MSIQUERY * view;
4932 static const WCHAR ExecSeqQuery[] =
4933 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4934 '`','P','u','b','l','i','s','h',
4935 'C','o','m','p','o','n','e','n','t','`',0};
4937 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4938 if (rc != ERROR_SUCCESS)
4939 return ERROR_SUCCESS;
4941 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4942 msiobj_release(&view->hdr);
4944 return rc;
4947 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
4949 static const WCHAR szInstallerComponents[] = {
4950 'S','o','f','t','w','a','r','e','\\',
4951 'M','i','c','r','o','s','o','f','t','\\',
4952 'I','n','s','t','a','l','l','e','r','\\',
4953 'C','o','m','p','o','n','e','n','t','s','\\',0};
4955 MSIPACKAGE *package = param;
4956 LPCWSTR compgroupid, component, feature, qualifier;
4957 MSICOMPONENT *comp;
4958 MSIFEATURE *feat;
4959 MSIRECORD *uirow;
4960 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
4961 LONG res;
4963 feature = MSI_RecordGetString( rec, 5 );
4964 feat = get_loaded_feature( package, feature );
4965 if (!feat)
4966 return ERROR_SUCCESS;
4968 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
4970 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
4971 feat->Action = feat->Installed;
4972 return ERROR_SUCCESS;
4975 component = MSI_RecordGetString( rec, 3 );
4976 comp = get_loaded_component( package, component );
4977 if (!comp)
4978 return ERROR_SUCCESS;
4980 compgroupid = MSI_RecordGetString( rec, 1 );
4981 qualifier = MSI_RecordGetString( rec, 2 );
4983 squash_guid( compgroupid, squashed );
4984 strcpyW( keypath, szInstallerComponents );
4985 strcatW( keypath, squashed );
4987 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
4988 if (res != ERROR_SUCCESS)
4990 WARN("Unable to delete component key %d\n", res);
4993 uirow = MSI_CreateRecord( 2 );
4994 MSI_RecordSetStringW( uirow, 1, compgroupid );
4995 MSI_RecordSetStringW( uirow, 2, qualifier );
4996 ui_actiondata( package, szUnpublishComponents, uirow );
4997 msiobj_release( &uirow->hdr );
4999 return ERROR_SUCCESS;
5002 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5004 UINT rc;
5005 MSIQUERY *view;
5006 static const WCHAR query[] =
5007 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5008 '`','P','u','b','l','i','s','h',
5009 'C','o','m','p','o','n','e','n','t','`',0};
5011 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5012 if (rc != ERROR_SUCCESS)
5013 return ERROR_SUCCESS;
5015 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5016 msiobj_release( &view->hdr );
5018 return rc;
5021 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5023 MSIPACKAGE *package = param;
5024 MSIRECORD *row;
5025 MSIFILE *file;
5026 SC_HANDLE hscm, service = NULL;
5027 LPCWSTR comp, depends, pass;
5028 LPWSTR name = NULL, disp = NULL;
5029 LPCWSTR load_order, serv_name, key;
5030 DWORD serv_type, start_type;
5031 DWORD err_control;
5033 static const WCHAR query[] =
5034 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5035 '`','C','o','m','p','o','n','e','n','t','`',' ',
5036 'W','H','E','R','E',' ',
5037 '`','C','o','m','p','o','n','e','n','t','`',' ',
5038 '=','\'','%','s','\'',0};
5040 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5041 if (!hscm)
5043 ERR("Failed to open the SC Manager!\n");
5044 goto done;
5047 start_type = MSI_RecordGetInteger(rec, 5);
5048 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5049 goto done;
5051 depends = MSI_RecordGetString(rec, 8);
5052 if (depends && *depends)
5053 FIXME("Dependency list unhandled!\n");
5055 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5056 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5057 serv_type = MSI_RecordGetInteger(rec, 4);
5058 err_control = MSI_RecordGetInteger(rec, 6);
5059 load_order = MSI_RecordGetString(rec, 7);
5060 serv_name = MSI_RecordGetString(rec, 9);
5061 pass = MSI_RecordGetString(rec, 10);
5062 comp = MSI_RecordGetString(rec, 12);
5064 /* fetch the service path */
5065 row = MSI_QueryGetRecord(package->db, query, comp);
5066 if (!row)
5068 ERR("Control query failed!\n");
5069 goto done;
5072 key = MSI_RecordGetString(row, 6);
5074 file = get_loaded_file(package, key);
5075 msiobj_release(&row->hdr);
5076 if (!file)
5078 ERR("Failed to load the service file\n");
5079 goto done;
5082 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5083 start_type, err_control, file->TargetPath,
5084 load_order, NULL, NULL, serv_name, pass);
5085 if (!service)
5087 if (GetLastError() != ERROR_SERVICE_EXISTS)
5088 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5091 done:
5092 CloseServiceHandle(service);
5093 CloseServiceHandle(hscm);
5094 msi_free(name);
5095 msi_free(disp);
5097 return ERROR_SUCCESS;
5100 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5102 UINT rc;
5103 MSIQUERY * view;
5104 static const WCHAR ExecSeqQuery[] =
5105 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5106 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5108 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5109 if (rc != ERROR_SUCCESS)
5110 return ERROR_SUCCESS;
5112 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5113 msiobj_release(&view->hdr);
5115 return rc;
5118 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5119 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5121 LPCWSTR *vector, *temp_vector;
5122 LPWSTR p, q;
5123 DWORD sep_len;
5125 static const WCHAR separator[] = {'[','~',']',0};
5127 *numargs = 0;
5128 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5130 if (!args)
5131 return NULL;
5133 vector = msi_alloc(sizeof(LPWSTR));
5134 if (!vector)
5135 return NULL;
5137 p = args;
5140 (*numargs)++;
5141 vector[*numargs - 1] = p;
5143 if ((q = strstrW(p, separator)))
5145 *q = '\0';
5147 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5148 if (!temp_vector)
5150 msi_free(vector);
5151 return NULL;
5153 vector = temp_vector;
5155 p = q + sep_len;
5157 } while (q);
5159 return vector;
5162 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5164 MSIPACKAGE *package = param;
5165 MSICOMPONENT *comp;
5166 SC_HANDLE scm = NULL, service = NULL;
5167 LPCWSTR component, *vector = NULL;
5168 LPWSTR name, args;
5169 DWORD event, numargs;
5170 UINT r = ERROR_FUNCTION_FAILED;
5172 component = MSI_RecordGetString(rec, 6);
5173 comp = get_loaded_component(package, component);
5174 if (!comp)
5175 return ERROR_SUCCESS;
5177 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5179 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5180 comp->Action = comp->Installed;
5181 return ERROR_SUCCESS;
5183 comp->Action = INSTALLSTATE_LOCAL;
5185 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5186 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5187 event = MSI_RecordGetInteger(rec, 3);
5189 if (!(event & msidbServiceControlEventStart))
5191 r = ERROR_SUCCESS;
5192 goto done;
5195 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5196 if (!scm)
5198 ERR("Failed to open the service control manager\n");
5199 goto done;
5202 service = OpenServiceW(scm, name, SERVICE_START);
5203 if (!service)
5205 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5206 goto done;
5209 vector = msi_service_args_to_vector(args, &numargs);
5211 if (!StartServiceW(service, numargs, vector) &&
5212 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5214 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5215 goto done;
5218 r = ERROR_SUCCESS;
5220 done:
5221 CloseServiceHandle(service);
5222 CloseServiceHandle(scm);
5224 msi_free(name);
5225 msi_free(args);
5226 msi_free(vector);
5227 return r;
5230 static UINT ACTION_StartServices( MSIPACKAGE *package )
5232 UINT rc;
5233 MSIQUERY *view;
5235 static const WCHAR query[] = {
5236 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5237 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5239 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5240 if (rc != ERROR_SUCCESS)
5241 return ERROR_SUCCESS;
5243 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5244 msiobj_release(&view->hdr);
5246 return rc;
5249 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5251 DWORD i, needed, count;
5252 ENUM_SERVICE_STATUSW *dependencies;
5253 SERVICE_STATUS ss;
5254 SC_HANDLE depserv;
5256 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5257 0, &needed, &count))
5258 return TRUE;
5260 if (GetLastError() != ERROR_MORE_DATA)
5261 return FALSE;
5263 dependencies = msi_alloc(needed);
5264 if (!dependencies)
5265 return FALSE;
5267 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5268 needed, &needed, &count))
5269 goto error;
5271 for (i = 0; i < count; i++)
5273 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5274 SERVICE_STOP | SERVICE_QUERY_STATUS);
5275 if (!depserv)
5276 goto error;
5278 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5279 goto error;
5282 return TRUE;
5284 error:
5285 msi_free(dependencies);
5286 return FALSE;
5289 static UINT stop_service( LPCWSTR name )
5291 SC_HANDLE scm = NULL, service = NULL;
5292 SERVICE_STATUS status;
5293 SERVICE_STATUS_PROCESS ssp;
5294 DWORD needed;
5296 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5297 if (!scm)
5299 WARN("Failed to open the SCM: %d\n", GetLastError());
5300 goto done;
5303 service = OpenServiceW(scm, name,
5304 SERVICE_STOP |
5305 SERVICE_QUERY_STATUS |
5306 SERVICE_ENUMERATE_DEPENDENTS);
5307 if (!service)
5309 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5310 goto done;
5313 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5314 sizeof(SERVICE_STATUS_PROCESS), &needed))
5316 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5317 goto done;
5320 if (ssp.dwCurrentState == SERVICE_STOPPED)
5321 goto done;
5323 stop_service_dependents(scm, service);
5325 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5326 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5328 done:
5329 CloseServiceHandle(service);
5330 CloseServiceHandle(scm);
5332 return ERROR_SUCCESS;
5335 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5337 MSIPACKAGE *package = param;
5338 MSICOMPONENT *comp;
5339 LPCWSTR component;
5340 LPWSTR name;
5341 DWORD event;
5343 event = MSI_RecordGetInteger( rec, 3 );
5344 if (!(event & msidbServiceControlEventStop))
5345 return ERROR_SUCCESS;
5347 component = MSI_RecordGetString( rec, 6 );
5348 comp = get_loaded_component( package, component );
5349 if (!comp)
5350 return ERROR_SUCCESS;
5352 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5354 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5355 comp->Action = comp->Installed;
5356 return ERROR_SUCCESS;
5358 comp->Action = INSTALLSTATE_ABSENT;
5360 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5361 stop_service( name );
5362 msi_free( name );
5364 return ERROR_SUCCESS;
5367 static UINT ACTION_StopServices( MSIPACKAGE *package )
5369 UINT rc;
5370 MSIQUERY *view;
5372 static const WCHAR query[] = {
5373 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5374 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5376 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5377 if (rc != ERROR_SUCCESS)
5378 return ERROR_SUCCESS;
5380 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5381 msiobj_release(&view->hdr);
5383 return rc;
5386 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5388 MSIPACKAGE *package = param;
5389 MSICOMPONENT *comp;
5390 LPCWSTR component;
5391 LPWSTR name = NULL;
5392 DWORD event;
5393 SC_HANDLE scm = NULL, service = NULL;
5395 event = MSI_RecordGetInteger( rec, 3 );
5396 if (!(event & msidbServiceControlEventDelete))
5397 return ERROR_SUCCESS;
5399 component = MSI_RecordGetString(rec, 6);
5400 comp = get_loaded_component(package, component);
5401 if (!comp)
5402 return ERROR_SUCCESS;
5404 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5406 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5407 comp->Action = comp->Installed;
5408 return ERROR_SUCCESS;
5410 comp->Action = INSTALLSTATE_ABSENT;
5412 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5413 stop_service( name );
5415 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5416 if (!scm)
5418 WARN("Failed to open the SCM: %d\n", GetLastError());
5419 goto done;
5422 service = OpenServiceW( scm, name, DELETE );
5423 if (!service)
5425 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5426 goto done;
5429 if (!DeleteService( service ))
5430 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5432 done:
5433 CloseServiceHandle( service );
5434 CloseServiceHandle( scm );
5435 msi_free( name );
5437 return ERROR_SUCCESS;
5440 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5442 UINT rc;
5443 MSIQUERY *view;
5445 static const WCHAR query[] = {
5446 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5447 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5449 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5450 if (rc != ERROR_SUCCESS)
5451 return ERROR_SUCCESS;
5453 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5454 msiobj_release( &view->hdr );
5456 return rc;
5459 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5461 MSIFILE *file;
5463 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5465 if (!lstrcmpW(file->File, filename))
5466 return file;
5469 return NULL;
5472 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5474 MSIPACKAGE *package = param;
5475 LPWSTR driver, driver_path, ptr;
5476 WCHAR outpath[MAX_PATH];
5477 MSIFILE *driver_file, *setup_file;
5478 LPCWSTR desc;
5479 DWORD len, usage;
5480 UINT r = ERROR_SUCCESS;
5482 static const WCHAR driver_fmt[] = {
5483 'D','r','i','v','e','r','=','%','s',0};
5484 static const WCHAR setup_fmt[] = {
5485 'S','e','t','u','p','=','%','s',0};
5486 static const WCHAR usage_fmt[] = {
5487 'F','i','l','e','U','s','a','g','e','=','1',0};
5489 desc = MSI_RecordGetString(rec, 3);
5491 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5492 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5494 if (!driver_file)
5496 ERR("ODBC Driver entry not found!\n");
5497 return ERROR_FUNCTION_FAILED;
5500 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5501 if (setup_file)
5502 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5503 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5505 driver = msi_alloc(len * sizeof(WCHAR));
5506 if (!driver)
5507 return ERROR_OUTOFMEMORY;
5509 ptr = driver;
5510 lstrcpyW(ptr, desc);
5511 ptr += lstrlenW(ptr) + 1;
5513 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5514 ptr += len + 1;
5516 if (setup_file)
5518 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5519 ptr += len + 1;
5522 lstrcpyW(ptr, usage_fmt);
5523 ptr += lstrlenW(ptr) + 1;
5524 *ptr = '\0';
5526 driver_path = strdupW(driver_file->TargetPath);
5527 ptr = strrchrW(driver_path, '\\');
5528 if (ptr) *ptr = '\0';
5530 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5531 NULL, ODBC_INSTALL_COMPLETE, &usage))
5533 ERR("Failed to install SQL driver!\n");
5534 r = ERROR_FUNCTION_FAILED;
5537 msi_free(driver);
5538 msi_free(driver_path);
5540 return r;
5543 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5545 MSIPACKAGE *package = param;
5546 LPWSTR translator, translator_path, ptr;
5547 WCHAR outpath[MAX_PATH];
5548 MSIFILE *translator_file, *setup_file;
5549 LPCWSTR desc;
5550 DWORD len, usage;
5551 UINT r = ERROR_SUCCESS;
5553 static const WCHAR translator_fmt[] = {
5554 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5555 static const WCHAR setup_fmt[] = {
5556 'S','e','t','u','p','=','%','s',0};
5558 desc = MSI_RecordGetString(rec, 3);
5560 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5561 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5563 if (!translator_file)
5565 ERR("ODBC Translator entry not found!\n");
5566 return ERROR_FUNCTION_FAILED;
5569 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5570 if (setup_file)
5571 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5573 translator = msi_alloc(len * sizeof(WCHAR));
5574 if (!translator)
5575 return ERROR_OUTOFMEMORY;
5577 ptr = translator;
5578 lstrcpyW(ptr, desc);
5579 ptr += lstrlenW(ptr) + 1;
5581 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5582 ptr += len + 1;
5584 if (setup_file)
5586 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5587 ptr += len + 1;
5589 *ptr = '\0';
5591 translator_path = strdupW(translator_file->TargetPath);
5592 ptr = strrchrW(translator_path, '\\');
5593 if (ptr) *ptr = '\0';
5595 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5596 NULL, ODBC_INSTALL_COMPLETE, &usage))
5598 ERR("Failed to install SQL translator!\n");
5599 r = ERROR_FUNCTION_FAILED;
5602 msi_free(translator);
5603 msi_free(translator_path);
5605 return r;
5608 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5610 LPWSTR attrs;
5611 LPCWSTR desc, driver;
5612 WORD request = ODBC_ADD_SYS_DSN;
5613 INT registration;
5614 DWORD len;
5615 UINT r = ERROR_SUCCESS;
5617 static const WCHAR attrs_fmt[] = {
5618 'D','S','N','=','%','s',0 };
5620 desc = MSI_RecordGetString(rec, 3);
5621 driver = MSI_RecordGetString(rec, 4);
5622 registration = MSI_RecordGetInteger(rec, 5);
5624 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5625 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5627 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5628 attrs = msi_alloc(len * sizeof(WCHAR));
5629 if (!attrs)
5630 return ERROR_OUTOFMEMORY;
5632 len = sprintfW(attrs, attrs_fmt, desc);
5633 attrs[len + 1] = 0;
5635 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5637 ERR("Failed to install SQL data source!\n");
5638 r = ERROR_FUNCTION_FAILED;
5641 msi_free(attrs);
5643 return r;
5646 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5648 UINT rc;
5649 MSIQUERY *view;
5651 static const WCHAR driver_query[] = {
5652 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5653 'O','D','B','C','D','r','i','v','e','r',0 };
5655 static const WCHAR translator_query[] = {
5656 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5657 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5659 static const WCHAR source_query[] = {
5660 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5661 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5663 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5664 if (rc != ERROR_SUCCESS)
5665 return ERROR_SUCCESS;
5667 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5668 msiobj_release(&view->hdr);
5670 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5671 if (rc != ERROR_SUCCESS)
5672 return ERROR_SUCCESS;
5674 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5675 msiobj_release(&view->hdr);
5677 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5678 if (rc != ERROR_SUCCESS)
5679 return ERROR_SUCCESS;
5681 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5682 msiobj_release(&view->hdr);
5684 return rc;
5687 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5689 DWORD usage;
5690 LPCWSTR desc;
5692 desc = MSI_RecordGetString( rec, 3 );
5693 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5695 WARN("Failed to remove ODBC driver\n");
5697 else if (!usage)
5699 FIXME("Usage count reached 0\n");
5702 return ERROR_SUCCESS;
5705 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5707 DWORD usage;
5708 LPCWSTR desc;
5710 desc = MSI_RecordGetString( rec, 3 );
5711 if (!SQLRemoveTranslatorW( desc, &usage ))
5713 WARN("Failed to remove ODBC translator\n");
5715 else if (!usage)
5717 FIXME("Usage count reached 0\n");
5720 return ERROR_SUCCESS;
5723 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5725 LPWSTR attrs;
5726 LPCWSTR desc, driver;
5727 WORD request = ODBC_REMOVE_SYS_DSN;
5728 INT registration;
5729 DWORD len;
5731 static const WCHAR attrs_fmt[] = {
5732 'D','S','N','=','%','s',0 };
5734 desc = MSI_RecordGetString( rec, 3 );
5735 driver = MSI_RecordGetString( rec, 4 );
5736 registration = MSI_RecordGetInteger( rec, 5 );
5738 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
5739 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
5741 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
5742 attrs = msi_alloc( len * sizeof(WCHAR) );
5743 if (!attrs)
5744 return ERROR_OUTOFMEMORY;
5746 FIXME("Use ODBCSourceAttribute table\n");
5748 len = sprintfW( attrs, attrs_fmt, desc );
5749 attrs[len + 1] = 0;
5751 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
5753 WARN("Failed to remove ODBC data source\n");
5755 msi_free( attrs );
5757 return ERROR_SUCCESS;
5760 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5762 UINT rc;
5763 MSIQUERY *view;
5765 static const WCHAR driver_query[] = {
5766 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5767 'O','D','B','C','D','r','i','v','e','r',0 };
5769 static const WCHAR translator_query[] = {
5770 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5771 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5773 static const WCHAR source_query[] = {
5774 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5775 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5777 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
5778 if (rc != ERROR_SUCCESS)
5779 return ERROR_SUCCESS;
5781 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
5782 msiobj_release( &view->hdr );
5784 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
5785 if (rc != ERROR_SUCCESS)
5786 return ERROR_SUCCESS;
5788 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
5789 msiobj_release( &view->hdr );
5791 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
5792 if (rc != ERROR_SUCCESS)
5793 return ERROR_SUCCESS;
5795 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
5796 msiobj_release( &view->hdr );
5798 return rc;
5801 #define ENV_ACT_SETALWAYS 0x1
5802 #define ENV_ACT_SETABSENT 0x2
5803 #define ENV_ACT_REMOVE 0x4
5804 #define ENV_ACT_REMOVEMATCH 0x8
5806 #define ENV_MOD_MACHINE 0x20000000
5807 #define ENV_MOD_APPEND 0x40000000
5808 #define ENV_MOD_PREFIX 0x80000000
5809 #define ENV_MOD_MASK 0xC0000000
5811 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5813 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5815 LPCWSTR cptr = *name;
5817 static const WCHAR prefix[] = {'[','~',']',0};
5818 static const int prefix_len = 3;
5820 *flags = 0;
5821 while (*cptr)
5823 if (*cptr == '=')
5824 *flags |= ENV_ACT_SETALWAYS;
5825 else if (*cptr == '+')
5826 *flags |= ENV_ACT_SETABSENT;
5827 else if (*cptr == '-')
5828 *flags |= ENV_ACT_REMOVE;
5829 else if (*cptr == '!')
5830 *flags |= ENV_ACT_REMOVEMATCH;
5831 else if (*cptr == '*')
5832 *flags |= ENV_MOD_MACHINE;
5833 else
5834 break;
5836 cptr++;
5837 (*name)++;
5840 if (!*cptr)
5842 ERR("Missing environment variable\n");
5843 return ERROR_FUNCTION_FAILED;
5846 if (*value)
5848 LPCWSTR ptr = *value;
5849 if (!strncmpW(ptr, prefix, prefix_len))
5851 if (ptr[prefix_len] == szSemiColon[0])
5853 *flags |= ENV_MOD_APPEND;
5854 *value += lstrlenW(prefix);
5856 else
5858 *value = NULL;
5861 else if (lstrlenW(*value) >= prefix_len)
5863 ptr += lstrlenW(ptr) - prefix_len;
5864 if (!lstrcmpW(ptr, prefix))
5866 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5868 *flags |= ENV_MOD_PREFIX;
5869 /* the "[~]" will be removed by deformat_string */;
5871 else
5873 *value = NULL;
5879 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5880 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5881 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5882 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5884 ERR("Invalid flags: %08x\n", *flags);
5885 return ERROR_FUNCTION_FAILED;
5888 if (!*flags)
5889 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5891 return ERROR_SUCCESS;
5894 static UINT open_env_key( DWORD flags, HKEY *key )
5896 static const WCHAR user_env[] =
5897 {'E','n','v','i','r','o','n','m','e','n','t',0};
5898 static const WCHAR machine_env[] =
5899 {'S','y','s','t','e','m','\\',
5900 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5901 'C','o','n','t','r','o','l','\\',
5902 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5903 'E','n','v','i','r','o','n','m','e','n','t',0};
5904 const WCHAR *env;
5905 HKEY root;
5906 LONG res;
5908 if (flags & ENV_MOD_MACHINE)
5910 env = machine_env;
5911 root = HKEY_LOCAL_MACHINE;
5913 else
5915 env = user_env;
5916 root = HKEY_CURRENT_USER;
5919 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
5920 if (res != ERROR_SUCCESS)
5922 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
5923 return ERROR_FUNCTION_FAILED;
5926 return ERROR_SUCCESS;
5929 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5931 MSIPACKAGE *package = param;
5932 LPCWSTR name, value, component;
5933 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
5934 DWORD flags, type, size;
5935 UINT res;
5936 HKEY env;
5937 MSICOMPONENT *comp;
5938 MSIRECORD *uirow;
5939 int action = 0;
5941 component = MSI_RecordGetString(rec, 4);
5942 comp = get_loaded_component(package, component);
5943 if (!comp)
5944 return ERROR_SUCCESS;
5946 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5948 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5949 comp->Action = comp->Installed;
5950 return ERROR_SUCCESS;
5952 comp->Action = INSTALLSTATE_LOCAL;
5954 name = MSI_RecordGetString(rec, 2);
5955 value = MSI_RecordGetString(rec, 3);
5957 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5959 res = env_parse_flags(&name, &value, &flags);
5960 if (res != ERROR_SUCCESS || !value)
5961 goto done;
5963 if (value && !deformat_string(package, value, &deformatted))
5965 res = ERROR_OUTOFMEMORY;
5966 goto done;
5969 value = deformatted;
5971 res = open_env_key( flags, &env );
5972 if (res != ERROR_SUCCESS)
5973 goto done;
5975 if (flags & ENV_MOD_MACHINE)
5976 action |= 0x20000000;
5978 size = 0;
5979 type = REG_SZ;
5980 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5981 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5982 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5983 goto done;
5985 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5987 action = 0x2;
5989 /* Nothing to do. */
5990 if (!value)
5992 res = ERROR_SUCCESS;
5993 goto done;
5996 /* If we are appending but the string was empty, strip ; */
5997 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5999 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6000 newval = strdupW(value);
6001 if (!newval)
6003 res = ERROR_OUTOFMEMORY;
6004 goto done;
6007 else
6009 action = 0x1;
6011 /* Contrary to MSDN, +-variable to [~];path works */
6012 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6014 res = ERROR_SUCCESS;
6015 goto done;
6018 data = msi_alloc(size);
6019 if (!data)
6021 RegCloseKey(env);
6022 return ERROR_OUTOFMEMORY;
6025 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6026 if (res != ERROR_SUCCESS)
6027 goto done;
6029 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
6031 action = 0x4;
6032 res = RegDeleteValueW(env, name);
6033 if (res != ERROR_SUCCESS)
6034 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6035 goto done;
6038 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6039 if (flags & ENV_MOD_MASK)
6041 DWORD mod_size;
6042 int multiplier = 0;
6043 if (flags & ENV_MOD_APPEND) multiplier++;
6044 if (flags & ENV_MOD_PREFIX) multiplier++;
6045 mod_size = lstrlenW(value) * multiplier;
6046 size += mod_size * sizeof(WCHAR);
6049 newval = msi_alloc(size);
6050 ptr = newval;
6051 if (!newval)
6053 res = ERROR_OUTOFMEMORY;
6054 goto done;
6057 if (flags & ENV_MOD_PREFIX)
6059 lstrcpyW(newval, value);
6060 ptr = newval + lstrlenW(value);
6061 action |= 0x80000000;
6064 lstrcpyW(ptr, data);
6066 if (flags & ENV_MOD_APPEND)
6068 lstrcatW(newval, value);
6069 action |= 0x40000000;
6072 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6073 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6074 if (res)
6076 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6079 done:
6080 uirow = MSI_CreateRecord( 3 );
6081 MSI_RecordSetStringW( uirow, 1, name );
6082 MSI_RecordSetStringW( uirow, 2, newval );
6083 MSI_RecordSetInteger( uirow, 3, action );
6084 ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6085 msiobj_release( &uirow->hdr );
6087 if (env) RegCloseKey(env);
6088 msi_free(deformatted);
6089 msi_free(data);
6090 msi_free(newval);
6091 return res;
6094 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6096 UINT rc;
6097 MSIQUERY * view;
6098 static const WCHAR ExecSeqQuery[] =
6099 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6100 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6101 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6102 if (rc != ERROR_SUCCESS)
6103 return ERROR_SUCCESS;
6105 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6106 msiobj_release(&view->hdr);
6108 return rc;
6111 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6113 MSIPACKAGE *package = param;
6114 LPCWSTR name, value, component;
6115 LPWSTR deformatted = NULL;
6116 DWORD flags;
6117 HKEY env;
6118 MSICOMPONENT *comp;
6119 MSIRECORD *uirow;
6120 int action = 0;
6121 LONG res;
6122 UINT r;
6124 component = MSI_RecordGetString( rec, 4 );
6125 comp = get_loaded_component( package, component );
6126 if (!comp)
6127 return ERROR_SUCCESS;
6129 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6131 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6132 comp->Action = comp->Installed;
6133 return ERROR_SUCCESS;
6135 comp->Action = INSTALLSTATE_ABSENT;
6137 name = MSI_RecordGetString( rec, 2 );
6138 value = MSI_RecordGetString( rec, 3 );
6140 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6142 r = env_parse_flags( &name, &value, &flags );
6143 if (r != ERROR_SUCCESS)
6144 return r;
6146 if (!(flags & ENV_ACT_REMOVE))
6148 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6149 return ERROR_SUCCESS;
6152 if (value && !deformat_string( package, value, &deformatted ))
6153 return ERROR_OUTOFMEMORY;
6155 value = deformatted;
6157 r = open_env_key( flags, &env );
6158 if (r != ERROR_SUCCESS)
6160 r = ERROR_SUCCESS;
6161 goto done;
6164 if (flags & ENV_MOD_MACHINE)
6165 action |= 0x20000000;
6167 TRACE("Removing %s\n", debugstr_w(name));
6169 res = RegDeleteValueW( env, name );
6170 if (res != ERROR_SUCCESS)
6172 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6173 r = ERROR_SUCCESS;
6176 done:
6177 uirow = MSI_CreateRecord( 3 );
6178 MSI_RecordSetStringW( uirow, 1, name );
6179 MSI_RecordSetStringW( uirow, 2, value );
6180 MSI_RecordSetInteger( uirow, 3, action );
6181 ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6182 msiobj_release( &uirow->hdr );
6184 if (env) RegCloseKey( env );
6185 msi_free( deformatted );
6186 return r;
6189 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6191 UINT rc;
6192 MSIQUERY *view;
6193 static const WCHAR query[] =
6194 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6195 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6197 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6198 if (rc != ERROR_SUCCESS)
6199 return ERROR_SUCCESS;
6201 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6202 msiobj_release( &view->hdr );
6204 return rc;
6207 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
6209 typedef struct
6211 struct list entry;
6212 LPWSTR sourcename;
6213 LPWSTR destname;
6214 LPWSTR source;
6215 LPWSTR dest;
6216 } FILE_LIST;
6218 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
6220 BOOL ret;
6222 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
6223 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
6225 WARN("Source or dest is directory, not moving\n");
6226 return FALSE;
6229 if (options == msidbMoveFileOptionsMove)
6231 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
6232 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
6233 if (!ret)
6235 WARN("MoveFile failed: %d\n", GetLastError());
6236 return FALSE;
6239 else
6241 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
6242 ret = CopyFileW(source, dest, FALSE);
6243 if (!ret)
6245 WARN("CopyFile failed: %d\n", GetLastError());
6246 return FALSE;
6250 return TRUE;
6253 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
6255 LPWSTR path, ptr;
6256 DWORD dirlen, pathlen;
6258 ptr = strrchrW(wildcard, '\\');
6259 dirlen = ptr - wildcard + 1;
6261 pathlen = dirlen + lstrlenW(filename) + 1;
6262 path = msi_alloc(pathlen * sizeof(WCHAR));
6264 lstrcpynW(path, wildcard, dirlen + 1);
6265 lstrcatW(path, filename);
6267 return path;
6270 static void free_file_entry(FILE_LIST *file)
6272 msi_free(file->source);
6273 msi_free(file->dest);
6274 msi_free(file);
6277 static void free_list(FILE_LIST *list)
6279 while (!list_empty(&list->entry))
6281 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
6283 list_remove(&file->entry);
6284 free_file_entry(file);
6288 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
6290 FILE_LIST *new, *file;
6291 LPWSTR ptr, filename;
6292 DWORD size;
6294 new = msi_alloc_zero(sizeof(FILE_LIST));
6295 if (!new)
6296 return FALSE;
6298 new->source = strdupW(source);
6299 ptr = strrchrW(dest, '\\') + 1;
6300 filename = strrchrW(new->source, '\\') + 1;
6302 new->sourcename = filename;
6304 if (*ptr)
6305 new->destname = ptr;
6306 else
6307 new->destname = new->sourcename;
6309 size = (ptr - dest) + lstrlenW(filename) + 1;
6310 new->dest = msi_alloc(size * sizeof(WCHAR));
6311 if (!new->dest)
6313 free_file_entry(new);
6314 return FALSE;
6317 lstrcpynW(new->dest, dest, ptr - dest + 1);
6318 lstrcatW(new->dest, filename);
6320 if (list_empty(&files->entry))
6322 list_add_head(&files->entry, &new->entry);
6323 return TRUE;
6326 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
6328 if (lstrcmpW(source, file->source) < 0)
6330 list_add_before(&file->entry, &new->entry);
6331 return TRUE;
6335 list_add_after(&file->entry, &new->entry);
6336 return TRUE;
6339 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
6341 WIN32_FIND_DATAW wfd;
6342 HANDLE hfile;
6343 LPWSTR path;
6344 BOOL res;
6345 FILE_LIST files, *file;
6346 DWORD size;
6348 hfile = FindFirstFileW(source, &wfd);
6349 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
6351 list_init(&files.entry);
6353 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
6355 if (is_dot_dir(wfd.cFileName)) continue;
6357 path = wildcard_to_file(source, wfd.cFileName);
6358 if (!path)
6360 res = FALSE;
6361 goto done;
6364 add_wildcard(&files, path, dest);
6365 msi_free(path);
6368 /* no files match the wildcard */
6369 if (list_empty(&files.entry))
6370 goto done;
6372 /* only the first wildcard match gets renamed to dest */
6373 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
6374 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
6375 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
6376 if (!file->dest)
6378 res = FALSE;
6379 goto done;
6382 /* file->dest may be shorter after the reallocation, so add a NULL
6383 * terminator. This is needed for the call to strrchrW, as there will no
6384 * longer be a NULL terminator within the bounds of the allocation in this case.
6386 file->dest[size - 1] = '\0';
6387 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
6389 while (!list_empty(&files.entry))
6391 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
6393 msi_move_file(file->source, file->dest, options);
6395 list_remove(&file->entry);
6396 free_file_entry(file);
6399 res = TRUE;
6401 done:
6402 free_list(&files);
6403 FindClose(hfile);
6404 return res;
6407 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
6409 MSIPACKAGE *package = param;
6410 MSICOMPONENT *comp;
6411 LPCWSTR sourcename, component;
6412 LPWSTR destname = NULL;
6413 LPWSTR sourcedir = NULL, destdir = NULL;
6414 LPWSTR source = NULL, dest = NULL;
6415 int options;
6416 DWORD size;
6417 BOOL ret, wildcards;
6419 component = MSI_RecordGetString(rec, 2);
6420 comp = get_loaded_component(package, component);
6421 if (!comp)
6422 return ERROR_SUCCESS;
6424 if (comp->ActionRequest != INSTALLSTATE_LOCAL && comp->ActionRequest != INSTALLSTATE_SOURCE)
6426 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6427 comp->Action = comp->Installed;
6428 return ERROR_SUCCESS;
6430 comp->Action = comp->ActionRequest;
6432 sourcename = MSI_RecordGetString(rec, 3);
6433 options = MSI_RecordGetInteger(rec, 7);
6435 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
6436 if (!sourcedir)
6437 goto done;
6439 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
6440 if (!destdir)
6441 goto done;
6443 if (!sourcename)
6445 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
6446 goto done;
6448 source = strdupW(sourcedir);
6449 if (!source)
6450 goto done;
6452 else
6454 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
6455 source = msi_alloc(size * sizeof(WCHAR));
6456 if (!source)
6457 goto done;
6459 lstrcpyW(source, sourcedir);
6460 if (source[lstrlenW(source) - 1] != '\\')
6461 lstrcatW(source, szBackSlash);
6462 lstrcatW(source, sourcename);
6465 wildcards = strchrW(source, '*') || strchrW(source, '?');
6467 if (MSI_RecordIsNull(rec, 4))
6469 if (!wildcards)
6471 destname = strdupW(sourcename);
6472 if (!destname)
6473 goto done;
6476 else
6478 destname = strdupW(MSI_RecordGetString(rec, 4));
6479 if (destname)
6480 reduce_to_longfilename(destname);
6483 size = 0;
6484 if (destname)
6485 size = lstrlenW(destname);
6487 size += lstrlenW(destdir) + 2;
6488 dest = msi_alloc(size * sizeof(WCHAR));
6489 if (!dest)
6490 goto done;
6492 lstrcpyW(dest, destdir);
6493 if (dest[lstrlenW(dest) - 1] != '\\')
6494 lstrcatW(dest, szBackSlash);
6496 if (destname)
6497 lstrcatW(dest, destname);
6499 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
6501 ret = CreateDirectoryW(destdir, NULL);
6502 if (!ret)
6504 WARN("CreateDirectory failed: %d\n", GetLastError());
6505 return ERROR_SUCCESS;
6509 if (!wildcards)
6510 msi_move_file(source, dest, options);
6511 else
6512 move_files_wildcard(source, dest, options);
6514 done:
6515 msi_free(sourcedir);
6516 msi_free(destdir);
6517 msi_free(destname);
6518 msi_free(source);
6519 msi_free(dest);
6521 return ERROR_SUCCESS;
6524 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
6526 UINT rc;
6527 MSIQUERY *view;
6529 static const WCHAR ExecSeqQuery[] =
6530 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6531 '`','M','o','v','e','F','i','l','e','`',0};
6533 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6534 if (rc != ERROR_SUCCESS)
6535 return ERROR_SUCCESS;
6537 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
6538 msiobj_release(&view->hdr);
6540 return rc;
6543 typedef struct tagMSIASSEMBLY
6545 struct list entry;
6546 MSICOMPONENT *component;
6547 MSIFEATURE *feature;
6548 MSIFILE *file;
6549 LPWSTR manifest;
6550 LPWSTR application;
6551 DWORD attributes;
6552 BOOL installed;
6553 } MSIASSEMBLY;
6555 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6556 DWORD dwReserved);
6557 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6558 LPVOID pvReserved, HMODULE *phModDll);
6560 static BOOL init_functionpointers(void)
6562 HRESULT hr;
6563 HMODULE hfusion;
6564 HMODULE hmscoree;
6566 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6568 hmscoree = LoadLibraryA("mscoree.dll");
6569 if (!hmscoree)
6571 WARN("mscoree.dll not available\n");
6572 return FALSE;
6575 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6576 if (!pLoadLibraryShim)
6578 WARN("LoadLibraryShim not available\n");
6579 FreeLibrary(hmscoree);
6580 return FALSE;
6583 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6584 if (FAILED(hr))
6586 WARN("fusion.dll not available\n");
6587 FreeLibrary(hmscoree);
6588 return FALSE;
6591 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6593 FreeLibrary(hmscoree);
6594 return TRUE;
6597 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6598 LPWSTR path)
6600 IAssemblyCache *cache;
6601 HRESULT hr;
6602 UINT r = ERROR_FUNCTION_FAILED;
6604 TRACE("installing assembly: %s\n", debugstr_w(path));
6606 if (assembly->feature)
6607 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6609 if (assembly->manifest)
6610 FIXME("Manifest unhandled\n");
6612 if (assembly->application)
6614 FIXME("Assembly should be privately installed\n");
6615 return ERROR_SUCCESS;
6618 if (assembly->attributes == msidbAssemblyAttributesWin32)
6620 FIXME("Win32 assemblies not handled\n");
6621 return ERROR_SUCCESS;
6624 hr = pCreateAssemblyCache(&cache, 0);
6625 if (FAILED(hr))
6626 goto done;
6628 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6629 if (FAILED(hr))
6630 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6632 r = ERROR_SUCCESS;
6634 done:
6635 IAssemblyCache_Release(cache);
6636 return r;
6639 typedef struct tagASSEMBLY_LIST
6641 MSIPACKAGE *package;
6642 IAssemblyCache *cache;
6643 struct list *assemblies;
6644 } ASSEMBLY_LIST;
6646 typedef struct tagASSEMBLY_NAME
6648 LPWSTR name;
6649 LPWSTR version;
6650 LPWSTR culture;
6651 LPWSTR pubkeytoken;
6652 } ASSEMBLY_NAME;
6654 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6656 ASSEMBLY_NAME *asmname = param;
6657 LPCWSTR name = MSI_RecordGetString(rec, 2);
6658 LPWSTR val = msi_dup_record_field(rec, 3);
6660 static const WCHAR Name[] = {'N','a','m','e',0};
6661 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6662 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6663 static const WCHAR PublicKeyToken[] = {
6664 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6666 if (!strcmpiW(name, Name))
6667 asmname->name = val;
6668 else if (!strcmpiW(name, Version))
6669 asmname->version = val;
6670 else if (!strcmpiW(name, Culture))
6671 asmname->culture = val;
6672 else if (!strcmpiW(name, PublicKeyToken))
6673 asmname->pubkeytoken = val;
6674 else
6675 msi_free(val);
6677 return ERROR_SUCCESS;
6680 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6682 if (!*str)
6684 *size = lstrlenW(append) + 1;
6685 *str = msi_alloc((*size) * sizeof(WCHAR));
6686 lstrcpyW(*str, append);
6687 return;
6690 (*size) += lstrlenW(append);
6691 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6692 lstrcatW(*str, append);
6695 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
6696 MSICOMPONENT *comp)
6698 ASSEMBLY_INFO asminfo;
6699 ASSEMBLY_NAME name;
6700 MSIQUERY *view;
6701 LPWSTR disp;
6702 DWORD size;
6703 BOOL found;
6704 UINT r;
6706 static const WCHAR separator[] = {',',' ',0};
6707 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6708 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6709 static const WCHAR PublicKeyToken[] = {
6710 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6711 static const WCHAR query[] = {
6712 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6713 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6714 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6715 '=','\'','%','s','\'',0};
6717 disp = NULL;
6718 found = FALSE;
6719 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
6720 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
6722 r = MSI_OpenQuery(db, &view, query, comp->Component);
6723 if (r != ERROR_SUCCESS)
6724 return ERROR_SUCCESS;
6726 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
6727 msiobj_release(&view->hdr);
6729 if (!name.name)
6731 ERR("No assembly name specified!\n");
6732 goto done;
6735 append_str(&disp, &size, name.name);
6737 if (name.version)
6739 append_str(&disp, &size, separator);
6740 append_str(&disp, &size, Version);
6741 append_str(&disp, &size, name.version);
6744 if (name.culture)
6746 append_str(&disp, &size, separator);
6747 append_str(&disp, &size, Culture);
6748 append_str(&disp, &size, name.culture);
6751 if (name.pubkeytoken)
6753 append_str(&disp, &size, separator);
6754 append_str(&disp, &size, PublicKeyToken);
6755 append_str(&disp, &size, name.pubkeytoken);
6758 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6759 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
6760 disp, &asminfo);
6761 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6763 done:
6764 msi_free(disp);
6765 msi_free(name.name);
6766 msi_free(name.version);
6767 msi_free(name.culture);
6768 msi_free(name.pubkeytoken);
6770 return found;
6773 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6775 ASSEMBLY_LIST *list = param;
6776 MSIASSEMBLY *assembly;
6777 LPCWSTR component;
6779 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6780 if (!assembly)
6781 return ERROR_OUTOFMEMORY;
6783 component = MSI_RecordGetString(rec, 1);
6784 assembly->component = get_loaded_component(list->package, component);
6785 if (!assembly->component)
6786 return ERROR_SUCCESS;
6788 if (assembly->component->ActionRequest != INSTALLSTATE_LOCAL &&
6789 assembly->component->ActionRequest != INSTALLSTATE_SOURCE)
6791 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6792 assembly->component->Action = assembly->component->Installed;
6793 return ERROR_SUCCESS;
6795 assembly->component->Action = assembly->component->ActionRequest;
6797 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6798 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6800 if (!assembly->file)
6802 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6803 return ERROR_FUNCTION_FAILED;
6806 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6807 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6808 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6810 if (assembly->application)
6812 WCHAR version[24];
6813 DWORD size = sizeof(version)/sizeof(WCHAR);
6815 /* FIXME: we should probably check the manifest file here */
6817 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6818 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6820 assembly->installed = TRUE;
6823 else
6824 assembly->installed = check_assembly_installed(list->package->db,
6825 list->cache,
6826 assembly->component);
6828 list_add_head(list->assemblies, &assembly->entry);
6829 return ERROR_SUCCESS;
6832 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6834 IAssemblyCache *cache = NULL;
6835 ASSEMBLY_LIST list;
6836 MSIQUERY *view;
6837 HRESULT hr;
6838 UINT r;
6840 static const WCHAR query[] =
6841 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6842 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6844 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6845 if (r != ERROR_SUCCESS)
6846 return ERROR_SUCCESS;
6848 hr = pCreateAssemblyCache(&cache, 0);
6849 if (FAILED(hr))
6850 return ERROR_FUNCTION_FAILED;
6852 list.package = package;
6853 list.cache = cache;
6854 list.assemblies = assemblies;
6856 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6857 msiobj_release(&view->hdr);
6859 IAssemblyCache_Release(cache);
6861 return r;
6864 static void free_assemblies(struct list *assemblies)
6866 struct list *item, *cursor;
6868 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6870 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6872 list_remove(&assembly->entry);
6873 msi_free(assembly->application);
6874 msi_free(assembly->manifest);
6875 msi_free(assembly);
6879 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6881 MSIASSEMBLY *assembly;
6883 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6885 if (!lstrcmpW(assembly->file->File, file))
6887 *out = assembly;
6888 return TRUE;
6892 return FALSE;
6895 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6896 LPWSTR *path, DWORD *attrs, PVOID user)
6898 MSIASSEMBLY *assembly;
6899 WCHAR temppath[MAX_PATH];
6900 struct list *assemblies = user;
6901 UINT r;
6903 if (!find_assembly(assemblies, file, &assembly))
6904 return FALSE;
6906 GetTempPathW(MAX_PATH, temppath);
6907 PathAddBackslashW(temppath);
6908 lstrcatW(temppath, assembly->file->FileName);
6910 if (action == MSICABEXTRACT_BEGINEXTRACT)
6912 if (assembly->installed)
6913 return FALSE;
6915 *path = strdupW(temppath);
6916 *attrs = assembly->file->Attributes;
6918 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6920 assembly->installed = TRUE;
6922 r = install_assembly(package, assembly, temppath);
6923 if (r != ERROR_SUCCESS)
6924 ERR("Failed to install assembly\n");
6927 return TRUE;
6930 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6932 UINT r;
6933 struct list assemblies = LIST_INIT(assemblies);
6934 MSIASSEMBLY *assembly;
6935 MSIMEDIAINFO *mi;
6937 if (!init_functionpointers() || !pCreateAssemblyCache)
6938 return ERROR_FUNCTION_FAILED;
6940 r = load_assemblies(package, &assemblies);
6941 if (r != ERROR_SUCCESS)
6942 goto done;
6944 if (list_empty(&assemblies))
6945 goto done;
6947 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6948 if (!mi)
6950 r = ERROR_OUTOFMEMORY;
6951 goto done;
6954 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6956 if (assembly->installed && !mi->is_continuous)
6957 continue;
6959 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6960 (assembly->file->IsCompressed && !mi->is_extracted))
6962 MSICABDATA data;
6964 r = ready_media(package, assembly->file, mi);
6965 if (r != ERROR_SUCCESS)
6967 ERR("Failed to ready media\n");
6968 break;
6971 data.mi = mi;
6972 data.package = package;
6973 data.cb = installassembly_cb;
6974 data.user = &assemblies;
6976 if (assembly->file->IsCompressed &&
6977 !msi_cabextract(package, mi, &data))
6979 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6980 r = ERROR_FUNCTION_FAILED;
6981 break;
6985 if (!assembly->file->IsCompressed)
6987 LPWSTR source = resolve_file_source(package, assembly->file);
6989 r = install_assembly(package, assembly, source);
6990 if (r != ERROR_SUCCESS)
6991 ERR("Failed to install assembly\n");
6993 msi_free(source);
6996 /* FIXME: write Installer assembly reg values */
6999 done:
7000 free_assemblies(&assemblies);
7001 return r;
7004 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7006 LPWSTR key, template, id;
7007 UINT r = ERROR_SUCCESS;
7009 id = msi_dup_property( package, szProductID );
7010 if (id)
7012 msi_free( id );
7013 return ERROR_SUCCESS;
7015 template = msi_dup_property( package, szPIDTemplate );
7016 key = msi_dup_property( package, szPIDKEY );
7018 if (key && template)
7020 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7021 r = MSI_SetPropertyW( package, szProductID, key );
7023 msi_free( template );
7024 msi_free( key );
7025 return r;
7028 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7030 TRACE("\n");
7031 package->need_reboot = 1;
7032 return ERROR_SUCCESS;
7035 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7037 static const WCHAR szAvailableFreeReg[] =
7038 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7040 TRACE("%p %d kilobytes\n", package, msi_get_property_int( package, szAvailableFreeReg, 0 ));
7041 return ERROR_SUCCESS;
7044 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7046 FIXME("%p\n", package);
7047 return ERROR_SUCCESS;
7050 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7052 FIXME("%p\n", package);
7053 return ERROR_SUCCESS;
7056 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7057 LPCSTR action, LPCWSTR table )
7059 static const WCHAR query[] = {
7060 'S','E','L','E','C','T',' ','*',' ',
7061 'F','R','O','M',' ','`','%','s','`',0 };
7062 MSIQUERY *view = NULL;
7063 DWORD count = 0;
7064 UINT r;
7066 r = MSI_OpenQuery( package->db, &view, query, table );
7067 if (r == ERROR_SUCCESS)
7069 r = MSI_IterateRecords(view, &count, NULL, package);
7070 msiobj_release(&view->hdr);
7073 if (count)
7074 FIXME("%s -> %u ignored %s table values\n",
7075 action, count, debugstr_w(table));
7077 return ERROR_SUCCESS;
7080 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7082 static const WCHAR table[] = { 'P','a','t','c','h',0 };
7083 return msi_unimplemented_action_stub( package, "PatchFiles", table );
7086 static UINT ACTION_BindImage( MSIPACKAGE *package )
7088 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7089 return msi_unimplemented_action_stub( package, "BindImage", table );
7092 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7094 static const WCHAR table[] = {
7095 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7096 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7099 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7101 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7102 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
7105 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
7107 static const WCHAR table[] = {
7108 'M','s','i','A','s','s','e','m','b','l','y',0 };
7109 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
7112 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7114 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7115 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7118 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7120 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7121 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7124 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7126 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7127 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7130 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7132 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7133 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7136 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7138 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7139 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
7142 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7144 static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
7145 return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
7148 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
7150 static const WCHAR table[] = { 'A','p','p','I','d',0 };
7151 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
7154 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
7156 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
7157 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
7160 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
7162 static const WCHAR table[] = { 'M','I','M','E',0 };
7163 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
7166 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
7168 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
7169 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
7172 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7174 static const struct
7176 const WCHAR *action;
7177 UINT (*handler)(MSIPACKAGE *);
7179 StandardActions[] =
7181 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7182 { szAppSearch, ACTION_AppSearch },
7183 { szBindImage, ACTION_BindImage },
7184 { szCCPSearch, ACTION_CCPSearch },
7185 { szCostFinalize, ACTION_CostFinalize },
7186 { szCostInitialize, ACTION_CostInitialize },
7187 { szCreateFolders, ACTION_CreateFolders },
7188 { szCreateShortcuts, ACTION_CreateShortcuts },
7189 { szDeleteServices, ACTION_DeleteServices },
7190 { szDisableRollback, ACTION_DisableRollback },
7191 { szDuplicateFiles, ACTION_DuplicateFiles },
7192 { szExecuteAction, ACTION_ExecuteAction },
7193 { szFileCost, ACTION_FileCost },
7194 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7195 { szForceReboot, ACTION_ForceReboot },
7196 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7197 { szInstallExecute, ACTION_InstallExecute },
7198 { szInstallExecuteAgain, ACTION_InstallExecute },
7199 { szInstallFiles, ACTION_InstallFiles},
7200 { szInstallFinalize, ACTION_InstallFinalize },
7201 { szInstallInitialize, ACTION_InstallInitialize },
7202 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7203 { szInstallValidate, ACTION_InstallValidate },
7204 { szIsolateComponents, ACTION_IsolateComponents },
7205 { szLaunchConditions, ACTION_LaunchConditions },
7206 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7207 { szMoveFiles, ACTION_MoveFiles },
7208 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7209 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7210 { szInstallODBC, ACTION_InstallODBC },
7211 { szInstallServices, ACTION_InstallServices },
7212 { szPatchFiles, ACTION_PatchFiles },
7213 { szProcessComponents, ACTION_ProcessComponents },
7214 { szPublishComponents, ACTION_PublishComponents },
7215 { szPublishFeatures, ACTION_PublishFeatures },
7216 { szPublishProduct, ACTION_PublishProduct },
7217 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7218 { szRegisterComPlus, ACTION_RegisterComPlus},
7219 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7220 { szRegisterFonts, ACTION_RegisterFonts },
7221 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7222 { szRegisterProduct, ACTION_RegisterProduct },
7223 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7224 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7225 { szRegisterUser, ACTION_RegisterUser },
7226 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7227 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7228 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7229 { szRemoveFiles, ACTION_RemoveFiles },
7230 { szRemoveFolders, ACTION_RemoveFolders },
7231 { szRemoveIniValues, ACTION_RemoveIniValues },
7232 { szRemoveODBC, ACTION_RemoveODBC },
7233 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7234 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7235 { szResolveSource, ACTION_ResolveSource },
7236 { szRMCCPSearch, ACTION_RMCCPSearch },
7237 { szScheduleReboot, ACTION_ScheduleReboot },
7238 { szSelfRegModules, ACTION_SelfRegModules },
7239 { szSelfUnregModules, ACTION_SelfUnregModules },
7240 { szSetODBCFolders, ACTION_SetODBCFolders },
7241 { szStartServices, ACTION_StartServices },
7242 { szStopServices, ACTION_StopServices },
7243 { szUnpublishComponents, ACTION_UnpublishComponents },
7244 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7245 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7246 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7247 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7248 { szUnregisterFonts, ACTION_UnregisterFonts },
7249 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7250 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7251 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7252 { szValidateProductID, ACTION_ValidateProductID },
7253 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7254 { szWriteIniValues, ACTION_WriteIniValues },
7255 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7256 { NULL, NULL },
7259 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
7260 UINT* rc, BOOL force )
7262 BOOL ret = FALSE;
7263 BOOL run = force;
7264 int i;
7266 if (!run && !package->script->CurrentlyScripting)
7267 run = TRUE;
7269 if (!run)
7271 if (strcmpW(action,szInstallFinalize) == 0 ||
7272 strcmpW(action,szInstallExecute) == 0 ||
7273 strcmpW(action,szInstallExecuteAgain) == 0)
7274 run = TRUE;
7277 i = 0;
7278 while (StandardActions[i].action != NULL)
7280 if (strcmpW(StandardActions[i].action, action)==0)
7282 if (!run)
7284 ui_actioninfo(package, action, TRUE, 0);
7285 *rc = schedule_action(package,INSTALL_SCRIPT,action);
7286 ui_actioninfo(package, action, FALSE, *rc);
7288 else
7290 ui_actionstart(package, action);
7291 if (StandardActions[i].handler)
7293 *rc = StandardActions[i].handler(package);
7295 else
7297 FIXME("unhandled standard action %s\n",debugstr_w(action));
7298 *rc = ERROR_SUCCESS;
7301 ret = TRUE;
7302 break;
7304 i++;
7306 return ret;
7309 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
7311 UINT rc = ERROR_SUCCESS;
7312 BOOL handled;
7314 TRACE("Performing action (%s)\n", debugstr_w(action));
7316 handled = ACTION_HandleStandardAction(package, action, &rc, force);
7318 if (!handled)
7319 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
7321 if (!handled)
7323 WARN("unhandled msi action %s\n", debugstr_w(action));
7324 rc = ERROR_FUNCTION_NOT_CALLED;
7327 return rc;
7330 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7332 UINT rc = ERROR_SUCCESS;
7333 BOOL handled = FALSE;
7335 TRACE("Performing action (%s)\n", debugstr_w(action));
7337 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
7339 if (!handled)
7340 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7342 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7343 handled = TRUE;
7345 if (!handled)
7347 WARN("unhandled msi action %s\n", debugstr_w(action));
7348 rc = ERROR_FUNCTION_NOT_CALLED;
7351 return rc;
7354 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7356 UINT rc = ERROR_SUCCESS;
7357 MSIRECORD *row;
7359 static const WCHAR ExecSeqQuery[] =
7360 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7361 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7362 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7363 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7364 static const WCHAR UISeqQuery[] =
7365 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7366 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7367 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7368 ' ', '=',' ','%','i',0};
7370 if (needs_ui_sequence(package))
7371 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7372 else
7373 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7375 if (row)
7377 LPCWSTR action, cond;
7379 TRACE("Running the actions\n");
7381 /* check conditions */
7382 cond = MSI_RecordGetString(row, 2);
7384 /* this is a hack to skip errors in the condition code */
7385 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7387 msiobj_release(&row->hdr);
7388 return ERROR_SUCCESS;
7391 action = MSI_RecordGetString(row, 1);
7392 if (!action)
7394 ERR("failed to fetch action\n");
7395 msiobj_release(&row->hdr);
7396 return ERROR_FUNCTION_FAILED;
7399 if (needs_ui_sequence(package))
7400 rc = ACTION_PerformUIAction(package, action, -1);
7401 else
7402 rc = ACTION_PerformAction(package, action, -1, FALSE);
7404 msiobj_release(&row->hdr);
7407 return rc;
7410 /****************************************************
7411 * TOP level entry points
7412 *****************************************************/
7414 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7415 LPCWSTR szCommandLine )
7417 UINT rc;
7418 BOOL ui_exists;
7420 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7421 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7423 MSI_SetPropertyW(package, szAction, szInstall);
7425 package->script->InWhatSequence = SEQUENCE_INSTALL;
7427 if (szPackagePath)
7429 LPWSTR p, dir;
7430 LPCWSTR file;
7432 dir = strdupW(szPackagePath);
7433 p = strrchrW(dir, '\\');
7434 if (p)
7436 *(++p) = 0;
7437 file = szPackagePath + (p - dir);
7439 else
7441 msi_free(dir);
7442 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7443 GetCurrentDirectoryW(MAX_PATH, dir);
7444 lstrcatW(dir, szBackSlash);
7445 file = szPackagePath;
7448 msi_free( package->PackagePath );
7449 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7450 if (!package->PackagePath)
7452 msi_free(dir);
7453 return ERROR_OUTOFMEMORY;
7456 lstrcpyW(package->PackagePath, dir);
7457 lstrcatW(package->PackagePath, file);
7458 msi_free(dir);
7460 msi_set_sourcedir_props(package, FALSE);
7463 msi_parse_command_line( package, szCommandLine, FALSE );
7465 msi_apply_transforms( package );
7466 msi_apply_patches( package );
7468 if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
7470 TRACE("setting reinstall property\n");
7471 MSI_SetPropertyW( package, szReinstall, szAll );
7474 /* properties may have been added by a transform */
7475 msi_clone_properties( package );
7476 msi_set_context( package );
7478 if (needs_ui_sequence( package))
7480 package->script->InWhatSequence |= SEQUENCE_UI;
7481 rc = ACTION_ProcessUISequence(package);
7482 ui_exists = ui_sequence_exists(package);
7483 if (rc == ERROR_SUCCESS || !ui_exists)
7485 package->script->InWhatSequence |= SEQUENCE_EXEC;
7486 rc = ACTION_ProcessExecSequence(package, ui_exists);
7489 else
7490 rc = ACTION_ProcessExecSequence(package, FALSE);
7492 package->script->CurrentlyScripting = FALSE;
7494 /* process the ending type action */
7495 if (rc == ERROR_SUCCESS)
7496 ACTION_PerformActionSequence(package, -1);
7497 else if (rc == ERROR_INSTALL_USEREXIT)
7498 ACTION_PerformActionSequence(package, -2);
7499 else if (rc == ERROR_INSTALL_SUSPEND)
7500 ACTION_PerformActionSequence(package, -4);
7501 else /* failed */
7502 ACTION_PerformActionSequence(package, -3);
7504 /* finish up running custom actions */
7505 ACTION_FinishCustomActions(package);
7507 if (rc == ERROR_SUCCESS && package->need_reboot)
7508 return ERROR_SUCCESS_REBOOT_REQUIRED;
7510 return rc;