comdlg32: Fix some alignment issues in the Dutch translation.
[wine/hramrach.git] / dlls / msi / action.c
blob6ed1417e652adbb54b8c16b5c698dd4405b1f0ec
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 szAllocateRegistrySpace[] =
96 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
97 static const WCHAR szBindImage[] =
98 {'B','i','n','d','I','m','a','g','e',0};
99 static const WCHAR szDeleteServices[] =
100 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
101 static const WCHAR szDisableRollback[] =
102 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
103 static const WCHAR szExecuteAction[] =
104 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
105 static const WCHAR szInstallAdminPackage[] =
106 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
107 static const WCHAR szInstallSFPCatalogFile[] =
108 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
109 static const WCHAR szIsolateComponents[] =
110 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
111 static const WCHAR szMigrateFeatureStates[] =
112 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
113 static const WCHAR szMsiPublishAssemblies[] =
114 {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
115 static const WCHAR szMsiUnpublishAssemblies[] =
116 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
117 static const WCHAR szInstallODBC[] =
118 {'I','n','s','t','a','l','l','O','D','B','C',0};
119 static const WCHAR szInstallServices[] =
120 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
121 static const WCHAR szPatchFiles[] =
122 {'P','a','t','c','h','F','i','l','e','s',0};
123 static const WCHAR szPublishComponents[] =
124 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
125 static const WCHAR szRegisterComPlus[] =
126 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
127 static const WCHAR szRegisterUser[] =
128 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
129 static const WCHAR szRemoveEnvironmentStrings[] =
130 {'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};
131 static const WCHAR szRemoveExistingProducts[] =
132 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
133 static const WCHAR szRemoveFolders[] =
134 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
135 static const WCHAR szRemoveIniValues[] =
136 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
137 static const WCHAR szRemoveODBC[] =
138 {'R','e','m','o','v','e','O','D','B','C',0};
139 static const WCHAR szRemoveRegistryValues[] =
140 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
141 static const WCHAR szRemoveShortcuts[] =
142 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
143 static const WCHAR szRMCCPSearch[] =
144 {'R','M','C','C','P','S','e','a','r','c','h',0};
145 static const WCHAR szScheduleReboot[] =
146 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
147 static const WCHAR szSelfUnregModules[] =
148 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
149 static const WCHAR szSetODBCFolders[] =
150 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
151 static const WCHAR szStartServices[] =
152 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
153 static const WCHAR szStopServices[] =
154 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
155 static const WCHAR szUnpublishComponents[] =
156 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
157 static const WCHAR szUnpublishFeatures[] =
158 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
159 static const WCHAR szUnregisterComPlus[] =
160 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
161 static const WCHAR szUnregisterTypeLibraries[] =
162 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
163 static const WCHAR szValidateProductID[] =
164 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
165 static const WCHAR szWriteEnvironmentStrings[] =
166 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
168 /********************************************************
169 * helper functions
170 ********************************************************/
172 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
174 static const WCHAR Query_t[] =
175 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
176 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
177 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
178 ' ','\'','%','s','\'',0};
179 MSIRECORD * row;
181 row = MSI_QueryGetRecord( package->db, Query_t, action );
182 if (!row)
183 return;
184 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
185 msiobj_release(&row->hdr);
188 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
189 UINT rc)
191 MSIRECORD * row;
192 static const WCHAR template_s[]=
193 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
194 '%','s', '.',0};
195 static const WCHAR template_e[]=
196 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
197 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
198 '%','i','.',0};
199 static const WCHAR format[] =
200 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
201 WCHAR message[1024];
202 WCHAR timet[0x100];
204 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
205 if (start)
206 sprintfW(message,template_s,timet,action);
207 else
208 sprintfW(message,template_e,timet,action,rc);
210 row = MSI_CreateRecord(1);
211 MSI_RecordSetStringW(row,1,message);
213 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
214 msiobj_release(&row->hdr);
217 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
218 BOOL preserve_case )
220 LPCWSTR ptr,ptr2;
221 BOOL quote;
222 DWORD len;
223 LPWSTR prop = NULL, val = NULL;
225 if (!szCommandLine)
226 return ERROR_SUCCESS;
228 ptr = szCommandLine;
230 while (*ptr)
232 if (*ptr==' ')
234 ptr++;
235 continue;
238 TRACE("Looking at %s\n",debugstr_w(ptr));
240 ptr2 = strchrW(ptr,'=');
241 if (!ptr2)
243 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
244 break;
247 quote = FALSE;
249 len = ptr2-ptr;
250 prop = msi_alloc((len+1)*sizeof(WCHAR));
251 memcpy(prop,ptr,len*sizeof(WCHAR));
252 prop[len]=0;
254 if (!preserve_case)
255 struprW(prop);
257 ptr2++;
259 len = 0;
260 ptr = ptr2;
261 while (*ptr && (quote || (!quote && *ptr!=' ')))
263 if (*ptr == '"')
264 quote = !quote;
265 ptr++;
266 len++;
269 if (*ptr2=='"')
271 ptr2++;
272 len -= 2;
274 val = msi_alloc((len+1)*sizeof(WCHAR));
275 memcpy(val,ptr2,len*sizeof(WCHAR));
276 val[len] = 0;
278 if (lstrlenW(prop) > 0)
280 UINT r = msi_set_property( package->db, prop, val );
282 TRACE("Found commandline property (%s) = (%s)\n",
283 debugstr_w(prop), debugstr_w(val));
285 if (r == ERROR_SUCCESS && !strcmpW( prop, cszSourceDir ))
286 msi_reset_folders( package, TRUE );
288 msi_free(val);
289 msi_free(prop);
292 return ERROR_SUCCESS;
296 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
298 LPCWSTR pc;
299 LPWSTR p, *ret = NULL;
300 UINT count = 0;
302 if (!str)
303 return ret;
305 /* count the number of substrings */
306 for ( pc = str, count = 0; pc; count++ )
308 pc = strchrW( pc, sep );
309 if (pc)
310 pc++;
313 /* allocate space for an array of substring pointers and the substrings */
314 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
315 (lstrlenW(str)+1) * sizeof(WCHAR) );
316 if (!ret)
317 return ret;
319 /* copy the string and set the pointers */
320 p = (LPWSTR) &ret[count+1];
321 lstrcpyW( p, str );
322 for( count = 0; (ret[count] = p); count++ )
324 p = strchrW( p, sep );
325 if (p)
326 *p++ = 0;
329 return ret;
332 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
334 static const WCHAR szSystemLanguageID[] =
335 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
337 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
338 UINT ret = ERROR_FUNCTION_FAILED;
340 prod_code = msi_dup_property( package->db, szProductCode );
341 patch_product = msi_get_suminfo_product( patch );
343 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
345 if ( strstrW( patch_product, prod_code ) )
347 MSISUMMARYINFO *si;
348 const WCHAR *p;
350 si = MSI_GetSummaryInformationW( patch, 0 );
351 if (!si)
353 ERR("no summary information!\n");
354 goto end;
357 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
358 if (!template)
360 ERR("no template property!\n");
361 msiobj_release( &si->hdr );
362 goto end;
365 if (!template[0])
367 ret = ERROR_SUCCESS;
368 msiobj_release( &si->hdr );
369 goto end;
372 langid = msi_dup_property( package->db, szSystemLanguageID );
373 if (!langid)
375 msiobj_release( &si->hdr );
376 goto end;
379 p = strchrW( template, ';' );
380 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
382 TRACE("applicable transform\n");
383 ret = ERROR_SUCCESS;
386 /* FIXME: check platform */
388 msiobj_release( &si->hdr );
391 end:
392 msi_free( patch_product );
393 msi_free( prod_code );
394 msi_free( template );
395 msi_free( langid );
397 return ret;
400 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
401 MSIDATABASE *patch_db, LPCWSTR name )
403 UINT ret = ERROR_FUNCTION_FAILED;
404 IStorage *stg = NULL;
405 HRESULT r;
407 TRACE("%p %s\n", package, debugstr_w(name) );
409 if (*name++ != ':')
411 ERR("expected a colon in %s\n", debugstr_w(name));
412 return ERROR_FUNCTION_FAILED;
415 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
416 if (SUCCEEDED(r))
418 ret = msi_check_transform_applicable( package, stg );
419 if (ret == ERROR_SUCCESS)
420 msi_table_apply_transform( package->db, stg );
421 else
422 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
423 IStorage_Release( stg );
425 else
426 ERR("failed to open substorage %s\n", debugstr_w(name));
428 return ERROR_SUCCESS;
431 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
433 LPWSTR guid_list, *guids, product_code;
434 UINT i, ret = ERROR_FUNCTION_FAILED;
436 product_code = msi_dup_property( package->db, szProductCode );
437 if (!product_code)
439 /* FIXME: the property ProductCode should be written into the DB somewhere */
440 ERR("no product code to check\n");
441 return ERROR_SUCCESS;
444 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
445 guids = msi_split_string( guid_list, ';' );
446 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
448 if (!lstrcmpW( guids[i], product_code ))
449 ret = ERROR_SUCCESS;
451 msi_free( guids );
452 msi_free( guid_list );
453 msi_free( product_code );
455 return ret;
458 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
460 MSIQUERY *view;
461 MSIRECORD *rec = NULL;
462 LPWSTR patch;
463 LPCWSTR prop;
464 UINT r;
466 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
467 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
468 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
469 '`','S','o','u','r','c','e','`',' ','I','S',' ',
470 'N','O','T',' ','N','U','L','L',0};
472 r = MSI_DatabaseOpenViewW(package->db, query, &view);
473 if (r != ERROR_SUCCESS)
474 return r;
476 r = MSI_ViewExecute(view, 0);
477 if (r != ERROR_SUCCESS)
478 goto done;
480 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
482 prop = MSI_RecordGetString(rec, 1);
483 patch = msi_dup_property(package->db, szPatch);
484 msi_set_property(package->db, prop, patch);
485 msi_free(patch);
488 done:
489 if (rec) msiobj_release(&rec->hdr);
490 msiobj_release(&view->hdr);
492 return r;
495 UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
497 MSIPATCHINFO *pi;
498 UINT r = ERROR_SUCCESS;
500 pi = msi_alloc_zero( sizeof(MSIPATCHINFO) );
501 if (!pi)
502 return ERROR_OUTOFMEMORY;
504 pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER );
505 if (!pi->patchcode)
507 msi_free( pi );
508 return ERROR_OUTOFMEMORY;
511 pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
512 if (!pi->transforms)
514 msi_free( pi->patchcode );
515 msi_free( pi );
516 return ERROR_OUTOFMEMORY;
519 *patch = pi;
520 return r;
523 UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
525 UINT i, r = ERROR_SUCCESS;
526 WCHAR **substorage;
528 /* apply substorage transforms */
529 substorage = msi_split_string( patch->transforms, ';' );
530 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
531 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
533 msi_free( substorage );
534 if (r != ERROR_SUCCESS)
535 return r;
537 msi_set_media_source_prop( package );
540 * There might be a CAB file in the patch package,
541 * so append it to the list of storages to search for streams.
543 append_storage_to_db( package->db, patch_db->storage );
545 list_add_tail( &package->patches, &patch->entry );
546 return ERROR_SUCCESS;
549 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
551 static const WCHAR dotmsp[] = {'.','m','s','p',0};
552 MSIDATABASE *patch_db = NULL;
553 WCHAR localfile[MAX_PATH];
554 MSISUMMARYINFO *si;
555 MSIPATCHINFO *patch = NULL;
556 UINT r = ERROR_SUCCESS;
558 TRACE("%p %s\n", package, debugstr_w( file ) );
560 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
561 if ( r != ERROR_SUCCESS )
563 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
564 return r;
567 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
568 if (!si)
570 msiobj_release( &patch_db->hdr );
571 return ERROR_FUNCTION_FAILED;
574 r = msi_check_patch_applicable( package, si );
575 if (r != ERROR_SUCCESS)
577 TRACE("patch not applicable\n");
578 r = ERROR_SUCCESS;
579 goto done;
582 r = msi_parse_patch_summary( si, &patch );
583 if ( r != ERROR_SUCCESS )
584 goto done;
586 r = msi_get_local_package_name( localfile, dotmsp );
587 if ( r != ERROR_SUCCESS )
588 goto done;
590 TRACE("copying to local package %s\n", debugstr_w(localfile));
592 if (!CopyFileW( file, localfile, FALSE ))
594 ERR("Unable to copy package (%s -> %s) (error %u)\n",
595 debugstr_w(file), debugstr_w(localfile), GetLastError());
596 r = GetLastError();
597 goto done;
599 patch->localfile = strdupW( localfile );
601 r = msi_apply_patch_db( package, patch_db, patch );
602 if ( r != ERROR_SUCCESS )
603 WARN("patch failed to apply %u\n", r);
605 done:
606 msiobj_release( &si->hdr );
607 msiobj_release( &patch_db->hdr );
608 if (patch && r != ERROR_SUCCESS)
610 if (patch->localfile)
611 DeleteFileW( patch->localfile );
613 msi_free( patch->patchcode );
614 msi_free( patch->transforms );
615 msi_free( patch->localfile );
616 msi_free( patch );
618 return r;
621 /* get the PATCH property, and apply all the patches it specifies */
622 static UINT msi_apply_patches( MSIPACKAGE *package )
624 LPWSTR patch_list, *patches;
625 UINT i, r = ERROR_SUCCESS;
627 patch_list = msi_dup_property( package->db, szPatch );
629 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
631 patches = msi_split_string( patch_list, ';' );
632 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
633 r = msi_apply_patch_package( package, patches[i] );
635 msi_free( patches );
636 msi_free( patch_list );
638 return r;
641 static UINT msi_apply_transforms( MSIPACKAGE *package )
643 static const WCHAR szTransforms[] = {
644 'T','R','A','N','S','F','O','R','M','S',0 };
645 LPWSTR xform_list, *xforms;
646 UINT i, r = ERROR_SUCCESS;
648 xform_list = msi_dup_property( package->db, szTransforms );
649 xforms = msi_split_string( xform_list, ';' );
651 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
653 if (xforms[i][0] == ':')
654 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
655 else
656 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
659 msi_free( xforms );
660 msi_free( xform_list );
662 return r;
665 static BOOL ui_sequence_exists( MSIPACKAGE *package )
667 MSIQUERY *view;
668 UINT rc;
670 static const WCHAR ExecSeqQuery [] =
671 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
672 '`','I','n','s','t','a','l','l',
673 'U','I','S','e','q','u','e','n','c','e','`',
674 ' ','W','H','E','R','E',' ',
675 '`','S','e','q','u','e','n','c','e','`',' ',
676 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
677 '`','S','e','q','u','e','n','c','e','`',0};
679 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
680 if (rc == ERROR_SUCCESS)
682 msiobj_release(&view->hdr);
683 return TRUE;
686 return FALSE;
689 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
691 LPWSTR p, db;
692 LPWSTR source, check;
693 DWORD len;
695 static const WCHAR szOriginalDatabase[] =
696 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
698 db = msi_dup_property( package->db, szOriginalDatabase );
699 if (!db)
700 return ERROR_OUTOFMEMORY;
702 p = strrchrW( db, '\\' );
703 if (!p)
705 p = strrchrW( db, '/' );
706 if (!p)
708 msi_free(db);
709 return ERROR_SUCCESS;
713 len = p - db + 2;
714 source = msi_alloc( len * sizeof(WCHAR) );
715 lstrcpynW( source, db, len );
717 check = msi_dup_property( package->db, cszSourceDir );
718 if (!check || replace)
720 UINT r = msi_set_property( package->db, cszSourceDir, source );
721 if (r == ERROR_SUCCESS)
722 msi_reset_folders( package, TRUE );
724 msi_free( check );
726 check = msi_dup_property( package->db, cszSOURCEDIR );
727 if (!check || replace)
728 msi_set_property( package->db, cszSOURCEDIR, source );
730 msi_free( check );
731 msi_free( source );
732 msi_free( db );
734 return ERROR_SUCCESS;
737 static BOOL needs_ui_sequence(MSIPACKAGE *package)
739 INT level = msi_get_property_int(package->db, szUILevel, 0);
740 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
743 UINT msi_set_context(MSIPACKAGE *package)
745 int num;
747 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
749 num = msi_get_property_int(package->db, szAllUsers, 0);
750 if (num == 1 || num == 2)
751 package->Context = MSIINSTALLCONTEXT_MACHINE;
753 return ERROR_SUCCESS;
756 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
758 UINT rc;
759 LPCWSTR cond, action;
760 MSIPACKAGE *package = param;
762 action = MSI_RecordGetString(row,1);
763 if (!action)
765 ERR("Error is retrieving action name\n");
766 return ERROR_FUNCTION_FAILED;
769 /* check conditions */
770 cond = MSI_RecordGetString(row,2);
772 /* this is a hack to skip errors in the condition code */
773 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
775 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
776 return ERROR_SUCCESS;
779 if (needs_ui_sequence(package))
780 rc = ACTION_PerformUIAction(package, action, -1);
781 else
782 rc = ACTION_PerformAction(package, action, -1, FALSE);
784 msi_dialog_check_messages( NULL );
786 if (package->CurrentInstallState != ERROR_SUCCESS)
787 rc = package->CurrentInstallState;
789 if (rc == ERROR_FUNCTION_NOT_CALLED)
790 rc = ERROR_SUCCESS;
792 if (rc != ERROR_SUCCESS)
793 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
795 return rc;
798 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
800 MSIQUERY * view;
801 UINT r;
802 static const WCHAR query[] =
803 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
804 '`','%','s','`',
805 ' ','W','H','E','R','E',' ',
806 '`','S','e','q','u','e','n','c','e','`',' ',
807 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
808 '`','S','e','q','u','e','n','c','e','`',0};
810 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
812 r = MSI_OpenQuery( package->db, &view, query, szTable );
813 if (r == ERROR_SUCCESS)
815 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
816 msiobj_release(&view->hdr);
819 return r;
822 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
824 MSIQUERY * view;
825 UINT rc;
826 static const WCHAR ExecSeqQuery[] =
827 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
828 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
829 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
830 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
831 'O','R','D','E','R',' ', 'B','Y',' ',
832 '`','S','e','q','u','e','n','c','e','`',0 };
833 static const WCHAR IVQuery[] =
834 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
835 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
836 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
837 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
838 ' ','\'', 'I','n','s','t','a','l','l',
839 'V','a','l','i','d','a','t','e','\'', 0};
840 INT seq = 0;
842 if (package->script->ExecuteSequenceRun)
844 TRACE("Execute Sequence already Run\n");
845 return ERROR_SUCCESS;
848 package->script->ExecuteSequenceRun = TRUE;
850 /* get the sequence number */
851 if (UIran)
853 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
854 if( !row )
855 return ERROR_FUNCTION_FAILED;
856 seq = MSI_RecordGetInteger(row,1);
857 msiobj_release(&row->hdr);
860 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
861 if (rc == ERROR_SUCCESS)
863 TRACE("Running the actions\n");
865 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
866 msiobj_release(&view->hdr);
869 return rc;
872 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
874 MSIQUERY * view;
875 UINT rc;
876 static const WCHAR ExecSeqQuery [] =
877 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
878 '`','I','n','s','t','a','l','l',
879 'U','I','S','e','q','u','e','n','c','e','`',
880 ' ','W','H','E','R','E',' ',
881 '`','S','e','q','u','e','n','c','e','`',' ',
882 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
883 '`','S','e','q','u','e','n','c','e','`',0};
885 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
886 if (rc == ERROR_SUCCESS)
888 TRACE("Running the actions\n");
890 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
891 msiobj_release(&view->hdr);
894 return rc;
897 /********************************************************
898 * ACTION helper functions and functions that perform the actions
899 *******************************************************/
900 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
901 UINT* rc, UINT script, BOOL force )
903 BOOL ret=FALSE;
904 UINT arc;
906 arc = ACTION_CustomAction(package, action, script, force);
908 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
910 *rc = arc;
911 ret = TRUE;
913 return ret;
917 * Actual Action Handlers
920 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
922 MSIPACKAGE *package = param;
923 LPCWSTR dir, component;
924 LPWSTR full_path;
925 MSIRECORD *uirow;
926 MSIFOLDER *folder;
927 MSICOMPONENT *comp;
929 component = MSI_RecordGetString(row, 2);
930 comp = get_loaded_component(package, component);
931 if (!comp)
932 return ERROR_SUCCESS;
934 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
936 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
937 comp->Action = comp->Installed;
938 return ERROR_SUCCESS;
940 comp->Action = INSTALLSTATE_LOCAL;
942 dir = MSI_RecordGetString(row,1);
943 if (!dir)
945 ERR("Unable to get folder id\n");
946 return ERROR_SUCCESS;
949 uirow = MSI_CreateRecord(1);
950 MSI_RecordSetStringW(uirow, 1, dir);
951 ui_actiondata(package, szCreateFolders, uirow);
952 msiobj_release(&uirow->hdr);
954 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
955 if (!full_path)
957 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
958 return ERROR_SUCCESS;
961 TRACE("Folder is %s\n",debugstr_w(full_path));
963 if (folder->State == 0)
964 create_full_pathW(full_path);
966 folder->State = 3;
968 msi_free(full_path);
969 return ERROR_SUCCESS;
972 /* FIXME: probably should merge this with the above function */
973 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
975 UINT rc = ERROR_SUCCESS;
976 MSIFOLDER *folder;
977 LPWSTR install_path;
979 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
980 if (!install_path)
981 return ERROR_FUNCTION_FAILED;
983 /* create the path */
984 if (folder->State == 0)
986 create_full_pathW(install_path);
987 folder->State = 2;
989 msi_free(install_path);
991 return rc;
994 UINT msi_create_component_directories( MSIPACKAGE *package )
996 MSICOMPONENT *comp;
998 /* create all the folders required by the components are going to install */
999 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1001 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
1002 continue;
1003 msi_create_directory( package, comp->Directory );
1006 return ERROR_SUCCESS;
1009 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1011 static const WCHAR ExecSeqQuery[] =
1012 {'S','E','L','E','C','T',' ',
1013 '`','D','i','r','e','c','t','o','r','y','_','`',
1014 ' ','F','R','O','M',' ',
1015 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1016 UINT rc;
1017 MSIQUERY *view;
1019 /* create all the empty folders specified in the CreateFolder table */
1020 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1021 if (rc != ERROR_SUCCESS)
1022 return ERROR_SUCCESS;
1024 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1025 msiobj_release(&view->hdr);
1027 return rc;
1030 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1032 MSIPACKAGE *package = param;
1033 LPCWSTR dir, component;
1034 LPWSTR full_path;
1035 MSIRECORD *uirow;
1036 MSIFOLDER *folder;
1037 MSICOMPONENT *comp;
1039 component = MSI_RecordGetString(row, 2);
1040 comp = get_loaded_component(package, component);
1041 if (!comp)
1042 return ERROR_SUCCESS;
1044 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1046 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1047 comp->Action = comp->Installed;
1048 return ERROR_SUCCESS;
1050 comp->Action = INSTALLSTATE_ABSENT;
1052 dir = MSI_RecordGetString( row, 1 );
1053 if (!dir)
1055 ERR("Unable to get folder id\n");
1056 return ERROR_SUCCESS;
1059 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1060 if (!full_path)
1062 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1063 return ERROR_SUCCESS;
1066 TRACE("folder is %s\n", debugstr_w(full_path));
1068 uirow = MSI_CreateRecord( 1 );
1069 MSI_RecordSetStringW( uirow, 1, full_path );
1070 ui_actiondata( package, szRemoveFolders, uirow );
1071 msiobj_release( &uirow->hdr );
1073 RemoveDirectoryW( full_path );
1074 folder->State = 0;
1076 msi_free( full_path );
1077 return ERROR_SUCCESS;
1080 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1082 static const WCHAR query[] =
1083 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1084 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1086 MSIQUERY *view;
1087 UINT rc;
1089 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1090 if (rc != ERROR_SUCCESS)
1091 return ERROR_SUCCESS;
1093 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1094 msiobj_release( &view->hdr );
1096 return rc;
1099 static UINT load_component( MSIRECORD *row, LPVOID param )
1101 MSIPACKAGE *package = param;
1102 MSICOMPONENT *comp;
1104 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1105 if (!comp)
1106 return ERROR_FUNCTION_FAILED;
1108 list_add_tail( &package->components, &comp->entry );
1110 /* fill in the data */
1111 comp->Component = msi_dup_record_field( row, 1 );
1113 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1115 comp->ComponentId = msi_dup_record_field( row, 2 );
1116 comp->Directory = msi_dup_record_field( row, 3 );
1117 comp->Attributes = MSI_RecordGetInteger(row,4);
1118 comp->Condition = msi_dup_record_field( row, 5 );
1119 comp->KeyPath = msi_dup_record_field( row, 6 );
1121 comp->Installed = INSTALLSTATE_UNKNOWN;
1122 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1124 return ERROR_SUCCESS;
1127 static UINT load_all_components( MSIPACKAGE *package )
1129 static const WCHAR query[] = {
1130 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1131 '`','C','o','m','p','o','n','e','n','t','`',0 };
1132 MSIQUERY *view;
1133 UINT r;
1135 if (!list_empty(&package->components))
1136 return ERROR_SUCCESS;
1138 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1139 if (r != ERROR_SUCCESS)
1140 return r;
1142 r = MSI_IterateRecords(view, NULL, load_component, package);
1143 msiobj_release(&view->hdr);
1144 return r;
1147 typedef struct {
1148 MSIPACKAGE *package;
1149 MSIFEATURE *feature;
1150 } _ilfs;
1152 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1154 ComponentList *cl;
1156 cl = msi_alloc( sizeof (*cl) );
1157 if ( !cl )
1158 return ERROR_NOT_ENOUGH_MEMORY;
1159 cl->component = comp;
1160 list_add_tail( &feature->Components, &cl->entry );
1162 return ERROR_SUCCESS;
1165 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1167 FeatureList *fl;
1169 fl = msi_alloc( sizeof(*fl) );
1170 if ( !fl )
1171 return ERROR_NOT_ENOUGH_MEMORY;
1172 fl->feature = child;
1173 list_add_tail( &parent->Children, &fl->entry );
1175 return ERROR_SUCCESS;
1178 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1180 _ilfs* ilfs = param;
1181 LPCWSTR component;
1182 MSICOMPONENT *comp;
1184 component = MSI_RecordGetString(row,1);
1186 /* check to see if the component is already loaded */
1187 comp = get_loaded_component( ilfs->package, component );
1188 if (!comp)
1190 ERR("unknown component %s\n", debugstr_w(component));
1191 return ERROR_FUNCTION_FAILED;
1194 add_feature_component( ilfs->feature, comp );
1195 comp->Enabled = TRUE;
1197 return ERROR_SUCCESS;
1200 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1202 MSIFEATURE *feature;
1204 if ( !name )
1205 return NULL;
1207 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1209 if ( !lstrcmpW( feature->Feature, name ) )
1210 return feature;
1213 return NULL;
1216 static UINT load_feature(MSIRECORD * row, LPVOID param)
1218 MSIPACKAGE* package = param;
1219 MSIFEATURE* feature;
1220 static const WCHAR Query1[] =
1221 {'S','E','L','E','C','T',' ',
1222 '`','C','o','m','p','o','n','e','n','t','_','`',
1223 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1224 'C','o','m','p','o','n','e','n','t','s','`',' ',
1225 'W','H','E','R','E',' ',
1226 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1227 MSIQUERY * view;
1228 UINT rc;
1229 _ilfs ilfs;
1231 /* fill in the data */
1233 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1234 if (!feature)
1235 return ERROR_NOT_ENOUGH_MEMORY;
1237 list_init( &feature->Children );
1238 list_init( &feature->Components );
1240 feature->Feature = msi_dup_record_field( row, 1 );
1242 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1244 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1245 feature->Title = msi_dup_record_field( row, 3 );
1246 feature->Description = msi_dup_record_field( row, 4 );
1248 if (!MSI_RecordIsNull(row,5))
1249 feature->Display = MSI_RecordGetInteger(row,5);
1251 feature->Level= MSI_RecordGetInteger(row,6);
1252 feature->Directory = msi_dup_record_field( row, 7 );
1253 feature->Attributes = MSI_RecordGetInteger(row,8);
1255 feature->Installed = INSTALLSTATE_UNKNOWN;
1256 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1258 list_add_tail( &package->features, &feature->entry );
1260 /* load feature components */
1262 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1263 if (rc != ERROR_SUCCESS)
1264 return ERROR_SUCCESS;
1266 ilfs.package = package;
1267 ilfs.feature = feature;
1269 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1270 msiobj_release(&view->hdr);
1272 return ERROR_SUCCESS;
1275 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1277 MSIPACKAGE* package = param;
1278 MSIFEATURE *parent, *child;
1280 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1281 if (!child)
1282 return ERROR_FUNCTION_FAILED;
1284 if (!child->Feature_Parent)
1285 return ERROR_SUCCESS;
1287 parent = find_feature_by_name( package, child->Feature_Parent );
1288 if (!parent)
1289 return ERROR_FUNCTION_FAILED;
1291 add_feature_child( parent, child );
1292 return ERROR_SUCCESS;
1295 static UINT load_all_features( MSIPACKAGE *package )
1297 static const WCHAR query[] = {
1298 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1299 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1300 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1301 MSIQUERY *view;
1302 UINT r;
1304 if (!list_empty(&package->features))
1305 return ERROR_SUCCESS;
1307 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1308 if (r != ERROR_SUCCESS)
1309 return r;
1311 r = MSI_IterateRecords( view, NULL, load_feature, package );
1312 if (r != ERROR_SUCCESS)
1313 return r;
1315 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1316 msiobj_release( &view->hdr );
1318 return r;
1321 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1323 if (!p)
1324 return p;
1325 p = strchrW(p, ch);
1326 if (!p)
1327 return p;
1328 *p = 0;
1329 return p+1;
1332 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1334 static const WCHAR query[] = {
1335 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1336 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1337 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1338 MSIQUERY *view = NULL;
1339 MSIRECORD *row = NULL;
1340 UINT r;
1342 TRACE("%s\n", debugstr_w(file->File));
1344 r = MSI_OpenQuery(package->db, &view, query, file->File);
1345 if (r != ERROR_SUCCESS)
1346 goto done;
1348 r = MSI_ViewExecute(view, NULL);
1349 if (r != ERROR_SUCCESS)
1350 goto done;
1352 r = MSI_ViewFetch(view, &row);
1353 if (r != ERROR_SUCCESS)
1354 goto done;
1356 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1357 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1358 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1359 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1360 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1362 done:
1363 if (view) msiobj_release(&view->hdr);
1364 if (row) msiobj_release(&row->hdr);
1365 return r;
1368 static UINT load_file(MSIRECORD *row, LPVOID param)
1370 MSIPACKAGE* package = param;
1371 LPCWSTR component;
1372 MSIFILE *file;
1374 /* fill in the data */
1376 file = msi_alloc_zero( sizeof (MSIFILE) );
1377 if (!file)
1378 return ERROR_NOT_ENOUGH_MEMORY;
1380 file->File = msi_dup_record_field( row, 1 );
1382 component = MSI_RecordGetString( row, 2 );
1383 file->Component = get_loaded_component( package, component );
1385 if (!file->Component)
1387 WARN("Component not found: %s\n", debugstr_w(component));
1388 msi_free(file->File);
1389 msi_free(file);
1390 return ERROR_SUCCESS;
1393 file->FileName = msi_dup_record_field( row, 3 );
1394 reduce_to_longfilename( file->FileName );
1396 file->ShortName = msi_dup_record_field( row, 3 );
1397 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1399 file->FileSize = MSI_RecordGetInteger( row, 4 );
1400 file->Version = msi_dup_record_field( row, 5 );
1401 file->Language = msi_dup_record_field( row, 6 );
1402 file->Attributes = MSI_RecordGetInteger( row, 7 );
1403 file->Sequence = MSI_RecordGetInteger( row, 8 );
1405 file->state = msifs_invalid;
1407 /* if the compressed bits are not set in the file attributes,
1408 * then read the information from the package word count property
1410 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1412 file->IsCompressed = FALSE;
1414 else if (file->Attributes &
1415 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1417 file->IsCompressed = TRUE;
1419 else if (file->Attributes & msidbFileAttributesNoncompressed)
1421 file->IsCompressed = FALSE;
1423 else
1425 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1428 load_file_hash(package, file);
1430 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1432 list_add_tail( &package->files, &file->entry );
1434 return ERROR_SUCCESS;
1437 static UINT load_all_files(MSIPACKAGE *package)
1439 MSIQUERY * view;
1440 UINT rc;
1441 static const WCHAR Query[] =
1442 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1443 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1444 '`','S','e','q','u','e','n','c','e','`', 0};
1446 if (!list_empty(&package->files))
1447 return ERROR_SUCCESS;
1449 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1450 if (rc != ERROR_SUCCESS)
1451 return ERROR_SUCCESS;
1453 rc = MSI_IterateRecords(view, NULL, load_file, package);
1454 msiobj_release(&view->hdr);
1456 return ERROR_SUCCESS;
1459 static UINT load_folder( MSIRECORD *row, LPVOID param )
1461 MSIPACKAGE *package = param;
1462 static WCHAR szEmpty[] = { 0 };
1463 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1464 MSIFOLDER *folder;
1466 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1467 if (!folder)
1468 return ERROR_NOT_ENOUGH_MEMORY;
1470 folder->Directory = msi_dup_record_field( row, 1 );
1472 TRACE("%s\n", debugstr_w(folder->Directory));
1474 p = msi_dup_record_field(row, 3);
1476 /* split src and target dir */
1477 tgt_short = p;
1478 src_short = folder_split_path( p, ':' );
1480 /* split the long and short paths */
1481 tgt_long = folder_split_path( tgt_short, '|' );
1482 src_long = folder_split_path( src_short, '|' );
1484 /* check for no-op dirs */
1485 if (!lstrcmpW(szDot, tgt_short))
1486 tgt_short = szEmpty;
1487 if (!lstrcmpW(szDot, src_short))
1488 src_short = szEmpty;
1490 if (!tgt_long)
1491 tgt_long = tgt_short;
1493 if (!src_short) {
1494 src_short = tgt_short;
1495 src_long = tgt_long;
1498 if (!src_long)
1499 src_long = src_short;
1501 /* FIXME: use the target short path too */
1502 folder->TargetDefault = strdupW(tgt_long);
1503 folder->SourceShortPath = strdupW(src_short);
1504 folder->SourceLongPath = strdupW(src_long);
1505 msi_free(p);
1507 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1508 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1509 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1511 folder->Parent = msi_dup_record_field( row, 2 );
1513 folder->Property = msi_dup_property( package->db, folder->Directory );
1515 list_add_tail( &package->folders, &folder->entry );
1517 TRACE("returning %p\n", folder);
1519 return ERROR_SUCCESS;
1522 static UINT load_all_folders( MSIPACKAGE *package )
1524 static const WCHAR query[] = {
1525 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1526 '`','D','i','r','e','c','t','o','r','y','`',0 };
1527 MSIQUERY *view;
1528 UINT r;
1530 if (!list_empty(&package->folders))
1531 return ERROR_SUCCESS;
1533 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1534 if (r != ERROR_SUCCESS)
1535 return r;
1537 r = MSI_IterateRecords(view, NULL, load_folder, package);
1538 msiobj_release(&view->hdr);
1539 return r;
1543 * I am not doing any of the costing functionality yet.
1544 * Mostly looking at doing the Component and Feature loading
1546 * The native MSI does A LOT of modification to tables here. Mostly adding
1547 * a lot of temporary columns to the Feature and Component tables.
1549 * note: Native msi also tracks the short filename. But I am only going to
1550 * track the long ones. Also looking at this directory table
1551 * it appears that the directory table does not get the parents
1552 * resolved base on property only based on their entries in the
1553 * directory table.
1555 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1557 static const WCHAR szCosting[] =
1558 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1560 msi_set_property( package->db, szCosting, szZero );
1561 msi_set_property( package->db, cszRootDrive, c_colon );
1563 load_all_folders( package );
1564 load_all_components( package );
1565 load_all_features( package );
1566 load_all_files( package );
1568 return ERROR_SUCCESS;
1571 static UINT execute_script(MSIPACKAGE *package, UINT script )
1573 UINT i;
1574 UINT rc = ERROR_SUCCESS;
1576 TRACE("Executing Script %i\n",script);
1578 if (!package->script)
1580 ERR("no script!\n");
1581 return ERROR_FUNCTION_FAILED;
1584 for (i = 0; i < package->script->ActionCount[script]; i++)
1586 LPWSTR action;
1587 action = package->script->Actions[script][i];
1588 ui_actionstart(package, action);
1589 TRACE("Executing Action (%s)\n",debugstr_w(action));
1590 rc = ACTION_PerformAction(package, action, script, TRUE);
1591 if (rc != ERROR_SUCCESS)
1592 break;
1594 msi_free_action_script(package, script);
1595 return rc;
1598 static UINT ACTION_FileCost(MSIPACKAGE *package)
1600 return ERROR_SUCCESS;
1603 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1605 MSICOMPONENT *comp;
1606 INSTALLSTATE state;
1607 UINT r;
1609 state = MsiQueryProductStateW(package->ProductCode);
1611 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1613 if (!comp->ComponentId)
1614 continue;
1616 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1617 comp->Installed = INSTALLSTATE_ABSENT;
1618 else
1620 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1621 package->Context, comp->ComponentId,
1622 &comp->Installed);
1623 if (r != ERROR_SUCCESS)
1624 comp->Installed = INSTALLSTATE_ABSENT;
1629 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1631 MSIFEATURE *feature;
1632 INSTALLSTATE state;
1634 state = MsiQueryProductStateW(package->ProductCode);
1636 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1638 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1639 feature->Installed = INSTALLSTATE_ABSENT;
1640 else
1642 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1643 feature->Feature);
1648 static BOOL process_state_property(MSIPACKAGE* package, int level,
1649 LPCWSTR property, INSTALLSTATE state)
1651 LPWSTR override;
1652 MSIFEATURE *feature;
1654 override = msi_dup_property( package->db, property );
1655 if (!override)
1656 return FALSE;
1658 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1660 if (lstrcmpW(property, szRemove) &&
1661 (feature->Level <= 0 || feature->Level > level))
1662 continue;
1664 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1666 if (strcmpiW(override, szAll)==0)
1667 msi_feature_set_state(package, feature, state);
1668 else
1670 LPWSTR ptr = override;
1671 LPWSTR ptr2 = strchrW(override,',');
1673 while (ptr)
1675 int len = ptr2 - ptr;
1677 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1678 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1680 msi_feature_set_state(package, feature, state);
1681 break;
1683 if (ptr2)
1685 ptr=ptr2+1;
1686 ptr2 = strchrW(ptr,',');
1688 else
1689 break;
1693 msi_free(override);
1695 return TRUE;
1698 static BOOL process_overrides( MSIPACKAGE *package, int level )
1700 static const WCHAR szAddLocal[] =
1701 {'A','D','D','L','O','C','A','L',0};
1702 static const WCHAR szAddSource[] =
1703 {'A','D','D','S','O','U','R','C','E',0};
1704 static const WCHAR szAdvertise[] =
1705 {'A','D','V','E','R','T','I','S','E',0};
1706 BOOL ret = FALSE;
1708 /* all these activation/deactivation things happen in order and things
1709 * later on the list override things earlier on the list.
1711 * 0 INSTALLLEVEL processing
1712 * 1 ADDLOCAL
1713 * 2 REMOVE
1714 * 3 ADDSOURCE
1715 * 4 ADDDEFAULT
1716 * 5 REINSTALL
1717 * 6 ADVERTISE
1718 * 7 COMPADDLOCAL
1719 * 8 COMPADDSOURCE
1720 * 9 FILEADDLOCAL
1721 * 10 FILEADDSOURCE
1722 * 11 FILEADDDEFAULT
1724 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1725 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1726 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1727 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1728 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1730 if (ret)
1731 msi_set_property( package->db, szPreselected, szOne );
1733 return ret;
1736 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1738 int level;
1739 static const WCHAR szlevel[] =
1740 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1741 MSICOMPONENT* component;
1742 MSIFEATURE *feature;
1744 TRACE("Checking Install Level\n");
1746 level = msi_get_property_int(package->db, szlevel, 1);
1748 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1750 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1752 BOOL feature_state = ((feature->Level > 0) &&
1753 (feature->Level <= level));
1755 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1757 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1758 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1759 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1760 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1761 else
1762 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1766 /* disable child features of unselected parent features */
1767 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1769 FeatureList *fl;
1771 if (feature->Level > 0 && feature->Level <= level)
1772 continue;
1774 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1775 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1780 * now we want to enable or disable components base on feature
1783 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1785 ComponentList *cl;
1787 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1788 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1790 if (!feature->Level)
1791 continue;
1793 /* features with components that have compressed files are made local */
1794 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1796 if (cl->component->Enabled &&
1797 cl->component->ForceLocalState &&
1798 feature->Action == INSTALLSTATE_SOURCE)
1800 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1801 break;
1805 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1807 component = cl->component;
1809 if (!component->Enabled)
1810 continue;
1812 switch (feature->Action)
1814 case INSTALLSTATE_ABSENT:
1815 component->anyAbsent = 1;
1816 break;
1817 case INSTALLSTATE_ADVERTISED:
1818 component->hasAdvertiseFeature = 1;
1819 break;
1820 case INSTALLSTATE_SOURCE:
1821 component->hasSourceFeature = 1;
1822 break;
1823 case INSTALLSTATE_LOCAL:
1824 component->hasLocalFeature = 1;
1825 break;
1826 case INSTALLSTATE_DEFAULT:
1827 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1828 component->hasAdvertiseFeature = 1;
1829 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1830 component->hasSourceFeature = 1;
1831 else
1832 component->hasLocalFeature = 1;
1833 break;
1834 default:
1835 break;
1840 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1842 /* if the component isn't enabled, leave it alone */
1843 if (!component->Enabled)
1844 continue;
1846 /* check if it's local or source */
1847 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1848 (component->hasLocalFeature || component->hasSourceFeature))
1850 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1851 !component->ForceLocalState)
1852 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1853 else
1854 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1855 continue;
1858 /* if any feature is local, the component must be local too */
1859 if (component->hasLocalFeature)
1861 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1862 continue;
1865 if (component->hasSourceFeature)
1867 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1868 continue;
1871 if (component->hasAdvertiseFeature)
1873 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1874 continue;
1877 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1878 if (component->anyAbsent)
1879 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1882 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1884 if (component->Action == INSTALLSTATE_DEFAULT)
1886 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1887 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1890 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1891 debugstr_w(component->Component), component->Installed, component->Action);
1895 return ERROR_SUCCESS;
1898 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1900 MSIPACKAGE *package = param;
1901 LPCWSTR name;
1902 LPWSTR path;
1903 MSIFOLDER *f;
1905 name = MSI_RecordGetString(row,1);
1907 f = get_loaded_folder(package, name);
1908 if (!f) return ERROR_SUCCESS;
1910 /* reset the ResolvedTarget */
1911 msi_free(f->ResolvedTarget);
1912 f->ResolvedTarget = NULL;
1914 /* This helper function now does ALL the work */
1915 TRACE("Dir %s ...\n",debugstr_w(name));
1916 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1917 TRACE("resolves to %s\n",debugstr_w(path));
1918 msi_free(path);
1920 return ERROR_SUCCESS;
1923 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1925 MSIPACKAGE *package = param;
1926 LPCWSTR name;
1927 MSIFEATURE *feature;
1929 name = MSI_RecordGetString( row, 1 );
1931 feature = get_loaded_feature( package, name );
1932 if (!feature)
1933 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1934 else
1936 LPCWSTR Condition;
1937 Condition = MSI_RecordGetString(row,3);
1939 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1941 int level = MSI_RecordGetInteger(row,2);
1942 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1943 feature->Level = level;
1946 return ERROR_SUCCESS;
1949 static LPWSTR get_disk_file_version( LPCWSTR filename )
1951 static const WCHAR name_fmt[] =
1952 {'%','u','.','%','u','.','%','u','.','%','u',0};
1953 static const WCHAR name[] = {'\\',0};
1954 VS_FIXEDFILEINFO *lpVer;
1955 WCHAR filever[0x100];
1956 LPVOID version;
1957 DWORD versize;
1958 DWORD handle;
1959 UINT sz;
1961 TRACE("%s\n", debugstr_w(filename));
1963 versize = GetFileVersionInfoSizeW( filename, &handle );
1964 if (!versize)
1965 return NULL;
1967 version = msi_alloc( versize );
1968 GetFileVersionInfoW( filename, 0, versize, version );
1970 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1972 msi_free( version );
1973 return NULL;
1976 sprintfW( filever, name_fmt,
1977 HIWORD(lpVer->dwFileVersionMS),
1978 LOWORD(lpVer->dwFileVersionMS),
1979 HIWORD(lpVer->dwFileVersionLS),
1980 LOWORD(lpVer->dwFileVersionLS));
1982 msi_free( version );
1984 return strdupW( filever );
1987 static DWORD get_disk_file_size( LPCWSTR filename )
1989 HANDLE file;
1990 DWORD size;
1992 TRACE("%s\n", debugstr_w(filename));
1994 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
1995 if (file == INVALID_HANDLE_VALUE)
1996 return INVALID_FILE_SIZE;
1998 size = GetFileSize( file, NULL );
1999 CloseHandle( file );
2000 return size;
2003 static BOOL hash_matches( MSIFILE *file )
2005 UINT r;
2006 MSIFILEHASHINFO hash;
2008 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2009 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2010 if (r != ERROR_SUCCESS)
2011 return FALSE;
2013 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2016 static UINT set_file_install_states( MSIPACKAGE *package )
2018 LPWSTR file_version;
2019 MSIFILE *file;
2021 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2023 MSICOMPONENT* comp = file->Component;
2024 DWORD file_size;
2025 LPWSTR p;
2027 if (!comp)
2028 continue;
2030 if (file->IsCompressed)
2031 comp->ForceLocalState = TRUE;
2033 /* calculate target */
2034 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2036 msi_free(file->TargetPath);
2038 TRACE("file %s is named %s\n",
2039 debugstr_w(file->File), debugstr_w(file->FileName));
2041 file->TargetPath = build_directory_name(2, p, file->FileName);
2043 msi_free(p);
2045 TRACE("file %s resolves to %s\n",
2046 debugstr_w(file->File), debugstr_w(file->TargetPath));
2048 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2050 file->state = msifs_missing;
2051 comp->Cost += file->FileSize;
2052 continue;
2054 if (file->Version && (file_version = get_disk_file_version( file->TargetPath )))
2056 TRACE("new %s old %s\n", debugstr_w(file->Version), debugstr_w(file_version));
2058 if (strcmpiW(file_version, file->Version) < 0)
2060 file->state = msifs_overwrite;
2061 comp->Cost += file->FileSize;
2063 else
2065 TRACE("Destination file version equal or greater, not overwriting\n");
2066 file->state = msifs_present;
2068 msi_free( file_version );
2069 continue;
2071 if ((file_size = get_disk_file_size( file->TargetPath )) != file->FileSize)
2073 file->state = msifs_overwrite;
2074 comp->Cost += file->FileSize - file_size;
2075 continue;
2077 if (file->hash.dwFileHashInfoSize && hash_matches( file ))
2079 TRACE("File hashes match, not overwriting\n");
2080 file->state = msifs_present;
2081 continue;
2083 file->state = msifs_overwrite;
2084 comp->Cost += file->FileSize - file_size;
2087 return ERROR_SUCCESS;
2091 * A lot is done in this function aside from just the costing.
2092 * The costing needs to be implemented at some point but for now I am going
2093 * to focus on the directory building
2096 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2098 static const WCHAR ExecSeqQuery[] =
2099 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2100 '`','D','i','r','e','c','t','o','r','y','`',0};
2101 static const WCHAR ConditionQuery[] =
2102 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2103 '`','C','o','n','d','i','t','i','o','n','`',0};
2104 static const WCHAR szCosting[] =
2105 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2106 static const WCHAR szlevel[] =
2107 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2108 static const WCHAR szOutOfDiskSpace[] =
2109 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2110 MSICOMPONENT *comp;
2111 UINT rc = ERROR_SUCCESS;
2112 MSIQUERY * view;
2113 LPWSTR level;
2115 TRACE("Building Directory properties\n");
2117 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2118 if (rc == ERROR_SUCCESS)
2120 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2121 package);
2122 msiobj_release(&view->hdr);
2125 /* read components states from the registry */
2126 ACTION_GetComponentInstallStates(package);
2127 ACTION_GetFeatureInstallStates(package);
2129 TRACE("Calculating file install states\n");
2130 set_file_install_states( package );
2132 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2134 TRACE("Evaluating feature conditions\n");
2136 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2137 if (rc == ERROR_SUCCESS)
2139 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2140 msiobj_release( &view->hdr );
2143 TRACE("Evaluating component conditions\n");
2145 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2147 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2149 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2150 comp->Enabled = FALSE;
2152 else
2153 comp->Enabled = TRUE;
2156 msi_set_property( package->db, szCosting, szOne );
2157 /* set default run level if not set */
2158 level = msi_dup_property( package->db, szlevel );
2159 if (!level)
2160 msi_set_property( package->db, szlevel, szOne );
2161 msi_free(level);
2163 /* FIXME: check volume disk space */
2164 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2166 return MSI_SetFeatureStates(package);
2169 /* OK this value is "interpreted" and then formatted based on the
2170 first few characters */
2171 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2172 DWORD *size)
2174 LPSTR data = NULL;
2176 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2178 if (value[1]=='x')
2180 LPWSTR ptr;
2181 CHAR byte[5];
2182 LPWSTR deformated = NULL;
2183 int count;
2185 deformat_string(package, &value[2], &deformated);
2187 /* binary value type */
2188 ptr = deformated;
2189 *type = REG_BINARY;
2190 if (strlenW(ptr)%2)
2191 *size = (strlenW(ptr)/2)+1;
2192 else
2193 *size = strlenW(ptr)/2;
2195 data = msi_alloc(*size);
2197 byte[0] = '0';
2198 byte[1] = 'x';
2199 byte[4] = 0;
2200 count = 0;
2201 /* if uneven pad with a zero in front */
2202 if (strlenW(ptr)%2)
2204 byte[2]= '0';
2205 byte[3]= *ptr;
2206 ptr++;
2207 data[count] = (BYTE)strtol(byte,NULL,0);
2208 count ++;
2209 TRACE("Uneven byte count\n");
2211 while (*ptr)
2213 byte[2]= *ptr;
2214 ptr++;
2215 byte[3]= *ptr;
2216 ptr++;
2217 data[count] = (BYTE)strtol(byte,NULL,0);
2218 count ++;
2220 msi_free(deformated);
2222 TRACE("Data %i bytes(%i)\n",*size,count);
2224 else
2226 LPWSTR deformated;
2227 LPWSTR p;
2228 DWORD d = 0;
2229 deformat_string(package, &value[1], &deformated);
2231 *type=REG_DWORD;
2232 *size = sizeof(DWORD);
2233 data = msi_alloc(*size);
2234 p = deformated;
2235 if (*p == '-')
2236 p++;
2237 while (*p)
2239 if ( (*p < '0') || (*p > '9') )
2240 break;
2241 d *= 10;
2242 d += (*p - '0');
2243 p++;
2245 if (deformated[0] == '-')
2246 d = -d;
2247 *(LPDWORD)data = d;
2248 TRACE("DWORD %i\n",*(LPDWORD)data);
2250 msi_free(deformated);
2253 else
2255 static const WCHAR szMulti[] = {'[','~',']',0};
2256 LPCWSTR ptr;
2257 *type=REG_SZ;
2259 if (value[0]=='#')
2261 if (value[1]=='%')
2263 ptr = &value[2];
2264 *type=REG_EXPAND_SZ;
2266 else
2267 ptr = &value[1];
2269 else
2270 ptr=value;
2272 if (strstrW(value,szMulti))
2273 *type = REG_MULTI_SZ;
2275 /* remove initial delimiter */
2276 if (!strncmpW(value, szMulti, 3))
2277 ptr = value + 3;
2279 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2281 /* add double NULL terminator */
2282 if (*type == REG_MULTI_SZ)
2284 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2285 data = msi_realloc_zero(data, *size);
2288 return data;
2291 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2293 const WCHAR *ret;
2295 switch (root)
2297 case -1:
2298 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2300 *root_key = HKEY_LOCAL_MACHINE;
2301 ret = szHLM;
2303 else
2305 *root_key = HKEY_CURRENT_USER;
2306 ret = szHCU;
2308 break;
2309 case 0:
2310 *root_key = HKEY_CLASSES_ROOT;
2311 ret = szHCR;
2312 break;
2313 case 1:
2314 *root_key = HKEY_CURRENT_USER;
2315 ret = szHCU;
2316 break;
2317 case 2:
2318 *root_key = HKEY_LOCAL_MACHINE;
2319 ret = szHLM;
2320 break;
2321 case 3:
2322 *root_key = HKEY_USERS;
2323 ret = szHU;
2324 break;
2325 default:
2326 ERR("Unknown root %i\n", root);
2327 return NULL;
2330 return ret;
2333 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2335 MSIPACKAGE *package = param;
2336 LPSTR value_data = NULL;
2337 HKEY root_key, hkey;
2338 DWORD type,size;
2339 LPWSTR deformated;
2340 LPCWSTR szRoot, component, name, key, value;
2341 MSICOMPONENT *comp;
2342 MSIRECORD * uirow;
2343 LPWSTR uikey;
2344 INT root;
2345 BOOL check_first = FALSE;
2346 UINT rc;
2348 ui_progress(package,2,0,0,0);
2350 value = NULL;
2351 key = NULL;
2352 uikey = NULL;
2353 name = NULL;
2355 component = MSI_RecordGetString(row, 6);
2356 comp = get_loaded_component(package,component);
2357 if (!comp)
2358 return ERROR_SUCCESS;
2360 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2362 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2363 comp->Action = comp->Installed;
2364 return ERROR_SUCCESS;
2366 comp->Action = INSTALLSTATE_LOCAL;
2368 name = MSI_RecordGetString(row, 4);
2369 if( MSI_RecordIsNull(row,5) && name )
2371 /* null values can have special meanings */
2372 if (name[0]=='-' && name[1] == 0)
2373 return ERROR_SUCCESS;
2374 else if ((name[0]=='+' && name[1] == 0) ||
2375 (name[0] == '*' && name[1] == 0))
2376 name = NULL;
2377 check_first = TRUE;
2380 root = MSI_RecordGetInteger(row,2);
2381 key = MSI_RecordGetString(row, 3);
2383 szRoot = get_root_key( package, root, &root_key );
2384 if (!szRoot)
2385 return ERROR_SUCCESS;
2387 deformat_string(package, key , &deformated);
2388 size = strlenW(deformated) + strlenW(szRoot) + 1;
2389 uikey = msi_alloc(size*sizeof(WCHAR));
2390 strcpyW(uikey,szRoot);
2391 strcatW(uikey,deformated);
2393 if (RegCreateKeyW( root_key, deformated, &hkey))
2395 ERR("Could not create key %s\n",debugstr_w(deformated));
2396 msi_free(deformated);
2397 msi_free(uikey);
2398 return ERROR_SUCCESS;
2400 msi_free(deformated);
2402 value = MSI_RecordGetString(row,5);
2403 if (value)
2404 value_data = parse_value(package, value, &type, &size);
2405 else
2407 value_data = (LPSTR)strdupW(szEmpty);
2408 size = sizeof(szEmpty);
2409 type = REG_SZ;
2412 deformat_string(package, name, &deformated);
2414 if (!check_first)
2416 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2417 debugstr_w(uikey));
2418 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2420 else
2422 DWORD sz = 0;
2423 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2424 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2426 TRACE("value %s of %s checked already exists\n",
2427 debugstr_w(deformated), debugstr_w(uikey));
2429 else
2431 TRACE("Checked and setting value %s of %s\n",
2432 debugstr_w(deformated), debugstr_w(uikey));
2433 if (deformated || size)
2434 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2437 RegCloseKey(hkey);
2439 uirow = MSI_CreateRecord(3);
2440 MSI_RecordSetStringW(uirow,2,deformated);
2441 MSI_RecordSetStringW(uirow,1,uikey);
2442 if (type == REG_SZ || type == REG_EXPAND_SZ)
2443 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2444 ui_actiondata(package,szWriteRegistryValues,uirow);
2445 msiobj_release( &uirow->hdr );
2447 msi_free(value_data);
2448 msi_free(deformated);
2449 msi_free(uikey);
2451 return ERROR_SUCCESS;
2454 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2456 UINT rc;
2457 MSIQUERY * view;
2458 static const WCHAR ExecSeqQuery[] =
2459 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2460 '`','R','e','g','i','s','t','r','y','`',0 };
2462 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2463 if (rc != ERROR_SUCCESS)
2464 return ERROR_SUCCESS;
2466 /* increment progress bar each time action data is sent */
2467 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2469 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2471 msiobj_release(&view->hdr);
2472 return rc;
2475 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2477 LONG res;
2478 HKEY hkey;
2479 DWORD num_subkeys, num_values;
2481 if (delete_key)
2483 if ((res = RegDeleteTreeW( hkey_root, key )))
2485 WARN("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2487 return;
2490 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2492 if ((res = RegDeleteValueW( hkey, value )))
2494 WARN("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2496 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2497 NULL, NULL, NULL, NULL );
2498 RegCloseKey( hkey );
2500 if (!res && !num_subkeys && !num_values)
2502 TRACE("Removing empty key %s\n", debugstr_w(key));
2503 RegDeleteKeyW( hkey_root, key );
2505 return;
2507 WARN("Failed to open key %s (%d)\n", debugstr_w(key), res);
2511 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2513 MSIPACKAGE *package = param;
2514 LPCWSTR component, name, key_str, root_key_str;
2515 LPWSTR deformated_key, deformated_name, ui_key_str;
2516 MSICOMPONENT *comp;
2517 MSIRECORD *uirow;
2518 BOOL delete_key = FALSE;
2519 HKEY hkey_root;
2520 UINT size;
2521 INT root;
2523 ui_progress( package, 2, 0, 0, 0 );
2525 component = MSI_RecordGetString( row, 6 );
2526 comp = get_loaded_component( package, component );
2527 if (!comp)
2528 return ERROR_SUCCESS;
2530 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2532 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2533 comp->Action = comp->Installed;
2534 return ERROR_SUCCESS;
2536 comp->Action = INSTALLSTATE_ABSENT;
2538 name = MSI_RecordGetString( row, 4 );
2539 if (MSI_RecordIsNull( row, 5 ) && name )
2541 if (name[0] == '+' && !name[1])
2542 return ERROR_SUCCESS;
2543 else if ((name[0] == '-' && !name[1]) || (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 ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2582 MSIPACKAGE *package = param;
2583 LPCWSTR component, name, key_str, root_key_str;
2584 LPWSTR deformated_key, deformated_name, ui_key_str;
2585 MSICOMPONENT *comp;
2586 MSIRECORD *uirow;
2587 BOOL delete_key = FALSE;
2588 HKEY hkey_root;
2589 UINT size;
2590 INT root;
2592 ui_progress( package, 2, 0, 0, 0 );
2594 component = MSI_RecordGetString( row, 5 );
2595 comp = get_loaded_component( package, component );
2596 if (!comp)
2597 return ERROR_SUCCESS;
2599 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2601 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2602 comp->Action = comp->Installed;
2603 return ERROR_SUCCESS;
2605 comp->Action = INSTALLSTATE_LOCAL;
2607 if ((name = MSI_RecordGetString( row, 4 )))
2609 if (name[0] == '-' && !name[1])
2611 delete_key = TRUE;
2612 name = NULL;
2616 root = MSI_RecordGetInteger( row, 2 );
2617 key_str = MSI_RecordGetString( row, 3 );
2619 root_key_str = get_root_key( package, root, &hkey_root );
2620 if (!root_key_str)
2621 return ERROR_SUCCESS;
2623 deformat_string( package, key_str, &deformated_key );
2624 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2625 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2626 strcpyW( ui_key_str, root_key_str );
2627 strcatW( ui_key_str, deformated_key );
2629 deformat_string( package, name, &deformated_name );
2631 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2632 msi_free( deformated_key );
2634 uirow = MSI_CreateRecord( 2 );
2635 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2636 MSI_RecordSetStringW( uirow, 2, deformated_name );
2638 ui_actiondata( package, szRemoveRegistryValues, uirow );
2639 msiobj_release( &uirow->hdr );
2641 msi_free( ui_key_str );
2642 msi_free( deformated_name );
2643 return ERROR_SUCCESS;
2646 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2648 UINT rc;
2649 MSIQUERY *view;
2650 static const WCHAR registry_query[] =
2651 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2652 '`','R','e','g','i','s','t','r','y','`',0 };
2653 static const WCHAR remove_registry_query[] =
2654 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2655 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2657 /* increment progress bar each time action data is sent */
2658 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2660 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2661 if (rc == ERROR_SUCCESS)
2663 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2664 msiobj_release( &view->hdr );
2665 if (rc != ERROR_SUCCESS)
2666 return rc;
2669 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2670 if (rc == ERROR_SUCCESS)
2672 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2673 msiobj_release( &view->hdr );
2674 if (rc != ERROR_SUCCESS)
2675 return rc;
2678 return ERROR_SUCCESS;
2681 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2683 package->script->CurrentlyScripting = TRUE;
2685 return ERROR_SUCCESS;
2689 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2691 MSICOMPONENT *comp;
2692 DWORD progress = 0;
2693 DWORD total = 0;
2694 static const WCHAR q1[]=
2695 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2696 '`','R','e','g','i','s','t','r','y','`',0};
2697 UINT rc;
2698 MSIQUERY * view;
2699 MSIFEATURE *feature;
2700 MSIFILE *file;
2702 TRACE("InstallValidate\n");
2704 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2705 if (rc == ERROR_SUCCESS)
2707 MSI_IterateRecords( view, &progress, NULL, package );
2708 msiobj_release( &view->hdr );
2709 total += progress * REG_PROGRESS_VALUE;
2712 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2713 total += COMPONENT_PROGRESS_VALUE;
2715 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2716 total += file->FileSize;
2718 ui_progress(package,0,total,0,0);
2720 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2722 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2723 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2724 feature->ActionRequest);
2727 return ERROR_SUCCESS;
2730 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2732 MSIPACKAGE* package = param;
2733 LPCWSTR cond = NULL;
2734 LPCWSTR message = NULL;
2735 UINT r;
2737 static const WCHAR title[]=
2738 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2740 cond = MSI_RecordGetString(row,1);
2742 r = MSI_EvaluateConditionW(package,cond);
2743 if (r == MSICONDITION_FALSE)
2745 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2747 LPWSTR deformated;
2748 message = MSI_RecordGetString(row,2);
2749 deformat_string(package,message,&deformated);
2750 MessageBoxW(NULL,deformated,title,MB_OK);
2751 msi_free(deformated);
2754 return ERROR_INSTALL_FAILURE;
2757 return ERROR_SUCCESS;
2760 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2762 UINT rc;
2763 MSIQUERY * view = NULL;
2764 static const WCHAR ExecSeqQuery[] =
2765 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2766 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2768 TRACE("Checking launch conditions\n");
2770 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2771 if (rc != ERROR_SUCCESS)
2772 return ERROR_SUCCESS;
2774 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2775 msiobj_release(&view->hdr);
2777 return rc;
2780 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2783 if (!cmp->KeyPath)
2784 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2786 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2788 MSIRECORD * row = 0;
2789 UINT root,len;
2790 LPWSTR deformated,buffer,deformated_name;
2791 LPCWSTR key,name;
2792 static const WCHAR ExecSeqQuery[] =
2793 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2794 '`','R','e','g','i','s','t','r','y','`',' ',
2795 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2796 ' ','=',' ' ,'\'','%','s','\'',0 };
2797 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2798 static const WCHAR fmt2[]=
2799 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2801 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2802 if (!row)
2803 return NULL;
2805 root = MSI_RecordGetInteger(row,2);
2806 key = MSI_RecordGetString(row, 3);
2807 name = MSI_RecordGetString(row, 4);
2808 deformat_string(package, key , &deformated);
2809 deformat_string(package, name, &deformated_name);
2811 len = strlenW(deformated) + 6;
2812 if (deformated_name)
2813 len+=strlenW(deformated_name);
2815 buffer = msi_alloc( len *sizeof(WCHAR));
2817 if (deformated_name)
2818 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2819 else
2820 sprintfW(buffer,fmt,root,deformated);
2822 msi_free(deformated);
2823 msi_free(deformated_name);
2824 msiobj_release(&row->hdr);
2826 return buffer;
2828 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2830 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2831 return NULL;
2833 else
2835 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2837 if (file)
2838 return strdupW( file->TargetPath );
2840 return NULL;
2843 static HKEY openSharedDLLsKey(void)
2845 HKEY hkey=0;
2846 static const WCHAR path[] =
2847 {'S','o','f','t','w','a','r','e','\\',
2848 'M','i','c','r','o','s','o','f','t','\\',
2849 'W','i','n','d','o','w','s','\\',
2850 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2851 'S','h','a','r','e','d','D','L','L','s',0};
2853 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2854 return hkey;
2857 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2859 HKEY hkey;
2860 DWORD count=0;
2861 DWORD type;
2862 DWORD sz = sizeof(count);
2863 DWORD rc;
2865 hkey = openSharedDLLsKey();
2866 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2867 if (rc != ERROR_SUCCESS)
2868 count = 0;
2869 RegCloseKey(hkey);
2870 return count;
2873 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2875 HKEY hkey;
2877 hkey = openSharedDLLsKey();
2878 if (count > 0)
2879 msi_reg_set_val_dword( hkey, path, count );
2880 else
2881 RegDeleteValueW(hkey,path);
2882 RegCloseKey(hkey);
2883 return count;
2887 * Return TRUE if the count should be written out and FALSE if not
2889 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2891 MSIFEATURE *feature;
2892 INT count = 0;
2893 BOOL write = FALSE;
2895 /* only refcount DLLs */
2896 if (comp->KeyPath == NULL ||
2897 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2898 comp->Attributes & msidbComponentAttributesODBCDataSource)
2899 write = FALSE;
2900 else
2902 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2903 write = (count > 0);
2905 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2906 write = TRUE;
2909 /* increment counts */
2910 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2912 ComponentList *cl;
2914 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2915 continue;
2917 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2919 if ( cl->component == comp )
2920 count++;
2924 /* decrement counts */
2925 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2927 ComponentList *cl;
2929 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2930 continue;
2932 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2934 if ( cl->component == comp )
2935 count--;
2939 /* ref count all the files in the component */
2940 if (write)
2942 MSIFILE *file;
2944 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2946 if (file->Component == comp)
2947 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2951 /* add a count for permanent */
2952 if (comp->Attributes & msidbComponentAttributesPermanent)
2953 count ++;
2955 comp->RefCount = count;
2957 if (write)
2958 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2961 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2963 WCHAR squished_pc[GUID_SIZE];
2964 WCHAR squished_cc[GUID_SIZE];
2965 UINT rc;
2966 MSICOMPONENT *comp;
2967 HKEY hkey;
2969 TRACE("\n");
2971 squash_guid(package->ProductCode,squished_pc);
2972 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2974 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2976 MSIRECORD * uirow;
2978 ui_progress(package,2,0,0,0);
2979 if (!comp->ComponentId)
2980 continue;
2982 squash_guid(comp->ComponentId,squished_cc);
2984 msi_free(comp->FullKeypath);
2985 comp->FullKeypath = resolve_keypath( package, comp );
2987 ACTION_RefCountComponent( package, comp );
2989 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2990 debugstr_w(comp->Component),
2991 debugstr_w(squished_cc),
2992 debugstr_w(comp->FullKeypath),
2993 comp->RefCount);
2995 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
2996 comp->ActionRequest == INSTALLSTATE_SOURCE)
2998 if (!comp->FullKeypath)
2999 continue;
3001 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3002 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
3003 &hkey, TRUE);
3004 else
3005 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
3006 &hkey, TRUE);
3008 if (rc != ERROR_SUCCESS)
3009 continue;
3011 if (comp->Attributes & msidbComponentAttributesPermanent)
3013 static const WCHAR szPermKey[] =
3014 { '0','0','0','0','0','0','0','0','0','0','0','0',
3015 '0','0','0','0','0','0','0','0','0','0','0','0',
3016 '0','0','0','0','0','0','0','0',0 };
3018 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3021 if (comp->Action == INSTALLSTATE_LOCAL)
3022 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3023 else
3025 MSIFILE *file;
3026 MSIRECORD *row;
3027 LPWSTR ptr, ptr2;
3028 WCHAR source[MAX_PATH];
3029 WCHAR base[MAX_PATH];
3030 LPWSTR sourcepath;
3032 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3033 static const WCHAR query[] = {
3034 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3035 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3036 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3037 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3038 '`','D','i','s','k','I','d','`',0};
3040 file = get_loaded_file(package, comp->KeyPath);
3041 if (!file)
3042 continue;
3044 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3045 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3046 ptr2 = strrchrW(source, '\\') + 1;
3047 msiobj_release(&row->hdr);
3049 lstrcpyW(base, package->PackagePath);
3050 ptr = strrchrW(base, '\\');
3051 *(ptr + 1) = '\0';
3053 sourcepath = resolve_file_source(package, file);
3054 ptr = sourcepath + lstrlenW(base);
3055 lstrcpyW(ptr2, ptr);
3056 msi_free(sourcepath);
3058 msi_reg_set_val_str(hkey, squished_pc, source);
3060 RegCloseKey(hkey);
3062 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3064 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3065 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3066 else
3067 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3069 comp->Action = comp->ActionRequest;
3071 /* UI stuff */
3072 uirow = MSI_CreateRecord(3);
3073 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3074 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3075 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3076 ui_actiondata(package,szProcessComponents,uirow);
3077 msiobj_release( &uirow->hdr );
3080 return ERROR_SUCCESS;
3083 typedef struct {
3084 CLSID clsid;
3085 LPWSTR source;
3087 LPWSTR path;
3088 ITypeLib *ptLib;
3089 } typelib_struct;
3091 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3092 LPWSTR lpszName, LONG_PTR lParam)
3094 TLIBATTR *attr;
3095 typelib_struct *tl_struct = (typelib_struct*) lParam;
3096 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3097 int sz;
3098 HRESULT res;
3100 if (!IS_INTRESOURCE(lpszName))
3102 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3103 return TRUE;
3106 sz = strlenW(tl_struct->source)+4;
3107 sz *= sizeof(WCHAR);
3109 if ((INT_PTR)lpszName == 1)
3110 tl_struct->path = strdupW(tl_struct->source);
3111 else
3113 tl_struct->path = msi_alloc(sz);
3114 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3117 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3118 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3119 if (FAILED(res))
3121 msi_free(tl_struct->path);
3122 tl_struct->path = NULL;
3124 return TRUE;
3127 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3128 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3130 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3131 return FALSE;
3134 msi_free(tl_struct->path);
3135 tl_struct->path = NULL;
3137 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3138 ITypeLib_Release(tl_struct->ptLib);
3140 return TRUE;
3143 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3145 MSIPACKAGE* package = param;
3146 LPCWSTR component;
3147 MSICOMPONENT *comp;
3148 MSIFILE *file;
3149 typelib_struct tl_struct;
3150 ITypeLib *tlib;
3151 HMODULE module;
3152 HRESULT hr;
3154 component = MSI_RecordGetString(row,3);
3155 comp = get_loaded_component(package,component);
3156 if (!comp)
3157 return ERROR_SUCCESS;
3159 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3161 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3162 comp->Action = comp->Installed;
3163 return ERROR_SUCCESS;
3165 comp->Action = INSTALLSTATE_LOCAL;
3167 file = get_loaded_file( package, comp->KeyPath );
3168 if (!file)
3169 return ERROR_SUCCESS;
3171 ui_actiondata( package, szRegisterTypeLibraries, row );
3173 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3174 if (module)
3176 LPCWSTR guid;
3177 guid = MSI_RecordGetString(row,1);
3178 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3179 tl_struct.source = strdupW( file->TargetPath );
3180 tl_struct.path = NULL;
3182 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3183 (LONG_PTR)&tl_struct);
3185 if (tl_struct.path)
3187 LPWSTR help = NULL;
3188 LPCWSTR helpid;
3189 HRESULT res;
3191 helpid = MSI_RecordGetString(row,6);
3193 if (helpid)
3194 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3195 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3196 msi_free(help);
3198 if (FAILED(res))
3199 ERR("Failed to register type library %s\n",
3200 debugstr_w(tl_struct.path));
3201 else
3202 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3204 ITypeLib_Release(tl_struct.ptLib);
3205 msi_free(tl_struct.path);
3207 else
3208 ERR("Failed to load type library %s\n",
3209 debugstr_w(tl_struct.source));
3211 FreeLibrary(module);
3212 msi_free(tl_struct.source);
3214 else
3216 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3217 if (FAILED(hr))
3219 ERR("Failed to load type library: %08x\n", hr);
3220 return ERROR_INSTALL_FAILURE;
3223 ITypeLib_Release(tlib);
3226 return ERROR_SUCCESS;
3229 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3232 * OK this is a bit confusing.. I am given a _Component key and I believe
3233 * that the file that is being registered as a type library is the "key file
3234 * of that component" which I interpret to mean "The file in the KeyPath of
3235 * that component".
3237 UINT rc;
3238 MSIQUERY * view;
3239 static const WCHAR Query[] =
3240 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3241 '`','T','y','p','e','L','i','b','`',0};
3243 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3244 if (rc != ERROR_SUCCESS)
3245 return ERROR_SUCCESS;
3247 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3248 msiobj_release(&view->hdr);
3249 return rc;
3252 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3254 MSIPACKAGE *package = param;
3255 LPCWSTR component, guid;
3256 MSICOMPONENT *comp;
3257 GUID libid;
3258 UINT version;
3259 LCID language;
3260 SYSKIND syskind;
3261 HRESULT hr;
3263 component = MSI_RecordGetString( row, 3 );
3264 comp = get_loaded_component( package, component );
3265 if (!comp)
3266 return ERROR_SUCCESS;
3268 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3270 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3271 comp->Action = comp->Installed;
3272 return ERROR_SUCCESS;
3274 comp->Action = INSTALLSTATE_ABSENT;
3276 ui_actiondata( package, szUnregisterTypeLibraries, row );
3278 guid = MSI_RecordGetString( row, 1 );
3279 CLSIDFromString( (LPCWSTR)guid, &libid );
3280 version = MSI_RecordGetInteger( row, 4 );
3281 language = MSI_RecordGetInteger( row, 2 );
3283 #ifdef _WIN64
3284 syskind = SYS_WIN64;
3285 #else
3286 syskind = SYS_WIN32;
3287 #endif
3289 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3290 if (FAILED(hr))
3292 WARN("Failed to unregister typelib: %08x\n", hr);
3295 return ERROR_SUCCESS;
3298 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3300 UINT rc;
3301 MSIQUERY *view;
3302 static const WCHAR query[] =
3303 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3304 '`','T','y','p','e','L','i','b','`',0};
3306 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3307 if (rc != ERROR_SUCCESS)
3308 return ERROR_SUCCESS;
3310 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3311 msiobj_release( &view->hdr );
3312 return rc;
3315 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3317 static const WCHAR szlnk[] = {'.','l','n','k',0};
3318 LPCWSTR directory, extension;
3319 LPWSTR link_folder, link_file, filename;
3321 directory = MSI_RecordGetString( row, 2 );
3322 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3324 /* may be needed because of a bug somewhere else */
3325 create_full_pathW( link_folder );
3327 filename = msi_dup_record_field( row, 3 );
3328 reduce_to_longfilename( filename );
3330 extension = strchrW( filename, '.' );
3331 if (!extension || strcmpiW( extension, szlnk ))
3333 int len = strlenW( filename );
3334 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3335 memcpy( filename + len, szlnk, sizeof(szlnk) );
3337 link_file = build_directory_name( 2, link_folder, filename );
3338 msi_free( link_folder );
3339 msi_free( filename );
3341 return link_file;
3344 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3346 MSIPACKAGE *package = param;
3347 LPWSTR link_file, deformated, path;
3348 LPCWSTR component, target;
3349 MSICOMPONENT *comp;
3350 IShellLinkW *sl = NULL;
3351 IPersistFile *pf = NULL;
3352 HRESULT res;
3354 component = MSI_RecordGetString(row, 4);
3355 comp = get_loaded_component(package, component);
3356 if (!comp)
3357 return ERROR_SUCCESS;
3359 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3361 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3362 comp->Action = comp->Installed;
3363 return ERROR_SUCCESS;
3365 comp->Action = INSTALLSTATE_LOCAL;
3367 ui_actiondata(package,szCreateShortcuts,row);
3369 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3370 &IID_IShellLinkW, (LPVOID *) &sl );
3372 if (FAILED( res ))
3374 ERR("CLSID_ShellLink not available\n");
3375 goto err;
3378 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3379 if (FAILED( res ))
3381 ERR("QueryInterface(IID_IPersistFile) failed\n");
3382 goto err;
3385 target = MSI_RecordGetString(row, 5);
3386 if (strchrW(target, '['))
3388 deformat_string(package, target, &deformated);
3389 IShellLinkW_SetPath(sl,deformated);
3390 msi_free(deformated);
3392 else
3394 FIXME("poorly handled shortcut format, advertised shortcut\n");
3395 IShellLinkW_SetPath(sl,comp->FullKeypath);
3398 if (!MSI_RecordIsNull(row,6))
3400 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3401 deformat_string(package, arguments, &deformated);
3402 IShellLinkW_SetArguments(sl,deformated);
3403 msi_free(deformated);
3406 if (!MSI_RecordIsNull(row,7))
3408 LPCWSTR description = MSI_RecordGetString(row, 7);
3409 IShellLinkW_SetDescription(sl, description);
3412 if (!MSI_RecordIsNull(row,8))
3413 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3415 if (!MSI_RecordIsNull(row,9))
3417 INT index;
3418 LPCWSTR icon = MSI_RecordGetString(row, 9);
3420 path = build_icon_path(package, icon);
3421 index = MSI_RecordGetInteger(row,10);
3423 /* no value means 0 */
3424 if (index == MSI_NULL_INTEGER)
3425 index = 0;
3427 IShellLinkW_SetIconLocation(sl, path, index);
3428 msi_free(path);
3431 if (!MSI_RecordIsNull(row,11))
3432 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3434 if (!MSI_RecordIsNull(row,12))
3436 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3437 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3438 if (path)
3439 IShellLinkW_SetWorkingDirectory(sl, path);
3440 msi_free(path);
3443 link_file = get_link_file(package, row);
3445 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3446 IPersistFile_Save(pf, link_file, FALSE);
3448 msi_free(link_file);
3450 err:
3451 if (pf)
3452 IPersistFile_Release( pf );
3453 if (sl)
3454 IShellLinkW_Release( sl );
3456 return ERROR_SUCCESS;
3459 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3461 UINT rc;
3462 HRESULT res;
3463 MSIQUERY * view;
3464 static const WCHAR Query[] =
3465 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3466 '`','S','h','o','r','t','c','u','t','`',0};
3468 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3469 if (rc != ERROR_SUCCESS)
3470 return ERROR_SUCCESS;
3472 res = CoInitialize( NULL );
3474 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3475 msiobj_release(&view->hdr);
3477 if (SUCCEEDED(res))
3478 CoUninitialize();
3480 return rc;
3483 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3485 MSIPACKAGE *package = param;
3486 LPWSTR link_file;
3487 LPCWSTR component;
3488 MSICOMPONENT *comp;
3490 component = MSI_RecordGetString( row, 4 );
3491 comp = get_loaded_component( package, component );
3492 if (!comp)
3493 return ERROR_SUCCESS;
3495 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3497 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3498 comp->Action = comp->Installed;
3499 return ERROR_SUCCESS;
3501 comp->Action = INSTALLSTATE_ABSENT;
3503 ui_actiondata( package, szRemoveShortcuts, row );
3505 link_file = get_link_file( package, row );
3507 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3508 if (!DeleteFileW( link_file ))
3510 WARN("Failed to remove shortcut file %u\n", GetLastError());
3512 msi_free( link_file );
3514 return ERROR_SUCCESS;
3517 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3519 UINT rc;
3520 MSIQUERY *view;
3521 static const WCHAR query[] =
3522 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3523 '`','S','h','o','r','t','c','u','t','`',0};
3525 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3526 if (rc != ERROR_SUCCESS)
3527 return ERROR_SUCCESS;
3529 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3530 msiobj_release( &view->hdr );
3532 return rc;
3535 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3537 MSIPACKAGE* package = param;
3538 HANDLE the_file;
3539 LPWSTR FilePath;
3540 LPCWSTR FileName;
3541 CHAR buffer[1024];
3542 DWORD sz;
3543 UINT rc;
3545 FileName = MSI_RecordGetString(row,1);
3546 if (!FileName)
3548 ERR("Unable to get FileName\n");
3549 return ERROR_SUCCESS;
3552 FilePath = build_icon_path(package,FileName);
3554 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3556 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3557 FILE_ATTRIBUTE_NORMAL, NULL);
3559 if (the_file == INVALID_HANDLE_VALUE)
3561 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3562 msi_free(FilePath);
3563 return ERROR_SUCCESS;
3568 DWORD write;
3569 sz = 1024;
3570 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3571 if (rc != ERROR_SUCCESS)
3573 ERR("Failed to get stream\n");
3574 CloseHandle(the_file);
3575 DeleteFileW(FilePath);
3576 break;
3578 WriteFile(the_file,buffer,sz,&write,NULL);
3579 } while (sz == 1024);
3581 msi_free(FilePath);
3582 CloseHandle(the_file);
3584 return ERROR_SUCCESS;
3587 static UINT msi_publish_icons(MSIPACKAGE *package)
3589 UINT r;
3590 MSIQUERY *view;
3592 static const WCHAR query[]= {
3593 'S','E','L','E','C','T',' ','*',' ',
3594 'F','R','O','M',' ','`','I','c','o','n','`',0};
3596 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3597 if (r == ERROR_SUCCESS)
3599 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3600 msiobj_release(&view->hdr);
3603 return ERROR_SUCCESS;
3606 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3608 UINT r;
3609 HKEY source;
3610 LPWSTR buffer;
3611 MSIMEDIADISK *disk;
3612 MSISOURCELISTINFO *info;
3614 r = RegCreateKeyW(hkey, szSourceList, &source);
3615 if (r != ERROR_SUCCESS)
3616 return r;
3618 RegCloseKey(source);
3620 buffer = strrchrW(package->PackagePath, '\\') + 1;
3621 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3622 package->Context, MSICODE_PRODUCT,
3623 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3624 if (r != ERROR_SUCCESS)
3625 return r;
3627 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3628 package->Context, MSICODE_PRODUCT,
3629 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3630 if (r != ERROR_SUCCESS)
3631 return r;
3633 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3634 package->Context, MSICODE_PRODUCT,
3635 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3636 if (r != ERROR_SUCCESS)
3637 return r;
3639 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3641 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3642 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3643 info->options, info->value);
3644 else
3645 MsiSourceListSetInfoW(package->ProductCode, NULL,
3646 info->context, info->options,
3647 info->property, info->value);
3650 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3652 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3653 disk->context, disk->options,
3654 disk->disk_id, disk->volume_label, disk->disk_prompt);
3657 return ERROR_SUCCESS;
3660 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3662 MSIHANDLE hdb, suminfo;
3663 WCHAR guids[MAX_PATH];
3664 WCHAR packcode[SQUISH_GUID_SIZE];
3665 LPWSTR buffer;
3666 LPWSTR ptr;
3667 DWORD langid;
3668 DWORD size;
3669 UINT r;
3671 static const WCHAR szProductLanguage[] =
3672 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3673 static const WCHAR szARPProductIcon[] =
3674 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3675 static const WCHAR szProductVersion[] =
3676 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3677 static const WCHAR szAssignment[] =
3678 {'A','s','s','i','g','n','m','e','n','t',0};
3679 static const WCHAR szAdvertiseFlags[] =
3680 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3681 static const WCHAR szClients[] =
3682 {'C','l','i','e','n','t','s',0};
3683 static const WCHAR szColon[] = {':',0};
3685 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3686 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3687 msi_free(buffer);
3689 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3690 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3692 /* FIXME */
3693 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3695 buffer = msi_dup_property(package->db, szARPProductIcon);
3696 if (buffer)
3698 LPWSTR path = build_icon_path(package,buffer);
3699 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3700 msi_free(path);
3701 msi_free(buffer);
3704 buffer = msi_dup_property(package->db, szProductVersion);
3705 if (buffer)
3707 DWORD verdword = msi_version_str_to_dword(buffer);
3708 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3709 msi_free(buffer);
3712 msi_reg_set_val_dword(hkey, szAssignment, 0);
3713 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3714 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3715 msi_reg_set_val_str(hkey, szClients, szColon);
3717 hdb = alloc_msihandle(&package->db->hdr);
3718 if (!hdb)
3719 return ERROR_NOT_ENOUGH_MEMORY;
3721 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3722 MsiCloseHandle(hdb);
3723 if (r != ERROR_SUCCESS)
3724 goto done;
3726 size = MAX_PATH;
3727 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3728 NULL, guids, &size);
3729 if (r != ERROR_SUCCESS)
3730 goto done;
3732 ptr = strchrW(guids, ';');
3733 if (ptr) *ptr = 0;
3734 squash_guid(guids, packcode);
3735 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3737 done:
3738 MsiCloseHandle(suminfo);
3739 return ERROR_SUCCESS;
3742 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3744 UINT r;
3745 HKEY hkey;
3746 LPWSTR upgrade;
3747 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3749 static const WCHAR szUpgradeCode[] =
3750 {'U','p','g','r','a','d','e','C','o','d','e',0};
3752 upgrade = msi_dup_property(package->db, szUpgradeCode);
3753 if (!upgrade)
3754 return ERROR_SUCCESS;
3756 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3758 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3759 if (r != ERROR_SUCCESS)
3760 goto done;
3762 else
3764 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3765 if (r != ERROR_SUCCESS)
3766 goto done;
3769 squash_guid(package->ProductCode, squashed_pc);
3770 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3772 RegCloseKey(hkey);
3774 done:
3775 msi_free(upgrade);
3776 return r;
3779 static BOOL msi_check_publish(MSIPACKAGE *package)
3781 MSIFEATURE *feature;
3783 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3785 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3786 return TRUE;
3789 return FALSE;
3792 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3794 MSIFEATURE *feature;
3796 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3798 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3799 return FALSE;
3802 return TRUE;
3805 static UINT msi_publish_patches( MSIPACKAGE *package, HKEY prodkey )
3807 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
3808 WCHAR patch_squashed[GUID_SIZE];
3809 HKEY patches_key = NULL, product_patches_key;
3810 LONG res;
3811 MSIPATCHINFO *patch;
3812 UINT r;
3813 WCHAR *p, *all_patches = NULL;
3814 DWORD len = 0;
3816 res = RegCreateKeyExW( prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
3817 if (res != ERROR_SUCCESS)
3818 return ERROR_FUNCTION_FAILED;
3820 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
3821 if (r != ERROR_SUCCESS)
3822 goto done;
3824 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3826 squash_guid( patch->patchcode, patch_squashed );
3827 len += strlenW( patch_squashed ) + 1;
3830 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
3831 if (!all_patches)
3832 goto done;
3834 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3836 HKEY patch_key;
3838 squash_guid( patch->patchcode, p );
3839 p += strlenW( p ) + 1;
3841 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
3842 (const BYTE *)patch->transforms,
3843 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
3844 if (res != ERROR_SUCCESS)
3845 goto done;
3847 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
3848 if (r != ERROR_SUCCESS)
3849 goto done;
3851 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
3852 (const BYTE *)patch->localfile,
3853 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
3854 RegCloseKey( patch_key );
3855 if (res != ERROR_SUCCESS)
3856 goto done;
3858 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
3859 RegCloseKey( patch_key );
3860 if (res != ERROR_SUCCESS)
3861 goto done;
3864 all_patches[len] = 0;
3865 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
3866 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3867 if (res != ERROR_SUCCESS)
3868 goto done;
3870 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
3871 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3872 if (res != ERROR_SUCCESS)
3873 r = ERROR_FUNCTION_FAILED;
3875 done:
3876 RegCloseKey( product_patches_key );
3877 RegCloseKey( patches_key );
3878 msi_free( all_patches );
3879 return r;
3883 * 99% of the work done here is only done for
3884 * advertised installs. However this is where the
3885 * Icon table is processed and written out
3886 * so that is what I am going to do here.
3888 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3890 UINT rc;
3891 HKEY hukey = NULL, hudkey = NULL;
3892 MSIRECORD *uirow;
3894 /* FIXME: also need to publish if the product is in advertise mode */
3895 if (!msi_check_publish(package))
3896 return ERROR_SUCCESS;
3898 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3899 &hukey, TRUE);
3900 if (rc != ERROR_SUCCESS)
3901 goto end;
3903 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3904 NULL, &hudkey, TRUE);
3905 if (rc != ERROR_SUCCESS)
3906 goto end;
3908 rc = msi_publish_upgrade_code(package);
3909 if (rc != ERROR_SUCCESS)
3910 goto end;
3912 if (!list_empty(&package->patches))
3914 rc = msi_publish_patches(package, hukey);
3915 if (rc != ERROR_SUCCESS)
3916 goto end;
3919 rc = msi_publish_product_properties(package, hukey);
3920 if (rc != ERROR_SUCCESS)
3921 goto end;
3923 rc = msi_publish_sourcelist(package, hukey);
3924 if (rc != ERROR_SUCCESS)
3925 goto end;
3927 rc = msi_publish_icons(package);
3929 end:
3930 uirow = MSI_CreateRecord( 1 );
3931 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
3932 ui_actiondata( package, szPublishProduct, uirow );
3933 msiobj_release( &uirow->hdr );
3935 RegCloseKey(hukey);
3936 RegCloseKey(hudkey);
3938 return rc;
3941 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
3943 WCHAR *filename, *ptr, *folder, *ret;
3944 const WCHAR *dirprop;
3946 filename = msi_dup_record_field( row, 2 );
3947 if (filename && (ptr = strchrW( filename, '|' )))
3948 ptr++;
3949 else
3950 ptr = filename;
3952 dirprop = MSI_RecordGetString( row, 3 );
3953 if (dirprop)
3955 folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
3956 if (!folder)
3957 folder = msi_dup_property( package->db, dirprop );
3959 else
3960 folder = msi_dup_property( package->db, szWindowsFolder );
3962 if (!folder)
3964 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
3965 msi_free( filename );
3966 return NULL;
3969 ret = build_directory_name( 2, folder, ptr );
3971 msi_free( filename );
3972 msi_free( folder );
3973 return ret;
3976 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3978 MSIPACKAGE *package = param;
3979 LPCWSTR component, section, key, value, identifier;
3980 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
3981 MSIRECORD * uirow;
3982 INT action;
3983 MSICOMPONENT *comp;
3985 component = MSI_RecordGetString(row, 8);
3986 comp = get_loaded_component(package,component);
3987 if (!comp)
3988 return ERROR_SUCCESS;
3990 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3992 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3993 comp->Action = comp->Installed;
3994 return ERROR_SUCCESS;
3996 comp->Action = INSTALLSTATE_LOCAL;
3998 identifier = MSI_RecordGetString(row,1);
3999 section = MSI_RecordGetString(row,4);
4000 key = MSI_RecordGetString(row,5);
4001 value = MSI_RecordGetString(row,6);
4002 action = MSI_RecordGetInteger(row,7);
4004 deformat_string(package,section,&deformated_section);
4005 deformat_string(package,key,&deformated_key);
4006 deformat_string(package,value,&deformated_value);
4008 fullname = get_ini_file_name(package, row);
4010 if (action == 0)
4012 TRACE("Adding value %s to section %s in %s\n",
4013 debugstr_w(deformated_key), debugstr_w(deformated_section),
4014 debugstr_w(fullname));
4015 WritePrivateProfileStringW(deformated_section, deformated_key,
4016 deformated_value, fullname);
4018 else if (action == 1)
4020 WCHAR returned[10];
4021 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4022 returned, 10, fullname);
4023 if (returned[0] == 0)
4025 TRACE("Adding value %s to section %s in %s\n",
4026 debugstr_w(deformated_key), debugstr_w(deformated_section),
4027 debugstr_w(fullname));
4029 WritePrivateProfileStringW(deformated_section, deformated_key,
4030 deformated_value, fullname);
4033 else if (action == 3)
4034 FIXME("Append to existing section not yet implemented\n");
4036 uirow = MSI_CreateRecord(4);
4037 MSI_RecordSetStringW(uirow,1,identifier);
4038 MSI_RecordSetStringW(uirow,2,deformated_section);
4039 MSI_RecordSetStringW(uirow,3,deformated_key);
4040 MSI_RecordSetStringW(uirow,4,deformated_value);
4041 ui_actiondata(package,szWriteIniValues,uirow);
4042 msiobj_release( &uirow->hdr );
4044 msi_free(fullname);
4045 msi_free(deformated_key);
4046 msi_free(deformated_value);
4047 msi_free(deformated_section);
4048 return ERROR_SUCCESS;
4051 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4053 UINT rc;
4054 MSIQUERY * view;
4055 static const WCHAR ExecSeqQuery[] =
4056 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4057 '`','I','n','i','F','i','l','e','`',0};
4059 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4060 if (rc != ERROR_SUCCESS)
4062 TRACE("no IniFile table\n");
4063 return ERROR_SUCCESS;
4066 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4067 msiobj_release(&view->hdr);
4068 return rc;
4071 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4073 MSIPACKAGE *package = param;
4074 LPCWSTR component, section, key, value, identifier;
4075 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4076 MSICOMPONENT *comp;
4077 MSIRECORD *uirow;
4078 INT action;
4080 component = MSI_RecordGetString( row, 8 );
4081 comp = get_loaded_component( package, component );
4082 if (!comp)
4083 return ERROR_SUCCESS;
4085 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4087 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4088 comp->Action = comp->Installed;
4089 return ERROR_SUCCESS;
4091 comp->Action = INSTALLSTATE_ABSENT;
4093 identifier = MSI_RecordGetString( row, 1 );
4094 section = MSI_RecordGetString( row, 4 );
4095 key = MSI_RecordGetString( row, 5 );
4096 value = MSI_RecordGetString( row, 6 );
4097 action = MSI_RecordGetInteger( row, 7 );
4099 deformat_string( package, section, &deformated_section );
4100 deformat_string( package, key, &deformated_key );
4101 deformat_string( package, value, &deformated_value );
4103 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4105 filename = get_ini_file_name( package, row );
4107 TRACE("Removing key %s from section %s in %s\n",
4108 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4110 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4112 WARN("Unable to remove key %u\n", GetLastError());
4114 msi_free( filename );
4116 else
4117 FIXME("Unsupported action %d\n", action);
4120 uirow = MSI_CreateRecord( 4 );
4121 MSI_RecordSetStringW( uirow, 1, identifier );
4122 MSI_RecordSetStringW( uirow, 2, deformated_section );
4123 MSI_RecordSetStringW( uirow, 3, deformated_key );
4124 MSI_RecordSetStringW( uirow, 4, deformated_value );
4125 ui_actiondata( package, szRemoveIniValues, uirow );
4126 msiobj_release( &uirow->hdr );
4128 msi_free( deformated_key );
4129 msi_free( deformated_value );
4130 msi_free( deformated_section );
4131 return ERROR_SUCCESS;
4134 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4136 MSIPACKAGE *package = param;
4137 LPCWSTR component, section, key, value, identifier;
4138 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4139 MSICOMPONENT *comp;
4140 MSIRECORD *uirow;
4141 INT action;
4143 component = MSI_RecordGetString( row, 8 );
4144 comp = get_loaded_component( package, component );
4145 if (!comp)
4146 return ERROR_SUCCESS;
4148 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4150 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4151 comp->Action = comp->Installed;
4152 return ERROR_SUCCESS;
4154 comp->Action = INSTALLSTATE_LOCAL;
4156 identifier = MSI_RecordGetString( row, 1 );
4157 section = MSI_RecordGetString( row, 4 );
4158 key = MSI_RecordGetString( row, 5 );
4159 value = MSI_RecordGetString( row, 6 );
4160 action = MSI_RecordGetInteger( row, 7 );
4162 deformat_string( package, section, &deformated_section );
4163 deformat_string( package, key, &deformated_key );
4164 deformat_string( package, value, &deformated_value );
4166 if (action == msidbIniFileActionRemoveLine)
4168 filename = get_ini_file_name( package, row );
4170 TRACE("Removing key %s from section %s in %s\n",
4171 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4173 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4175 WARN("Unable to remove key %u\n", GetLastError());
4177 msi_free( filename );
4179 else
4180 FIXME("Unsupported action %d\n", action);
4182 uirow = MSI_CreateRecord( 4 );
4183 MSI_RecordSetStringW( uirow, 1, identifier );
4184 MSI_RecordSetStringW( uirow, 2, deformated_section );
4185 MSI_RecordSetStringW( uirow, 3, deformated_key );
4186 MSI_RecordSetStringW( uirow, 4, deformated_value );
4187 ui_actiondata( package, szRemoveIniValues, uirow );
4188 msiobj_release( &uirow->hdr );
4190 msi_free( deformated_key );
4191 msi_free( deformated_value );
4192 msi_free( deformated_section );
4193 return ERROR_SUCCESS;
4196 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4198 UINT rc;
4199 MSIQUERY *view;
4200 static const WCHAR query[] =
4201 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4202 '`','I','n','i','F','i','l','e','`',0};
4203 static const WCHAR remove_query[] =
4204 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4205 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4207 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4208 if (rc == ERROR_SUCCESS)
4210 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4211 msiobj_release( &view->hdr );
4212 if (rc != ERROR_SUCCESS)
4213 return rc;
4216 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4217 if (rc == ERROR_SUCCESS)
4219 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4220 msiobj_release( &view->hdr );
4221 if (rc != ERROR_SUCCESS)
4222 return rc;
4225 return ERROR_SUCCESS;
4228 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4230 MSIPACKAGE *package = param;
4231 LPCWSTR filename;
4232 LPWSTR FullName;
4233 MSIFILE *file;
4234 DWORD len;
4235 static const WCHAR ExeStr[] =
4236 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
4237 static const WCHAR close[] = {'\"',0};
4238 STARTUPINFOW si;
4239 PROCESS_INFORMATION info;
4240 BOOL brc;
4241 MSIRECORD *uirow;
4242 LPWSTR uipath, p;
4244 memset(&si,0,sizeof(STARTUPINFOW));
4246 filename = MSI_RecordGetString(row,1);
4247 file = get_loaded_file( package, filename );
4249 if (!file)
4251 ERR("Unable to find file id %s\n",debugstr_w(filename));
4252 return ERROR_SUCCESS;
4255 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
4257 FullName = msi_alloc(len*sizeof(WCHAR));
4258 strcpyW(FullName,ExeStr);
4259 strcatW( FullName, file->TargetPath );
4260 strcatW(FullName,close);
4262 TRACE("Registering %s\n",debugstr_w(FullName));
4263 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
4264 &si, &info);
4266 if (brc)
4268 CloseHandle(info.hThread);
4269 msi_dialog_check_messages(info.hProcess);
4270 CloseHandle(info.hProcess);
4273 uirow = MSI_CreateRecord( 2 );
4274 MSI_RecordSetStringW( uirow, 1, filename );
4275 uipath = strdupW( file->TargetPath );
4276 if ((p = strrchrW( uipath,'\\' ))) *p = 0;
4277 MSI_RecordSetStringW( uirow, 2, uipath );
4278 ui_actiondata( package, szSelfRegModules, uirow );
4279 msiobj_release( &uirow->hdr );
4281 msi_free( FullName );
4282 msi_free( uipath );
4283 return ERROR_SUCCESS;
4286 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4288 UINT rc;
4289 MSIQUERY * view;
4290 static const WCHAR ExecSeqQuery[] =
4291 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4292 '`','S','e','l','f','R','e','g','`',0};
4294 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4295 if (rc != ERROR_SUCCESS)
4297 TRACE("no SelfReg table\n");
4298 return ERROR_SUCCESS;
4301 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4302 msiobj_release(&view->hdr);
4304 return ERROR_SUCCESS;
4307 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4309 static const WCHAR regsvr32[] =
4310 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
4311 static const WCHAR close[] = {'\"',0};
4312 MSIPACKAGE *package = param;
4313 LPCWSTR filename;
4314 LPWSTR cmdline;
4315 MSIFILE *file;
4316 DWORD len;
4317 STARTUPINFOW si;
4318 PROCESS_INFORMATION pi;
4319 BOOL ret;
4320 MSIRECORD *uirow;
4321 LPWSTR uipath, p;
4323 memset( &si, 0, sizeof(STARTUPINFOW) );
4325 filename = MSI_RecordGetString( row, 1 );
4326 file = get_loaded_file( package, filename );
4328 if (!file)
4330 ERR("Unable to find file id %s\n", debugstr_w(filename));
4331 return ERROR_SUCCESS;
4334 len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
4336 cmdline = msi_alloc( len * sizeof(WCHAR) );
4337 strcpyW( cmdline, regsvr32 );
4338 strcatW( cmdline, file->TargetPath );
4339 strcatW( cmdline, close );
4341 TRACE("Unregistering %s\n", debugstr_w(cmdline));
4343 ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
4344 if (ret)
4346 CloseHandle( pi.hThread );
4347 msi_dialog_check_messages( pi.hProcess );
4348 CloseHandle( pi.hProcess );
4351 uirow = MSI_CreateRecord( 2 );
4352 MSI_RecordSetStringW( uirow, 1, filename );
4353 uipath = strdupW( file->TargetPath );
4354 if ((p = strrchrW( uipath,'\\' ))) *p = 0;
4355 MSI_RecordSetStringW( uirow, 2, uipath );
4356 ui_actiondata( package, szSelfUnregModules, uirow );
4357 msiobj_release( &uirow->hdr );
4359 msi_free( cmdline );
4360 msi_free( uipath );
4361 return ERROR_SUCCESS;
4364 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4366 UINT rc;
4367 MSIQUERY *view;
4368 static const WCHAR query[] =
4369 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4370 '`','S','e','l','f','R','e','g','`',0};
4372 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4373 if (rc != ERROR_SUCCESS)
4375 TRACE("no SelfReg table\n");
4376 return ERROR_SUCCESS;
4379 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4380 msiobj_release( &view->hdr );
4382 return ERROR_SUCCESS;
4385 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4387 MSIFEATURE *feature;
4388 UINT rc;
4389 HKEY hkey = NULL, userdata = NULL;
4391 if (!msi_check_publish(package))
4392 return ERROR_SUCCESS;
4394 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4395 &hkey, TRUE);
4396 if (rc != ERROR_SUCCESS)
4397 goto end;
4399 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4400 &userdata, TRUE);
4401 if (rc != ERROR_SUCCESS)
4402 goto end;
4404 /* here the guids are base 85 encoded */
4405 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4407 ComponentList *cl;
4408 LPWSTR data = NULL;
4409 GUID clsid;
4410 INT size;
4411 BOOL absent = FALSE;
4412 MSIRECORD *uirow;
4414 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4415 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4416 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4418 size = 1;
4419 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4421 size += 21;
4423 if (feature->Feature_Parent)
4424 size += strlenW( feature->Feature_Parent )+2;
4426 data = msi_alloc(size * sizeof(WCHAR));
4428 data[0] = 0;
4429 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4431 MSICOMPONENT* component = cl->component;
4432 WCHAR buf[21];
4434 buf[0] = 0;
4435 if (component->ComponentId)
4437 TRACE("From %s\n",debugstr_w(component->ComponentId));
4438 CLSIDFromString(component->ComponentId, &clsid);
4439 encode_base85_guid(&clsid,buf);
4440 TRACE("to %s\n",debugstr_w(buf));
4441 strcatW(data,buf);
4445 if (feature->Feature_Parent)
4447 static const WCHAR sep[] = {'\2',0};
4448 strcatW(data,sep);
4449 strcatW(data,feature->Feature_Parent);
4452 msi_reg_set_val_str( userdata, feature->Feature, data );
4453 msi_free(data);
4455 size = 0;
4456 if (feature->Feature_Parent)
4457 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4458 if (!absent)
4460 size += sizeof(WCHAR);
4461 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4462 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4464 else
4466 size += 2*sizeof(WCHAR);
4467 data = msi_alloc(size);
4468 data[0] = 0x6;
4469 data[1] = 0;
4470 if (feature->Feature_Parent)
4471 strcpyW( &data[1], feature->Feature_Parent );
4472 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4473 (LPBYTE)data,size);
4474 msi_free(data);
4477 /* the UI chunk */
4478 uirow = MSI_CreateRecord( 1 );
4479 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4480 ui_actiondata( package, szPublishFeatures, uirow);
4481 msiobj_release( &uirow->hdr );
4482 /* FIXME: call ui_progress? */
4485 end:
4486 RegCloseKey(hkey);
4487 RegCloseKey(userdata);
4488 return rc;
4491 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4493 UINT r;
4494 HKEY hkey;
4495 MSIRECORD *uirow;
4497 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4499 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4500 &hkey, FALSE);
4501 if (r == ERROR_SUCCESS)
4503 RegDeleteValueW(hkey, feature->Feature);
4504 RegCloseKey(hkey);
4507 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4508 &hkey, FALSE);
4509 if (r == ERROR_SUCCESS)
4511 RegDeleteValueW(hkey, feature->Feature);
4512 RegCloseKey(hkey);
4515 uirow = MSI_CreateRecord( 1 );
4516 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4517 ui_actiondata( package, szUnpublishFeatures, uirow );
4518 msiobj_release( &uirow->hdr );
4520 return ERROR_SUCCESS;
4523 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4525 MSIFEATURE *feature;
4527 if (!msi_check_unpublish(package))
4528 return ERROR_SUCCESS;
4530 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4532 msi_unpublish_feature(package, feature);
4535 return ERROR_SUCCESS;
4538 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4540 LPWSTR prop, val, key;
4541 SYSTEMTIME systime;
4542 DWORD size, langid;
4543 WCHAR date[9];
4544 LPWSTR buffer;
4546 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4547 static const WCHAR szWindowsInstaller[] =
4548 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4549 static const WCHAR modpath_fmt[] =
4550 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4551 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4552 static const WCHAR szModifyPath[] =
4553 {'M','o','d','i','f','y','P','a','t','h',0};
4554 static const WCHAR szUninstallString[] =
4555 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4556 static const WCHAR szEstimatedSize[] =
4557 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4558 static const WCHAR szProductLanguage[] =
4559 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4560 static const WCHAR szProductVersion[] =
4561 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4562 static const WCHAR szProductName[] =
4563 {'P','r','o','d','u','c','t','N','a','m','e',0};
4564 static const WCHAR szDisplayName[] =
4565 {'D','i','s','p','l','a','y','N','a','m','e',0};
4566 static const WCHAR szDisplayVersion[] =
4567 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4568 static const WCHAR szManufacturer[] =
4569 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4571 static const LPCSTR propval[] = {
4572 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4573 "ARPCONTACT", "Contact",
4574 "ARPCOMMENTS", "Comments",
4575 "ProductName", "DisplayName",
4576 "ProductVersion", "DisplayVersion",
4577 "ARPHELPLINK", "HelpLink",
4578 "ARPHELPTELEPHONE", "HelpTelephone",
4579 "ARPINSTALLLOCATION", "InstallLocation",
4580 "SourceDir", "InstallSource",
4581 "Manufacturer", "Publisher",
4582 "ARPREADME", "Readme",
4583 "ARPSIZE", "Size",
4584 "ARPURLINFOABOUT", "URLInfoAbout",
4585 "ARPURLUPDATEINFO", "URLUpdateInfo",
4586 NULL,
4588 const LPCSTR *p = propval;
4590 while (*p)
4592 prop = strdupAtoW(*p++);
4593 key = strdupAtoW(*p++);
4594 val = msi_dup_property(package->db, prop);
4595 msi_reg_set_val_str(hkey, key, val);
4596 msi_free(val);
4597 msi_free(key);
4598 msi_free(prop);
4601 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4603 size = deformat_string(package, modpath_fmt, &buffer);
4604 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4605 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4606 msi_free(buffer);
4608 /* FIXME: Write real Estimated Size when we have it */
4609 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4611 buffer = msi_dup_property(package->db, szProductName);
4612 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4613 msi_free(buffer);
4615 buffer = msi_dup_property(package->db, cszSourceDir);
4616 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4617 msi_free(buffer);
4619 buffer = msi_dup_property(package->db, szManufacturer);
4620 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4621 msi_free(buffer);
4623 GetLocalTime(&systime);
4624 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4625 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4627 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4628 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4630 buffer = msi_dup_property(package->db, szProductVersion);
4631 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4632 if (buffer)
4634 DWORD verdword = msi_version_str_to_dword(buffer);
4636 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4637 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4638 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4639 msi_free(buffer);
4642 return ERROR_SUCCESS;
4645 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4647 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4648 MSIRECORD *uirow;
4649 LPWSTR upgrade_code;
4650 HKEY hkey, props;
4651 HKEY upgrade;
4652 UINT rc;
4654 static const WCHAR szUpgradeCode[] = {
4655 'U','p','g','r','a','d','e','C','o','d','e',0};
4657 /* FIXME: also need to publish if the product is in advertise mode */
4658 if (!msi_check_publish(package))
4659 return ERROR_SUCCESS;
4661 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4662 if (rc != ERROR_SUCCESS)
4663 return rc;
4665 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4666 NULL, &props, TRUE);
4667 if (rc != ERROR_SUCCESS)
4668 goto done;
4670 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4671 msi_free( package->db->localfile );
4672 package->db->localfile = NULL;
4674 rc = msi_publish_install_properties(package, hkey);
4675 if (rc != ERROR_SUCCESS)
4676 goto done;
4678 rc = msi_publish_install_properties(package, props);
4679 if (rc != ERROR_SUCCESS)
4680 goto done;
4682 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4683 if (upgrade_code)
4685 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4686 squash_guid(package->ProductCode, squashed_pc);
4687 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4688 RegCloseKey(upgrade);
4689 msi_free(upgrade_code);
4692 done:
4693 uirow = MSI_CreateRecord( 1 );
4694 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4695 ui_actiondata( package, szRegisterProduct, uirow );
4696 msiobj_release( &uirow->hdr );
4698 RegCloseKey(hkey);
4699 return ERROR_SUCCESS;
4702 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4704 return execute_script(package,INSTALL_SCRIPT);
4707 static UINT msi_unpublish_product(MSIPACKAGE *package)
4709 LPWSTR upgrade;
4710 LPWSTR remove = NULL;
4711 LPWSTR *features = NULL;
4712 BOOL full_uninstall = TRUE;
4713 MSIFEATURE *feature;
4714 MSIPATCHINFO *patch;
4716 static const WCHAR szUpgradeCode[] =
4717 {'U','p','g','r','a','d','e','C','o','d','e',0};
4719 remove = msi_dup_property(package->db, szRemove);
4720 if (!remove)
4721 return ERROR_SUCCESS;
4723 features = msi_split_string(remove, ',');
4724 if (!features)
4726 msi_free(remove);
4727 ERR("REMOVE feature list is empty!\n");
4728 return ERROR_FUNCTION_FAILED;
4731 if (!lstrcmpW(features[0], szAll))
4732 full_uninstall = TRUE;
4733 else
4735 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4737 if (feature->Action != INSTALLSTATE_ABSENT)
4738 full_uninstall = FALSE;
4742 if (!full_uninstall)
4743 goto done;
4745 MSIREG_DeleteProductKey(package->ProductCode);
4746 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4747 MSIREG_DeleteUninstallKey(package->ProductCode);
4749 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4751 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4752 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4754 else
4756 MSIREG_DeleteUserProductKey(package->ProductCode);
4757 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4760 upgrade = msi_dup_property(package->db, szUpgradeCode);
4761 if (upgrade)
4763 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4764 msi_free(upgrade);
4767 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4769 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4772 done:
4773 msi_free(remove);
4774 msi_free(features);
4775 return ERROR_SUCCESS;
4778 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4780 UINT rc;
4782 rc = msi_unpublish_product(package);
4783 if (rc != ERROR_SUCCESS)
4784 return rc;
4786 /* turn off scheduling */
4787 package->script->CurrentlyScripting= FALSE;
4789 /* first do the same as an InstallExecute */
4790 rc = ACTION_InstallExecute(package);
4791 if (rc != ERROR_SUCCESS)
4792 return rc;
4794 /* then handle Commit Actions */
4795 rc = execute_script(package,COMMIT_SCRIPT);
4797 return rc;
4800 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4802 static const WCHAR RunOnce[] = {
4803 'S','o','f','t','w','a','r','e','\\',
4804 'M','i','c','r','o','s','o','f','t','\\',
4805 'W','i','n','d','o','w','s','\\',
4806 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4807 'R','u','n','O','n','c','e',0};
4808 static const WCHAR InstallRunOnce[] = {
4809 'S','o','f','t','w','a','r','e','\\',
4810 'M','i','c','r','o','s','o','f','t','\\',
4811 'W','i','n','d','o','w','s','\\',
4812 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4813 'I','n','s','t','a','l','l','e','r','\\',
4814 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4816 static const WCHAR msiexec_fmt[] = {
4817 '%','s',
4818 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4819 '\"','%','s','\"',0};
4820 static const WCHAR install_fmt[] = {
4821 '/','I',' ','\"','%','s','\"',' ',
4822 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4823 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4824 WCHAR buffer[256], sysdir[MAX_PATH];
4825 HKEY hkey;
4826 WCHAR squished_pc[100];
4828 squash_guid(package->ProductCode,squished_pc);
4830 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4831 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4832 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4833 squished_pc);
4835 msi_reg_set_val_str( hkey, squished_pc, buffer );
4836 RegCloseKey(hkey);
4838 TRACE("Reboot command %s\n",debugstr_w(buffer));
4840 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4841 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4843 msi_reg_set_val_str( hkey, squished_pc, buffer );
4844 RegCloseKey(hkey);
4846 return ERROR_INSTALL_SUSPEND;
4849 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4851 DWORD attrib;
4852 UINT rc;
4855 * We are currently doing what should be done here in the top level Install
4856 * however for Administrative and uninstalls this step will be needed
4858 if (!package->PackagePath)
4859 return ERROR_SUCCESS;
4861 msi_set_sourcedir_props(package, TRUE);
4863 attrib = GetFileAttributesW(package->db->path);
4864 if (attrib == INVALID_FILE_ATTRIBUTES)
4866 LPWSTR prompt;
4867 LPWSTR msg;
4868 DWORD size = 0;
4870 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4871 package->Context, MSICODE_PRODUCT,
4872 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4873 if (rc == ERROR_MORE_DATA)
4875 prompt = msi_alloc(size * sizeof(WCHAR));
4876 MsiSourceListGetInfoW(package->ProductCode, NULL,
4877 package->Context, MSICODE_PRODUCT,
4878 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4880 else
4881 prompt = strdupW(package->db->path);
4883 msg = generate_error_string(package,1302,1,prompt);
4884 while(attrib == INVALID_FILE_ATTRIBUTES)
4886 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4887 if (rc == IDCANCEL)
4889 rc = ERROR_INSTALL_USEREXIT;
4890 break;
4892 attrib = GetFileAttributesW(package->db->path);
4894 msi_free(prompt);
4895 rc = ERROR_SUCCESS;
4897 else
4898 return ERROR_SUCCESS;
4900 return rc;
4903 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4905 HKEY hkey = 0;
4906 LPWSTR buffer, productid = NULL;
4907 UINT i, rc = ERROR_SUCCESS;
4908 MSIRECORD *uirow;
4910 static const WCHAR szPropKeys[][80] =
4912 {'P','r','o','d','u','c','t','I','D',0},
4913 {'U','S','E','R','N','A','M','E',0},
4914 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4915 {0},
4918 static const WCHAR szRegKeys[][80] =
4920 {'P','r','o','d','u','c','t','I','D',0},
4921 {'R','e','g','O','w','n','e','r',0},
4922 {'R','e','g','C','o','m','p','a','n','y',0},
4923 {0},
4926 if (msi_check_unpublish(package))
4928 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4929 goto end;
4932 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
4933 if (!productid)
4934 goto end;
4936 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4937 NULL, &hkey, TRUE);
4938 if (rc != ERROR_SUCCESS)
4939 goto end;
4941 for( i = 0; szPropKeys[i][0]; i++ )
4943 buffer = msi_dup_property( package->db, szPropKeys[i] );
4944 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4945 msi_free( buffer );
4948 end:
4949 uirow = MSI_CreateRecord( 1 );
4950 MSI_RecordSetStringW( uirow, 1, productid );
4951 ui_actiondata( package, szRegisterUser, uirow );
4952 msiobj_release( &uirow->hdr );
4954 msi_free(productid);
4955 RegCloseKey(hkey);
4956 return rc;
4960 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4962 UINT rc;
4964 package->script->InWhatSequence |= SEQUENCE_EXEC;
4965 rc = ACTION_ProcessExecSequence(package,FALSE);
4966 return rc;
4970 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4972 MSIPACKAGE *package = param;
4973 LPCWSTR compgroupid, component, feature, qualifier, text;
4974 LPWSTR advertise = NULL, output = NULL;
4975 HKEY hkey = NULL;
4976 UINT rc;
4977 MSICOMPONENT *comp;
4978 MSIFEATURE *feat;
4979 DWORD sz;
4980 MSIRECORD *uirow;
4982 feature = MSI_RecordGetString(rec, 5);
4983 feat = get_loaded_feature(package, feature);
4984 if (!feat)
4985 return ERROR_SUCCESS;
4987 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
4988 feat->ActionRequest != INSTALLSTATE_SOURCE &&
4989 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
4991 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
4992 feat->Action = feat->Installed;
4993 return ERROR_SUCCESS;
4996 component = MSI_RecordGetString(rec, 3);
4997 comp = get_loaded_component(package, component);
4998 if (!comp)
4999 return ERROR_SUCCESS;
5001 compgroupid = MSI_RecordGetString(rec,1);
5002 qualifier = MSI_RecordGetString(rec,2);
5004 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5005 if (rc != ERROR_SUCCESS)
5006 goto end;
5008 text = MSI_RecordGetString(rec,4);
5009 advertise = create_component_advertise_string(package, comp, feature);
5011 sz = strlenW(advertise);
5013 if (text)
5014 sz += lstrlenW(text);
5016 sz+=3;
5017 sz *= sizeof(WCHAR);
5019 output = msi_alloc_zero(sz);
5020 strcpyW(output,advertise);
5021 msi_free(advertise);
5023 if (text)
5024 strcatW(output,text);
5026 msi_reg_set_val_multi_str( hkey, qualifier, output );
5028 end:
5029 RegCloseKey(hkey);
5030 msi_free(output);
5032 /* the UI chunk */
5033 uirow = MSI_CreateRecord( 2 );
5034 MSI_RecordSetStringW( uirow, 1, compgroupid );
5035 MSI_RecordSetStringW( uirow, 2, qualifier);
5036 ui_actiondata( package, szPublishComponents, uirow);
5037 msiobj_release( &uirow->hdr );
5038 /* FIXME: call ui_progress? */
5040 return rc;
5044 * At present I am ignorning the advertised components part of this and only
5045 * focusing on the qualified component sets
5047 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5049 UINT rc;
5050 MSIQUERY * view;
5051 static const WCHAR ExecSeqQuery[] =
5052 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5053 '`','P','u','b','l','i','s','h',
5054 'C','o','m','p','o','n','e','n','t','`',0};
5056 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5057 if (rc != ERROR_SUCCESS)
5058 return ERROR_SUCCESS;
5060 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5061 msiobj_release(&view->hdr);
5063 return rc;
5066 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5068 static const WCHAR szInstallerComponents[] = {
5069 'S','o','f','t','w','a','r','e','\\',
5070 'M','i','c','r','o','s','o','f','t','\\',
5071 'I','n','s','t','a','l','l','e','r','\\',
5072 'C','o','m','p','o','n','e','n','t','s','\\',0};
5074 MSIPACKAGE *package = param;
5075 LPCWSTR compgroupid, component, feature, qualifier;
5076 MSICOMPONENT *comp;
5077 MSIFEATURE *feat;
5078 MSIRECORD *uirow;
5079 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5080 LONG res;
5082 feature = MSI_RecordGetString( rec, 5 );
5083 feat = get_loaded_feature( package, feature );
5084 if (!feat)
5085 return ERROR_SUCCESS;
5087 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5089 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5090 feat->Action = feat->Installed;
5091 return ERROR_SUCCESS;
5094 component = MSI_RecordGetString( rec, 3 );
5095 comp = get_loaded_component( package, component );
5096 if (!comp)
5097 return ERROR_SUCCESS;
5099 compgroupid = MSI_RecordGetString( rec, 1 );
5100 qualifier = MSI_RecordGetString( rec, 2 );
5102 squash_guid( compgroupid, squashed );
5103 strcpyW( keypath, szInstallerComponents );
5104 strcatW( keypath, squashed );
5106 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5107 if (res != ERROR_SUCCESS)
5109 WARN("Unable to delete component key %d\n", res);
5112 uirow = MSI_CreateRecord( 2 );
5113 MSI_RecordSetStringW( uirow, 1, compgroupid );
5114 MSI_RecordSetStringW( uirow, 2, qualifier );
5115 ui_actiondata( package, szUnpublishComponents, uirow );
5116 msiobj_release( &uirow->hdr );
5118 return ERROR_SUCCESS;
5121 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5123 UINT rc;
5124 MSIQUERY *view;
5125 static const WCHAR query[] =
5126 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5127 '`','P','u','b','l','i','s','h',
5128 'C','o','m','p','o','n','e','n','t','`',0};
5130 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5131 if (rc != ERROR_SUCCESS)
5132 return ERROR_SUCCESS;
5134 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5135 msiobj_release( &view->hdr );
5137 return rc;
5140 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5142 MSIPACKAGE *package = param;
5143 MSIRECORD *row;
5144 MSIFILE *file;
5145 SC_HANDLE hscm, service = NULL;
5146 LPCWSTR comp, depends, pass;
5147 LPWSTR name = NULL, disp = NULL;
5148 LPCWSTR load_order, serv_name, key;
5149 DWORD serv_type, start_type;
5150 DWORD err_control;
5152 static const WCHAR query[] =
5153 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5154 '`','C','o','m','p','o','n','e','n','t','`',' ',
5155 'W','H','E','R','E',' ',
5156 '`','C','o','m','p','o','n','e','n','t','`',' ',
5157 '=','\'','%','s','\'',0};
5159 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5160 if (!hscm)
5162 ERR("Failed to open the SC Manager!\n");
5163 goto done;
5166 start_type = MSI_RecordGetInteger(rec, 5);
5167 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5168 goto done;
5170 depends = MSI_RecordGetString(rec, 8);
5171 if (depends && *depends)
5172 FIXME("Dependency list unhandled!\n");
5174 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5175 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5176 serv_type = MSI_RecordGetInteger(rec, 4);
5177 err_control = MSI_RecordGetInteger(rec, 6);
5178 load_order = MSI_RecordGetString(rec, 7);
5179 serv_name = MSI_RecordGetString(rec, 9);
5180 pass = MSI_RecordGetString(rec, 10);
5181 comp = MSI_RecordGetString(rec, 12);
5183 /* fetch the service path */
5184 row = MSI_QueryGetRecord(package->db, query, comp);
5185 if (!row)
5187 ERR("Control query failed!\n");
5188 goto done;
5191 key = MSI_RecordGetString(row, 6);
5193 file = get_loaded_file(package, key);
5194 msiobj_release(&row->hdr);
5195 if (!file)
5197 ERR("Failed to load the service file\n");
5198 goto done;
5201 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5202 start_type, err_control, file->TargetPath,
5203 load_order, NULL, NULL, serv_name, pass);
5204 if (!service)
5206 if (GetLastError() != ERROR_SERVICE_EXISTS)
5207 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5210 done:
5211 CloseServiceHandle(service);
5212 CloseServiceHandle(hscm);
5213 msi_free(name);
5214 msi_free(disp);
5216 return ERROR_SUCCESS;
5219 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5221 UINT rc;
5222 MSIQUERY * view;
5223 static const WCHAR ExecSeqQuery[] =
5224 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5225 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5227 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5228 if (rc != ERROR_SUCCESS)
5229 return ERROR_SUCCESS;
5231 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5232 msiobj_release(&view->hdr);
5234 return rc;
5237 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5238 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5240 LPCWSTR *vector, *temp_vector;
5241 LPWSTR p, q;
5242 DWORD sep_len;
5244 static const WCHAR separator[] = {'[','~',']',0};
5246 *numargs = 0;
5247 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5249 if (!args)
5250 return NULL;
5252 vector = msi_alloc(sizeof(LPWSTR));
5253 if (!vector)
5254 return NULL;
5256 p = args;
5259 (*numargs)++;
5260 vector[*numargs - 1] = p;
5262 if ((q = strstrW(p, separator)))
5264 *q = '\0';
5266 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5267 if (!temp_vector)
5269 msi_free(vector);
5270 return NULL;
5272 vector = temp_vector;
5274 p = q + sep_len;
5276 } while (q);
5278 return vector;
5281 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5283 MSIPACKAGE *package = param;
5284 MSICOMPONENT *comp;
5285 MSIRECORD *uirow;
5286 SC_HANDLE scm = NULL, service = NULL;
5287 LPCWSTR component, *vector = NULL;
5288 LPWSTR name, args, display_name = NULL;
5289 DWORD event, numargs, len;
5290 UINT r = ERROR_FUNCTION_FAILED;
5292 component = MSI_RecordGetString(rec, 6);
5293 comp = get_loaded_component(package, component);
5294 if (!comp)
5295 return ERROR_SUCCESS;
5297 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5299 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5300 comp->Action = comp->Installed;
5301 return ERROR_SUCCESS;
5303 comp->Action = INSTALLSTATE_LOCAL;
5305 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5306 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5307 event = MSI_RecordGetInteger(rec, 3);
5309 if (!(event & msidbServiceControlEventStart))
5311 r = ERROR_SUCCESS;
5312 goto done;
5315 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5316 if (!scm)
5318 ERR("Failed to open the service control manager\n");
5319 goto done;
5322 len = 0;
5323 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5324 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5326 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5327 GetServiceDisplayNameW( scm, name, display_name, &len );
5330 service = OpenServiceW(scm, name, SERVICE_START);
5331 if (!service)
5333 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5334 goto done;
5337 vector = msi_service_args_to_vector(args, &numargs);
5339 if (!StartServiceW(service, numargs, vector) &&
5340 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5342 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5343 goto done;
5346 r = ERROR_SUCCESS;
5348 done:
5349 uirow = MSI_CreateRecord( 2 );
5350 MSI_RecordSetStringW( uirow, 1, display_name );
5351 MSI_RecordSetStringW( uirow, 2, name );
5352 ui_actiondata( package, szStartServices, uirow );
5353 msiobj_release( &uirow->hdr );
5355 CloseServiceHandle(service);
5356 CloseServiceHandle(scm);
5358 msi_free(name);
5359 msi_free(args);
5360 msi_free(vector);
5361 msi_free(display_name);
5362 return r;
5365 static UINT ACTION_StartServices( MSIPACKAGE *package )
5367 UINT rc;
5368 MSIQUERY *view;
5370 static const WCHAR query[] = {
5371 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5372 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5374 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5375 if (rc != ERROR_SUCCESS)
5376 return ERROR_SUCCESS;
5378 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5379 msiobj_release(&view->hdr);
5381 return rc;
5384 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5386 DWORD i, needed, count;
5387 ENUM_SERVICE_STATUSW *dependencies;
5388 SERVICE_STATUS ss;
5389 SC_HANDLE depserv;
5391 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5392 0, &needed, &count))
5393 return TRUE;
5395 if (GetLastError() != ERROR_MORE_DATA)
5396 return FALSE;
5398 dependencies = msi_alloc(needed);
5399 if (!dependencies)
5400 return FALSE;
5402 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5403 needed, &needed, &count))
5404 goto error;
5406 for (i = 0; i < count; i++)
5408 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5409 SERVICE_STOP | SERVICE_QUERY_STATUS);
5410 if (!depserv)
5411 goto error;
5413 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5414 goto error;
5417 return TRUE;
5419 error:
5420 msi_free(dependencies);
5421 return FALSE;
5424 static UINT stop_service( LPCWSTR name )
5426 SC_HANDLE scm = NULL, service = NULL;
5427 SERVICE_STATUS status;
5428 SERVICE_STATUS_PROCESS ssp;
5429 DWORD needed;
5431 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5432 if (!scm)
5434 WARN("Failed to open the SCM: %d\n", GetLastError());
5435 goto done;
5438 service = OpenServiceW(scm, name,
5439 SERVICE_STOP |
5440 SERVICE_QUERY_STATUS |
5441 SERVICE_ENUMERATE_DEPENDENTS);
5442 if (!service)
5444 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5445 goto done;
5448 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5449 sizeof(SERVICE_STATUS_PROCESS), &needed))
5451 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5452 goto done;
5455 if (ssp.dwCurrentState == SERVICE_STOPPED)
5456 goto done;
5458 stop_service_dependents(scm, service);
5460 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5461 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5463 done:
5464 CloseServiceHandle(service);
5465 CloseServiceHandle(scm);
5467 return ERROR_SUCCESS;
5470 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5472 MSIPACKAGE *package = param;
5473 MSICOMPONENT *comp;
5474 MSIRECORD *uirow;
5475 LPCWSTR component;
5476 LPWSTR name = NULL, display_name = NULL;
5477 DWORD event, len;
5478 SC_HANDLE scm;
5480 event = MSI_RecordGetInteger( rec, 3 );
5481 if (!(event & msidbServiceControlEventStop))
5482 return ERROR_SUCCESS;
5484 component = MSI_RecordGetString( rec, 6 );
5485 comp = get_loaded_component( package, component );
5486 if (!comp)
5487 return ERROR_SUCCESS;
5489 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5491 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5492 comp->Action = comp->Installed;
5493 return ERROR_SUCCESS;
5495 comp->Action = INSTALLSTATE_ABSENT;
5497 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5498 if (!scm)
5500 ERR("Failed to open the service control manager\n");
5501 goto done;
5504 len = 0;
5505 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5506 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5508 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5509 GetServiceDisplayNameW( scm, name, display_name, &len );
5511 CloseServiceHandle( scm );
5513 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5514 stop_service( name );
5516 done:
5517 uirow = MSI_CreateRecord( 2 );
5518 MSI_RecordSetStringW( uirow, 1, display_name );
5519 MSI_RecordSetStringW( uirow, 2, name );
5520 ui_actiondata( package, szStopServices, uirow );
5521 msiobj_release( &uirow->hdr );
5523 msi_free( name );
5524 msi_free( display_name );
5525 return ERROR_SUCCESS;
5528 static UINT ACTION_StopServices( MSIPACKAGE *package )
5530 UINT rc;
5531 MSIQUERY *view;
5533 static const WCHAR query[] = {
5534 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5535 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5537 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5538 if (rc != ERROR_SUCCESS)
5539 return ERROR_SUCCESS;
5541 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5542 msiobj_release(&view->hdr);
5544 return rc;
5547 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5549 MSIPACKAGE *package = param;
5550 MSICOMPONENT *comp;
5551 MSIRECORD *uirow;
5552 LPCWSTR component;
5553 LPWSTR name = NULL, display_name = NULL;
5554 DWORD event, len;
5555 SC_HANDLE scm = NULL, service = NULL;
5557 event = MSI_RecordGetInteger( rec, 3 );
5558 if (!(event & msidbServiceControlEventDelete))
5559 return ERROR_SUCCESS;
5561 component = MSI_RecordGetString(rec, 6);
5562 comp = get_loaded_component(package, component);
5563 if (!comp)
5564 return ERROR_SUCCESS;
5566 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5568 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5569 comp->Action = comp->Installed;
5570 return ERROR_SUCCESS;
5572 comp->Action = INSTALLSTATE_ABSENT;
5574 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5575 stop_service( name );
5577 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5578 if (!scm)
5580 WARN("Failed to open the SCM: %d\n", GetLastError());
5581 goto done;
5584 len = 0;
5585 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5586 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5588 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5589 GetServiceDisplayNameW( scm, name, display_name, &len );
5592 service = OpenServiceW( scm, name, DELETE );
5593 if (!service)
5595 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5596 goto done;
5599 if (!DeleteService( service ))
5600 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5602 done:
5603 uirow = MSI_CreateRecord( 2 );
5604 MSI_RecordSetStringW( uirow, 1, display_name );
5605 MSI_RecordSetStringW( uirow, 2, name );
5606 ui_actiondata( package, szDeleteServices, uirow );
5607 msiobj_release( &uirow->hdr );
5609 CloseServiceHandle( service );
5610 CloseServiceHandle( scm );
5611 msi_free( name );
5612 msi_free( display_name );
5614 return ERROR_SUCCESS;
5617 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5619 UINT rc;
5620 MSIQUERY *view;
5622 static const WCHAR query[] = {
5623 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5624 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5626 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5627 if (rc != ERROR_SUCCESS)
5628 return ERROR_SUCCESS;
5630 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5631 msiobj_release( &view->hdr );
5633 return rc;
5636 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5638 MSIFILE *file;
5640 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5642 if (!lstrcmpW(file->File, filename))
5643 return file;
5646 return NULL;
5649 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5651 MSIPACKAGE *package = param;
5652 LPWSTR driver, driver_path, ptr;
5653 WCHAR outpath[MAX_PATH];
5654 MSIFILE *driver_file, *setup_file;
5655 MSIRECORD *uirow;
5656 LPCWSTR desc;
5657 DWORD len, usage;
5658 UINT r = ERROR_SUCCESS;
5660 static const WCHAR driver_fmt[] = {
5661 'D','r','i','v','e','r','=','%','s',0};
5662 static const WCHAR setup_fmt[] = {
5663 'S','e','t','u','p','=','%','s',0};
5664 static const WCHAR usage_fmt[] = {
5665 'F','i','l','e','U','s','a','g','e','=','1',0};
5667 desc = MSI_RecordGetString(rec, 3);
5669 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5670 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5672 if (!driver_file)
5674 ERR("ODBC Driver entry not found!\n");
5675 return ERROR_FUNCTION_FAILED;
5678 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5679 if (setup_file)
5680 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5681 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5683 driver = msi_alloc(len * sizeof(WCHAR));
5684 if (!driver)
5685 return ERROR_OUTOFMEMORY;
5687 ptr = driver;
5688 lstrcpyW(ptr, desc);
5689 ptr += lstrlenW(ptr) + 1;
5691 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5692 ptr += len + 1;
5694 if (setup_file)
5696 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5697 ptr += len + 1;
5700 lstrcpyW(ptr, usage_fmt);
5701 ptr += lstrlenW(ptr) + 1;
5702 *ptr = '\0';
5704 driver_path = strdupW(driver_file->TargetPath);
5705 ptr = strrchrW(driver_path, '\\');
5706 if (ptr) *ptr = '\0';
5708 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5709 NULL, ODBC_INSTALL_COMPLETE, &usage))
5711 ERR("Failed to install SQL driver!\n");
5712 r = ERROR_FUNCTION_FAILED;
5715 uirow = MSI_CreateRecord( 5 );
5716 MSI_RecordSetStringW( uirow, 1, desc );
5717 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5718 MSI_RecordSetStringW( uirow, 3, driver_path );
5719 ui_actiondata( package, szInstallODBC, uirow );
5720 msiobj_release( &uirow->hdr );
5722 msi_free(driver);
5723 msi_free(driver_path);
5725 return r;
5728 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5730 MSIPACKAGE *package = param;
5731 LPWSTR translator, translator_path, ptr;
5732 WCHAR outpath[MAX_PATH];
5733 MSIFILE *translator_file, *setup_file;
5734 MSIRECORD *uirow;
5735 LPCWSTR desc;
5736 DWORD len, usage;
5737 UINT r = ERROR_SUCCESS;
5739 static const WCHAR translator_fmt[] = {
5740 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5741 static const WCHAR setup_fmt[] = {
5742 'S','e','t','u','p','=','%','s',0};
5744 desc = MSI_RecordGetString(rec, 3);
5746 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5747 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5749 if (!translator_file)
5751 ERR("ODBC Translator entry not found!\n");
5752 return ERROR_FUNCTION_FAILED;
5755 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5756 if (setup_file)
5757 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5759 translator = msi_alloc(len * sizeof(WCHAR));
5760 if (!translator)
5761 return ERROR_OUTOFMEMORY;
5763 ptr = translator;
5764 lstrcpyW(ptr, desc);
5765 ptr += lstrlenW(ptr) + 1;
5767 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5768 ptr += len + 1;
5770 if (setup_file)
5772 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5773 ptr += len + 1;
5775 *ptr = '\0';
5777 translator_path = strdupW(translator_file->TargetPath);
5778 ptr = strrchrW(translator_path, '\\');
5779 if (ptr) *ptr = '\0';
5781 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5782 NULL, ODBC_INSTALL_COMPLETE, &usage))
5784 ERR("Failed to install SQL translator!\n");
5785 r = ERROR_FUNCTION_FAILED;
5788 uirow = MSI_CreateRecord( 5 );
5789 MSI_RecordSetStringW( uirow, 1, desc );
5790 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5791 MSI_RecordSetStringW( uirow, 3, translator_path );
5792 ui_actiondata( package, szInstallODBC, uirow );
5793 msiobj_release( &uirow->hdr );
5795 msi_free(translator);
5796 msi_free(translator_path);
5798 return r;
5801 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5803 MSIPACKAGE *package = param;
5804 LPWSTR attrs;
5805 LPCWSTR desc, driver;
5806 WORD request = ODBC_ADD_SYS_DSN;
5807 INT registration;
5808 DWORD len;
5809 UINT r = ERROR_SUCCESS;
5810 MSIRECORD *uirow;
5812 static const WCHAR attrs_fmt[] = {
5813 'D','S','N','=','%','s',0 };
5815 desc = MSI_RecordGetString(rec, 3);
5816 driver = MSI_RecordGetString(rec, 4);
5817 registration = MSI_RecordGetInteger(rec, 5);
5819 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5820 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5822 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5823 attrs = msi_alloc(len * sizeof(WCHAR));
5824 if (!attrs)
5825 return ERROR_OUTOFMEMORY;
5827 len = sprintfW(attrs, attrs_fmt, desc);
5828 attrs[len + 1] = 0;
5830 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5832 ERR("Failed to install SQL data source!\n");
5833 r = ERROR_FUNCTION_FAILED;
5836 uirow = MSI_CreateRecord( 5 );
5837 MSI_RecordSetStringW( uirow, 1, desc );
5838 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5839 MSI_RecordSetInteger( uirow, 3, request );
5840 ui_actiondata( package, szInstallODBC, uirow );
5841 msiobj_release( &uirow->hdr );
5843 msi_free(attrs);
5845 return r;
5848 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5850 UINT rc;
5851 MSIQUERY *view;
5853 static const WCHAR driver_query[] = {
5854 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5855 'O','D','B','C','D','r','i','v','e','r',0 };
5857 static const WCHAR translator_query[] = {
5858 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5859 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5861 static const WCHAR source_query[] = {
5862 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5863 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5865 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5866 if (rc != ERROR_SUCCESS)
5867 return ERROR_SUCCESS;
5869 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5870 msiobj_release(&view->hdr);
5872 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5873 if (rc != ERROR_SUCCESS)
5874 return ERROR_SUCCESS;
5876 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5877 msiobj_release(&view->hdr);
5879 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5880 if (rc != ERROR_SUCCESS)
5881 return ERROR_SUCCESS;
5883 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5884 msiobj_release(&view->hdr);
5886 return rc;
5889 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5891 MSIPACKAGE *package = param;
5892 MSIRECORD *uirow;
5893 DWORD usage;
5894 LPCWSTR desc;
5896 desc = MSI_RecordGetString( rec, 3 );
5897 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5899 WARN("Failed to remove ODBC driver\n");
5901 else if (!usage)
5903 FIXME("Usage count reached 0\n");
5906 uirow = MSI_CreateRecord( 2 );
5907 MSI_RecordSetStringW( uirow, 1, desc );
5908 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5909 ui_actiondata( package, szRemoveODBC, uirow );
5910 msiobj_release( &uirow->hdr );
5912 return ERROR_SUCCESS;
5915 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5917 MSIPACKAGE *package = param;
5918 MSIRECORD *uirow;
5919 DWORD usage;
5920 LPCWSTR desc;
5922 desc = MSI_RecordGetString( rec, 3 );
5923 if (!SQLRemoveTranslatorW( desc, &usage ))
5925 WARN("Failed to remove ODBC translator\n");
5927 else if (!usage)
5929 FIXME("Usage count reached 0\n");
5932 uirow = MSI_CreateRecord( 2 );
5933 MSI_RecordSetStringW( uirow, 1, desc );
5934 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5935 ui_actiondata( package, szRemoveODBC, uirow );
5936 msiobj_release( &uirow->hdr );
5938 return ERROR_SUCCESS;
5941 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5943 MSIPACKAGE *package = param;
5944 MSIRECORD *uirow;
5945 LPWSTR attrs;
5946 LPCWSTR desc, driver;
5947 WORD request = ODBC_REMOVE_SYS_DSN;
5948 INT registration;
5949 DWORD len;
5951 static const WCHAR attrs_fmt[] = {
5952 'D','S','N','=','%','s',0 };
5954 desc = MSI_RecordGetString( rec, 3 );
5955 driver = MSI_RecordGetString( rec, 4 );
5956 registration = MSI_RecordGetInteger( rec, 5 );
5958 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
5959 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
5961 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
5962 attrs = msi_alloc( len * sizeof(WCHAR) );
5963 if (!attrs)
5964 return ERROR_OUTOFMEMORY;
5966 FIXME("Use ODBCSourceAttribute table\n");
5968 len = sprintfW( attrs, attrs_fmt, desc );
5969 attrs[len + 1] = 0;
5971 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
5973 WARN("Failed to remove ODBC data source\n");
5975 msi_free( attrs );
5977 uirow = MSI_CreateRecord( 3 );
5978 MSI_RecordSetStringW( uirow, 1, desc );
5979 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5980 MSI_RecordSetInteger( uirow, 3, request );
5981 ui_actiondata( package, szRemoveODBC, uirow );
5982 msiobj_release( &uirow->hdr );
5984 return ERROR_SUCCESS;
5987 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5989 UINT rc;
5990 MSIQUERY *view;
5992 static const WCHAR driver_query[] = {
5993 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5994 'O','D','B','C','D','r','i','v','e','r',0 };
5996 static const WCHAR translator_query[] = {
5997 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5998 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6000 static const WCHAR source_query[] = {
6001 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6002 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6004 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6005 if (rc != ERROR_SUCCESS)
6006 return ERROR_SUCCESS;
6008 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6009 msiobj_release( &view->hdr );
6011 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6012 if (rc != ERROR_SUCCESS)
6013 return ERROR_SUCCESS;
6015 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6016 msiobj_release( &view->hdr );
6018 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6019 if (rc != ERROR_SUCCESS)
6020 return ERROR_SUCCESS;
6022 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6023 msiobj_release( &view->hdr );
6025 return rc;
6028 #define ENV_ACT_SETALWAYS 0x1
6029 #define ENV_ACT_SETABSENT 0x2
6030 #define ENV_ACT_REMOVE 0x4
6031 #define ENV_ACT_REMOVEMATCH 0x8
6033 #define ENV_MOD_MACHINE 0x20000000
6034 #define ENV_MOD_APPEND 0x40000000
6035 #define ENV_MOD_PREFIX 0x80000000
6036 #define ENV_MOD_MASK 0xC0000000
6038 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6040 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6042 LPCWSTR cptr = *name;
6044 static const WCHAR prefix[] = {'[','~',']',0};
6045 static const int prefix_len = 3;
6047 *flags = 0;
6048 while (*cptr)
6050 if (*cptr == '=')
6051 *flags |= ENV_ACT_SETALWAYS;
6052 else if (*cptr == '+')
6053 *flags |= ENV_ACT_SETABSENT;
6054 else if (*cptr == '-')
6055 *flags |= ENV_ACT_REMOVE;
6056 else if (*cptr == '!')
6057 *flags |= ENV_ACT_REMOVEMATCH;
6058 else if (*cptr == '*')
6059 *flags |= ENV_MOD_MACHINE;
6060 else
6061 break;
6063 cptr++;
6064 (*name)++;
6067 if (!*cptr)
6069 ERR("Missing environment variable\n");
6070 return ERROR_FUNCTION_FAILED;
6073 if (*value)
6075 LPCWSTR ptr = *value;
6076 if (!strncmpW(ptr, prefix, prefix_len))
6078 if (ptr[prefix_len] == szSemiColon[0])
6080 *flags |= ENV_MOD_APPEND;
6081 *value += lstrlenW(prefix);
6083 else
6085 *value = NULL;
6088 else if (lstrlenW(*value) >= prefix_len)
6090 ptr += lstrlenW(ptr) - prefix_len;
6091 if (!lstrcmpW(ptr, prefix))
6093 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6095 *flags |= ENV_MOD_PREFIX;
6096 /* the "[~]" will be removed by deformat_string */;
6098 else
6100 *value = NULL;
6106 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6107 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6108 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6109 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6111 ERR("Invalid flags: %08x\n", *flags);
6112 return ERROR_FUNCTION_FAILED;
6115 if (!*flags)
6116 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6118 return ERROR_SUCCESS;
6121 static UINT open_env_key( DWORD flags, HKEY *key )
6123 static const WCHAR user_env[] =
6124 {'E','n','v','i','r','o','n','m','e','n','t',0};
6125 static const WCHAR machine_env[] =
6126 {'S','y','s','t','e','m','\\',
6127 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6128 'C','o','n','t','r','o','l','\\',
6129 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6130 'E','n','v','i','r','o','n','m','e','n','t',0};
6131 const WCHAR *env;
6132 HKEY root;
6133 LONG res;
6135 if (flags & ENV_MOD_MACHINE)
6137 env = machine_env;
6138 root = HKEY_LOCAL_MACHINE;
6140 else
6142 env = user_env;
6143 root = HKEY_CURRENT_USER;
6146 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6147 if (res != ERROR_SUCCESS)
6149 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6150 return ERROR_FUNCTION_FAILED;
6153 return ERROR_SUCCESS;
6156 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6158 MSIPACKAGE *package = param;
6159 LPCWSTR name, value, component;
6160 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6161 DWORD flags, type, size;
6162 UINT res;
6163 HKEY env = NULL;
6164 MSICOMPONENT *comp;
6165 MSIRECORD *uirow;
6166 int action = 0;
6168 component = MSI_RecordGetString(rec, 4);
6169 comp = get_loaded_component(package, component);
6170 if (!comp)
6171 return ERROR_SUCCESS;
6173 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6175 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6176 comp->Action = comp->Installed;
6177 return ERROR_SUCCESS;
6179 comp->Action = INSTALLSTATE_LOCAL;
6181 name = MSI_RecordGetString(rec, 2);
6182 value = MSI_RecordGetString(rec, 3);
6184 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6186 res = env_parse_flags(&name, &value, &flags);
6187 if (res != ERROR_SUCCESS || !value)
6188 goto done;
6190 if (value && !deformat_string(package, value, &deformatted))
6192 res = ERROR_OUTOFMEMORY;
6193 goto done;
6196 value = deformatted;
6198 res = open_env_key( flags, &env );
6199 if (res != ERROR_SUCCESS)
6200 goto done;
6202 if (flags & ENV_MOD_MACHINE)
6203 action |= 0x20000000;
6205 size = 0;
6206 type = REG_SZ;
6207 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6208 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6209 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6210 goto done;
6212 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6214 action = 0x2;
6216 /* Nothing to do. */
6217 if (!value)
6219 res = ERROR_SUCCESS;
6220 goto done;
6223 /* If we are appending but the string was empty, strip ; */
6224 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6226 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6227 newval = strdupW(value);
6228 if (!newval)
6230 res = ERROR_OUTOFMEMORY;
6231 goto done;
6234 else
6236 action = 0x1;
6238 /* Contrary to MSDN, +-variable to [~];path works */
6239 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6241 res = ERROR_SUCCESS;
6242 goto done;
6245 data = msi_alloc(size);
6246 if (!data)
6248 RegCloseKey(env);
6249 return ERROR_OUTOFMEMORY;
6252 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6253 if (res != ERROR_SUCCESS)
6254 goto done;
6256 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
6258 action = 0x4;
6259 res = RegDeleteValueW(env, name);
6260 if (res != ERROR_SUCCESS)
6261 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6262 goto done;
6265 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6266 if (flags & ENV_MOD_MASK)
6268 DWORD mod_size;
6269 int multiplier = 0;
6270 if (flags & ENV_MOD_APPEND) multiplier++;
6271 if (flags & ENV_MOD_PREFIX) multiplier++;
6272 mod_size = lstrlenW(value) * multiplier;
6273 size += mod_size * sizeof(WCHAR);
6276 newval = msi_alloc(size);
6277 ptr = newval;
6278 if (!newval)
6280 res = ERROR_OUTOFMEMORY;
6281 goto done;
6284 if (flags & ENV_MOD_PREFIX)
6286 lstrcpyW(newval, value);
6287 ptr = newval + lstrlenW(value);
6288 action |= 0x80000000;
6291 lstrcpyW(ptr, data);
6293 if (flags & ENV_MOD_APPEND)
6295 lstrcatW(newval, value);
6296 action |= 0x40000000;
6299 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6300 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6301 if (res)
6303 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6306 done:
6307 uirow = MSI_CreateRecord( 3 );
6308 MSI_RecordSetStringW( uirow, 1, name );
6309 MSI_RecordSetStringW( uirow, 2, newval );
6310 MSI_RecordSetInteger( uirow, 3, action );
6311 ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6312 msiobj_release( &uirow->hdr );
6314 if (env) RegCloseKey(env);
6315 msi_free(deformatted);
6316 msi_free(data);
6317 msi_free(newval);
6318 return res;
6321 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6323 UINT rc;
6324 MSIQUERY * view;
6325 static const WCHAR ExecSeqQuery[] =
6326 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6327 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6328 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6329 if (rc != ERROR_SUCCESS)
6330 return ERROR_SUCCESS;
6332 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6333 msiobj_release(&view->hdr);
6335 return rc;
6338 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6340 MSIPACKAGE *package = param;
6341 LPCWSTR name, value, component;
6342 LPWSTR deformatted = NULL;
6343 DWORD flags;
6344 HKEY env;
6345 MSICOMPONENT *comp;
6346 MSIRECORD *uirow;
6347 int action = 0;
6348 LONG res;
6349 UINT r;
6351 component = MSI_RecordGetString( rec, 4 );
6352 comp = get_loaded_component( package, component );
6353 if (!comp)
6354 return ERROR_SUCCESS;
6356 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6358 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6359 comp->Action = comp->Installed;
6360 return ERROR_SUCCESS;
6362 comp->Action = INSTALLSTATE_ABSENT;
6364 name = MSI_RecordGetString( rec, 2 );
6365 value = MSI_RecordGetString( rec, 3 );
6367 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6369 r = env_parse_flags( &name, &value, &flags );
6370 if (r != ERROR_SUCCESS)
6371 return r;
6373 if (!(flags & ENV_ACT_REMOVE))
6375 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6376 return ERROR_SUCCESS;
6379 if (value && !deformat_string( package, value, &deformatted ))
6380 return ERROR_OUTOFMEMORY;
6382 value = deformatted;
6384 r = open_env_key( flags, &env );
6385 if (r != ERROR_SUCCESS)
6387 r = ERROR_SUCCESS;
6388 goto done;
6391 if (flags & ENV_MOD_MACHINE)
6392 action |= 0x20000000;
6394 TRACE("Removing %s\n", debugstr_w(name));
6396 res = RegDeleteValueW( env, name );
6397 if (res != ERROR_SUCCESS)
6399 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6400 r = ERROR_SUCCESS;
6403 done:
6404 uirow = MSI_CreateRecord( 3 );
6405 MSI_RecordSetStringW( uirow, 1, name );
6406 MSI_RecordSetStringW( uirow, 2, value );
6407 MSI_RecordSetInteger( uirow, 3, action );
6408 ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6409 msiobj_release( &uirow->hdr );
6411 if (env) RegCloseKey( env );
6412 msi_free( deformatted );
6413 return r;
6416 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6418 UINT rc;
6419 MSIQUERY *view;
6420 static const WCHAR query[] =
6421 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6422 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6424 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6425 if (rc != ERROR_SUCCESS)
6426 return ERROR_SUCCESS;
6428 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6429 msiobj_release( &view->hdr );
6431 return rc;
6434 typedef struct tagMSIASSEMBLY
6436 struct list entry;
6437 MSICOMPONENT *component;
6438 MSIFEATURE *feature;
6439 MSIFILE *file;
6440 LPWSTR manifest;
6441 LPWSTR application;
6442 LPWSTR display_name;
6443 DWORD attributes;
6444 BOOL installed;
6445 } MSIASSEMBLY;
6447 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6448 DWORD dwReserved);
6449 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6450 LPVOID pvReserved, HMODULE *phModDll);
6452 static BOOL init_functionpointers(void)
6454 HRESULT hr;
6455 HMODULE hfusion;
6456 HMODULE hmscoree;
6458 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6460 hmscoree = LoadLibraryA("mscoree.dll");
6461 if (!hmscoree)
6463 WARN("mscoree.dll not available\n");
6464 return FALSE;
6467 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6468 if (!pLoadLibraryShim)
6470 WARN("LoadLibraryShim not available\n");
6471 FreeLibrary(hmscoree);
6472 return FALSE;
6475 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6476 if (FAILED(hr))
6478 WARN("fusion.dll not available\n");
6479 FreeLibrary(hmscoree);
6480 return FALSE;
6483 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6485 FreeLibrary(hmscoree);
6486 return TRUE;
6489 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6490 LPWSTR path)
6492 IAssemblyCache *cache;
6493 MSIRECORD *uirow;
6494 HRESULT hr;
6495 UINT r = ERROR_FUNCTION_FAILED;
6497 TRACE("installing assembly: %s\n", debugstr_w(path));
6499 uirow = MSI_CreateRecord( 2 );
6500 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
6501 ui_actiondata( package, szMsiPublishAssemblies, uirow );
6502 msiobj_release( &uirow->hdr );
6504 if (assembly->feature)
6505 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6507 if (assembly->manifest)
6508 FIXME("Manifest unhandled\n");
6510 if (assembly->application)
6512 FIXME("Assembly should be privately installed\n");
6513 return ERROR_SUCCESS;
6516 if (assembly->attributes == msidbAssemblyAttributesWin32)
6518 FIXME("Win32 assemblies not handled\n");
6519 return ERROR_SUCCESS;
6522 hr = pCreateAssemblyCache(&cache, 0);
6523 if (FAILED(hr))
6524 goto done;
6526 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6527 if (FAILED(hr))
6528 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6530 r = ERROR_SUCCESS;
6532 done:
6533 IAssemblyCache_Release(cache);
6534 return r;
6537 typedef struct tagASSEMBLY_LIST
6539 MSIPACKAGE *package;
6540 IAssemblyCache *cache;
6541 struct list *assemblies;
6542 } ASSEMBLY_LIST;
6544 typedef struct tagASSEMBLY_NAME
6546 LPWSTR name;
6547 LPWSTR version;
6548 LPWSTR culture;
6549 LPWSTR pubkeytoken;
6550 } ASSEMBLY_NAME;
6552 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6554 ASSEMBLY_NAME *asmname = param;
6555 LPCWSTR name = MSI_RecordGetString(rec, 2);
6556 LPWSTR val = msi_dup_record_field(rec, 3);
6558 static const WCHAR Name[] = {'N','a','m','e',0};
6559 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6560 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6561 static const WCHAR PublicKeyToken[] = {
6562 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6564 if (!strcmpiW(name, Name))
6565 asmname->name = val;
6566 else if (!strcmpiW(name, Version))
6567 asmname->version = val;
6568 else if (!strcmpiW(name, Culture))
6569 asmname->culture = val;
6570 else if (!strcmpiW(name, PublicKeyToken))
6571 asmname->pubkeytoken = val;
6572 else
6573 msi_free(val);
6575 return ERROR_SUCCESS;
6578 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6580 if (!*str)
6582 *size = lstrlenW(append) + 1;
6583 *str = msi_alloc((*size) * sizeof(WCHAR));
6584 lstrcpyW(*str, append);
6585 return;
6588 (*size) += lstrlenW(append);
6589 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6590 lstrcatW(*str, append);
6593 static WCHAR *get_assembly_display_name( MSIDATABASE *db, MSICOMPONENT *comp )
6595 static const WCHAR separator[] = {',',' ',0};
6596 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6597 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6598 static const WCHAR PublicKeyToken[] = {'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6599 static const WCHAR query[] = {
6600 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6601 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6602 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6603 '=','\'','%','s','\'',0};
6604 ASSEMBLY_NAME name;
6605 MSIQUERY *view;
6606 LPWSTR display_name;
6607 DWORD size;
6608 UINT r;
6610 display_name = NULL;
6611 memset( &name, 0, sizeof(ASSEMBLY_NAME) );
6613 r = MSI_OpenQuery( db, &view, query, comp->Component );
6614 if (r != ERROR_SUCCESS)
6615 return NULL;
6617 MSI_IterateRecords( view, NULL, parse_assembly_name, &name );
6618 msiobj_release( &view->hdr );
6620 if (!name.name)
6622 ERR("No assembly name specified!\n");
6623 return NULL;
6626 append_str( &display_name, &size, name.name );
6628 if (name.version)
6630 append_str( &display_name, &size, separator );
6631 append_str( &display_name, &size, Version );
6632 append_str( &display_name, &size, name.version );
6634 if (name.culture)
6636 append_str( &display_name, &size, separator );
6637 append_str( &display_name, &size, Culture );
6638 append_str( &display_name, &size, name.culture );
6640 if (name.pubkeytoken)
6642 append_str( &display_name, &size, separator );
6643 append_str( &display_name, &size, PublicKeyToken );
6644 append_str( &display_name, &size, name.pubkeytoken );
6647 msi_free( name.name );
6648 msi_free( name.version );
6649 msi_free( name.culture );
6650 msi_free( name.pubkeytoken );
6652 return display_name;
6655 static BOOL check_assembly_installed( MSIDATABASE *db, IAssemblyCache *cache, MSICOMPONENT *comp )
6657 ASSEMBLY_INFO asminfo;
6658 LPWSTR disp;
6659 BOOL found = FALSE;
6660 HRESULT hr;
6662 disp = get_assembly_display_name( db, comp );
6663 if (!disp)
6664 return FALSE;
6666 memset( &asminfo, 0, sizeof(ASSEMBLY_INFO) );
6667 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6669 hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_VALIDATE, disp, &asminfo );
6670 if (SUCCEEDED(hr))
6671 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6673 msi_free( disp );
6674 return found;
6677 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6679 ASSEMBLY_LIST *list = param;
6680 MSIASSEMBLY *assembly;
6681 LPCWSTR component;
6683 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6684 if (!assembly)
6685 return ERROR_OUTOFMEMORY;
6687 component = MSI_RecordGetString(rec, 1);
6688 assembly->component = get_loaded_component(list->package, component);
6689 if (!assembly->component)
6690 return ERROR_SUCCESS;
6692 if (assembly->component->ActionRequest != INSTALLSTATE_LOCAL &&
6693 assembly->component->ActionRequest != INSTALLSTATE_SOURCE)
6695 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6696 assembly->component->Action = assembly->component->Installed;
6697 return ERROR_SUCCESS;
6699 assembly->component->Action = assembly->component->ActionRequest;
6701 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6702 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6704 if (!assembly->file)
6706 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6707 return ERROR_FUNCTION_FAILED;
6710 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6711 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6712 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6714 if (assembly->application)
6716 WCHAR version[24];
6717 DWORD size = sizeof(version)/sizeof(WCHAR);
6719 /* FIXME: we should probably check the manifest file here */
6721 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6722 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6724 assembly->installed = TRUE;
6727 else
6728 assembly->installed = check_assembly_installed(list->package->db,
6729 list->cache,
6730 assembly->component);
6732 list_add_head(list->assemblies, &assembly->entry);
6733 return ERROR_SUCCESS;
6736 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6738 IAssemblyCache *cache = NULL;
6739 ASSEMBLY_LIST list;
6740 MSIQUERY *view;
6741 HRESULT hr;
6742 UINT r;
6744 static const WCHAR query[] =
6745 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6746 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6748 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6749 if (r != ERROR_SUCCESS)
6750 return ERROR_SUCCESS;
6752 hr = pCreateAssemblyCache(&cache, 0);
6753 if (FAILED(hr))
6754 return ERROR_FUNCTION_FAILED;
6756 list.package = package;
6757 list.cache = cache;
6758 list.assemblies = assemblies;
6760 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6761 msiobj_release(&view->hdr);
6763 IAssemblyCache_Release(cache);
6765 return r;
6768 static void free_assemblies(struct list *assemblies)
6770 struct list *item, *cursor;
6772 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6774 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6776 list_remove(&assembly->entry);
6777 msi_free(assembly->application);
6778 msi_free(assembly->manifest);
6779 msi_free(assembly->display_name);
6780 msi_free(assembly);
6784 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6786 MSIASSEMBLY *assembly;
6788 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6790 if (!lstrcmpW(assembly->file->File, file))
6792 *out = assembly;
6793 return TRUE;
6797 return FALSE;
6800 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6801 LPWSTR *path, DWORD *attrs, PVOID user)
6803 MSIASSEMBLY *assembly;
6804 WCHAR temppath[MAX_PATH];
6805 struct list *assemblies = user;
6806 UINT r;
6808 if (!find_assembly(assemblies, file, &assembly))
6809 return FALSE;
6811 GetTempPathW(MAX_PATH, temppath);
6812 PathAddBackslashW(temppath);
6813 lstrcatW(temppath, assembly->file->FileName);
6815 if (action == MSICABEXTRACT_BEGINEXTRACT)
6817 if (assembly->installed)
6818 return FALSE;
6820 *path = strdupW(temppath);
6821 *attrs = assembly->file->Attributes;
6823 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6825 assembly->installed = TRUE;
6827 r = install_assembly(package, assembly, temppath);
6828 if (r != ERROR_SUCCESS)
6829 ERR("Failed to install assembly\n");
6832 return TRUE;
6835 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6837 UINT r;
6838 struct list assemblies = LIST_INIT(assemblies);
6839 MSIASSEMBLY *assembly;
6840 MSIMEDIAINFO *mi;
6842 if (!init_functionpointers() || !pCreateAssemblyCache)
6843 return ERROR_FUNCTION_FAILED;
6845 r = load_assemblies(package, &assemblies);
6846 if (r != ERROR_SUCCESS)
6847 goto done;
6849 if (list_empty(&assemblies))
6850 goto done;
6852 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6853 if (!mi)
6855 r = ERROR_OUTOFMEMORY;
6856 goto done;
6859 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6861 if (assembly->installed && !mi->is_continuous)
6862 continue;
6864 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6865 (assembly->file->IsCompressed && !mi->is_extracted))
6867 MSICABDATA data;
6869 r = ready_media(package, assembly->file, mi);
6870 if (r != ERROR_SUCCESS)
6872 ERR("Failed to ready media\n");
6873 break;
6876 data.mi = mi;
6877 data.package = package;
6878 data.cb = installassembly_cb;
6879 data.user = &assemblies;
6881 if (assembly->file->IsCompressed &&
6882 !msi_cabextract(package, mi, &data))
6884 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6885 r = ERROR_FUNCTION_FAILED;
6886 break;
6890 if (!assembly->file->IsCompressed)
6892 LPWSTR source = resolve_file_source(package, assembly->file);
6894 r = install_assembly(package, assembly, source);
6895 if (r != ERROR_SUCCESS)
6896 ERR("Failed to install assembly\n");
6898 msi_free(source);
6901 /* FIXME: write Installer assembly reg values */
6904 done:
6905 free_assemblies(&assemblies);
6906 return r;
6909 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6911 LPWSTR key, template, id;
6912 UINT r = ERROR_SUCCESS;
6914 id = msi_dup_property( package->db, szProductID );
6915 if (id)
6917 msi_free( id );
6918 return ERROR_SUCCESS;
6920 template = msi_dup_property( package->db, szPIDTemplate );
6921 key = msi_dup_property( package->db, szPIDKEY );
6923 if (key && template)
6925 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6926 r = msi_set_property( package->db, szProductID, key );
6928 msi_free( template );
6929 msi_free( key );
6930 return r;
6933 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6935 TRACE("\n");
6936 package->need_reboot = 1;
6937 return ERROR_SUCCESS;
6940 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6942 static const WCHAR szAvailableFreeReg[] =
6943 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6944 MSIRECORD *uirow;
6945 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6947 TRACE("%p %d kilobytes\n", package, space);
6949 uirow = MSI_CreateRecord( 1 );
6950 MSI_RecordSetInteger( uirow, 1, space );
6951 ui_actiondata( package, szAllocateRegistrySpace, uirow );
6952 msiobj_release( &uirow->hdr );
6954 return ERROR_SUCCESS;
6957 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6959 FIXME("%p\n", package);
6960 return ERROR_SUCCESS;
6963 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6965 FIXME("%p\n", package);
6966 return ERROR_SUCCESS;
6969 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6971 UINT r, count;
6972 MSIQUERY *view;
6974 static const WCHAR driver_query[] = {
6975 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6976 'O','D','B','C','D','r','i','v','e','r',0 };
6978 static const WCHAR translator_query[] = {
6979 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6980 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6982 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6983 if (r == ERROR_SUCCESS)
6985 count = 0;
6986 r = MSI_IterateRecords( view, &count, NULL, package );
6987 msiobj_release( &view->hdr );
6988 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6991 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6992 if (r == ERROR_SUCCESS)
6994 count = 0;
6995 r = MSI_IterateRecords( view, &count, NULL, package );
6996 msiobj_release( &view->hdr );
6997 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7000 return ERROR_SUCCESS;
7003 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7004 LPCSTR action, LPCWSTR table )
7006 static const WCHAR query[] = {
7007 'S','E','L','E','C','T',' ','*',' ',
7008 'F','R','O','M',' ','`','%','s','`',0 };
7009 MSIQUERY *view = NULL;
7010 DWORD count = 0;
7011 UINT r;
7013 r = MSI_OpenQuery( package->db, &view, query, table );
7014 if (r == ERROR_SUCCESS)
7016 r = MSI_IterateRecords(view, &count, NULL, package);
7017 msiobj_release(&view->hdr);
7020 if (count)
7021 FIXME("%s -> %u ignored %s table values\n",
7022 action, count, debugstr_w(table));
7024 return ERROR_SUCCESS;
7027 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7029 static const WCHAR table[] = { 'P','a','t','c','h',0 };
7030 return msi_unimplemented_action_stub( package, "PatchFiles", table );
7033 static UINT ACTION_BindImage( MSIPACKAGE *package )
7035 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7036 return msi_unimplemented_action_stub( package, "BindImage", table );
7039 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7041 static const WCHAR table[] = {
7042 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7043 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7046 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7048 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7049 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
7052 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
7054 static const WCHAR table[] = {
7055 'M','s','i','A','s','s','e','m','b','l','y',0 };
7056 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
7059 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7061 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7062 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7065 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7067 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7068 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7071 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7073 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7074 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7077 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7079 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7080 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7083 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7085 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7086 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
7089 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7091 static const struct
7093 const WCHAR *action;
7094 UINT (*handler)(MSIPACKAGE *);
7096 StandardActions[] =
7098 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7099 { szAppSearch, ACTION_AppSearch },
7100 { szBindImage, ACTION_BindImage },
7101 { szCCPSearch, ACTION_CCPSearch },
7102 { szCostFinalize, ACTION_CostFinalize },
7103 { szCostInitialize, ACTION_CostInitialize },
7104 { szCreateFolders, ACTION_CreateFolders },
7105 { szCreateShortcuts, ACTION_CreateShortcuts },
7106 { szDeleteServices, ACTION_DeleteServices },
7107 { szDisableRollback, ACTION_DisableRollback },
7108 { szDuplicateFiles, ACTION_DuplicateFiles },
7109 { szExecuteAction, ACTION_ExecuteAction },
7110 { szFileCost, ACTION_FileCost },
7111 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7112 { szForceReboot, ACTION_ForceReboot },
7113 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7114 { szInstallExecute, ACTION_InstallExecute },
7115 { szInstallExecuteAgain, ACTION_InstallExecute },
7116 { szInstallFiles, ACTION_InstallFiles},
7117 { szInstallFinalize, ACTION_InstallFinalize },
7118 { szInstallInitialize, ACTION_InstallInitialize },
7119 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7120 { szInstallValidate, ACTION_InstallValidate },
7121 { szIsolateComponents, ACTION_IsolateComponents },
7122 { szLaunchConditions, ACTION_LaunchConditions },
7123 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7124 { szMoveFiles, ACTION_MoveFiles },
7125 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7126 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7127 { szInstallODBC, ACTION_InstallODBC },
7128 { szInstallServices, ACTION_InstallServices },
7129 { szPatchFiles, ACTION_PatchFiles },
7130 { szProcessComponents, ACTION_ProcessComponents },
7131 { szPublishComponents, ACTION_PublishComponents },
7132 { szPublishFeatures, ACTION_PublishFeatures },
7133 { szPublishProduct, ACTION_PublishProduct },
7134 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7135 { szRegisterComPlus, ACTION_RegisterComPlus},
7136 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7137 { szRegisterFonts, ACTION_RegisterFonts },
7138 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7139 { szRegisterProduct, ACTION_RegisterProduct },
7140 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7141 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7142 { szRegisterUser, ACTION_RegisterUser },
7143 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7144 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7145 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7146 { szRemoveFiles, ACTION_RemoveFiles },
7147 { szRemoveFolders, ACTION_RemoveFolders },
7148 { szRemoveIniValues, ACTION_RemoveIniValues },
7149 { szRemoveODBC, ACTION_RemoveODBC },
7150 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7151 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7152 { szResolveSource, ACTION_ResolveSource },
7153 { szRMCCPSearch, ACTION_RMCCPSearch },
7154 { szScheduleReboot, ACTION_ScheduleReboot },
7155 { szSelfRegModules, ACTION_SelfRegModules },
7156 { szSelfUnregModules, ACTION_SelfUnregModules },
7157 { szSetODBCFolders, ACTION_SetODBCFolders },
7158 { szStartServices, ACTION_StartServices },
7159 { szStopServices, ACTION_StopServices },
7160 { szUnpublishComponents, ACTION_UnpublishComponents },
7161 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7162 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7163 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7164 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7165 { szUnregisterFonts, ACTION_UnregisterFonts },
7166 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7167 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7168 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7169 { szValidateProductID, ACTION_ValidateProductID },
7170 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7171 { szWriteIniValues, ACTION_WriteIniValues },
7172 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7173 { NULL, NULL },
7176 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
7177 UINT* rc, BOOL force )
7179 BOOL ret = FALSE;
7180 BOOL run = force;
7181 int i;
7183 if (!run && !package->script->CurrentlyScripting)
7184 run = TRUE;
7186 if (!run)
7188 if (strcmpW(action,szInstallFinalize) == 0 ||
7189 strcmpW(action,szInstallExecute) == 0 ||
7190 strcmpW(action,szInstallExecuteAgain) == 0)
7191 run = TRUE;
7194 i = 0;
7195 while (StandardActions[i].action != NULL)
7197 if (strcmpW(StandardActions[i].action, action)==0)
7199 if (!run)
7201 ui_actioninfo(package, action, TRUE, 0);
7202 *rc = schedule_action(package,INSTALL_SCRIPT,action);
7203 ui_actioninfo(package, action, FALSE, *rc);
7205 else
7207 ui_actionstart(package, action);
7208 if (StandardActions[i].handler)
7210 *rc = StandardActions[i].handler(package);
7212 else
7214 FIXME("unhandled standard action %s\n",debugstr_w(action));
7215 *rc = ERROR_SUCCESS;
7218 ret = TRUE;
7219 break;
7221 i++;
7223 return ret;
7226 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
7228 UINT rc = ERROR_SUCCESS;
7229 BOOL handled;
7231 TRACE("Performing action (%s)\n", debugstr_w(action));
7233 handled = ACTION_HandleStandardAction(package, action, &rc, force);
7235 if (!handled)
7236 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
7238 if (!handled)
7240 WARN("unhandled msi action %s\n", debugstr_w(action));
7241 rc = ERROR_FUNCTION_NOT_CALLED;
7244 return rc;
7247 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7249 UINT rc = ERROR_SUCCESS;
7250 BOOL handled = FALSE;
7252 TRACE("Performing action (%s)\n", debugstr_w(action));
7254 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
7256 if (!handled)
7257 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7259 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7260 handled = TRUE;
7262 if (!handled)
7264 WARN("unhandled msi action %s\n", debugstr_w(action));
7265 rc = ERROR_FUNCTION_NOT_CALLED;
7268 return rc;
7271 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7273 UINT rc = ERROR_SUCCESS;
7274 MSIRECORD *row;
7276 static const WCHAR ExecSeqQuery[] =
7277 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7278 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7279 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7280 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7281 static const WCHAR UISeqQuery[] =
7282 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7283 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7284 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7285 ' ', '=',' ','%','i',0};
7287 if (needs_ui_sequence(package))
7288 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7289 else
7290 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7292 if (row)
7294 LPCWSTR action, cond;
7296 TRACE("Running the actions\n");
7298 /* check conditions */
7299 cond = MSI_RecordGetString(row, 2);
7301 /* this is a hack to skip errors in the condition code */
7302 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7304 msiobj_release(&row->hdr);
7305 return ERROR_SUCCESS;
7308 action = MSI_RecordGetString(row, 1);
7309 if (!action)
7311 ERR("failed to fetch action\n");
7312 msiobj_release(&row->hdr);
7313 return ERROR_FUNCTION_FAILED;
7316 if (needs_ui_sequence(package))
7317 rc = ACTION_PerformUIAction(package, action, -1);
7318 else
7319 rc = ACTION_PerformAction(package, action, -1, FALSE);
7321 msiobj_release(&row->hdr);
7324 return rc;
7327 /****************************************************
7328 * TOP level entry points
7329 *****************************************************/
7331 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7332 LPCWSTR szCommandLine )
7334 UINT rc;
7335 BOOL ui_exists;
7337 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7338 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7340 msi_set_property( package->db, szAction, szInstall );
7342 package->script->InWhatSequence = SEQUENCE_INSTALL;
7344 if (szPackagePath)
7346 LPWSTR p, dir;
7347 LPCWSTR file;
7349 dir = strdupW(szPackagePath);
7350 p = strrchrW(dir, '\\');
7351 if (p)
7353 *(++p) = 0;
7354 file = szPackagePath + (p - dir);
7356 else
7358 msi_free(dir);
7359 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7360 GetCurrentDirectoryW(MAX_PATH, dir);
7361 lstrcatW(dir, szBackSlash);
7362 file = szPackagePath;
7365 msi_free( package->PackagePath );
7366 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7367 if (!package->PackagePath)
7369 msi_free(dir);
7370 return ERROR_OUTOFMEMORY;
7373 lstrcpyW(package->PackagePath, dir);
7374 lstrcatW(package->PackagePath, file);
7375 msi_free(dir);
7377 msi_set_sourcedir_props(package, FALSE);
7380 msi_parse_command_line( package, szCommandLine, FALSE );
7382 msi_apply_transforms( package );
7383 msi_apply_patches( package );
7385 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7387 TRACE("setting reinstall property\n");
7388 msi_set_property( package->db, szReinstall, szAll );
7391 /* properties may have been added by a transform */
7392 msi_clone_properties( package );
7393 msi_set_context( package );
7395 if (needs_ui_sequence( package))
7397 package->script->InWhatSequence |= SEQUENCE_UI;
7398 rc = ACTION_ProcessUISequence(package);
7399 ui_exists = ui_sequence_exists(package);
7400 if (rc == ERROR_SUCCESS || !ui_exists)
7402 package->script->InWhatSequence |= SEQUENCE_EXEC;
7403 rc = ACTION_ProcessExecSequence(package, ui_exists);
7406 else
7407 rc = ACTION_ProcessExecSequence(package, FALSE);
7409 package->script->CurrentlyScripting = FALSE;
7411 /* process the ending type action */
7412 if (rc == ERROR_SUCCESS)
7413 ACTION_PerformActionSequence(package, -1);
7414 else if (rc == ERROR_INSTALL_USEREXIT)
7415 ACTION_PerformActionSequence(package, -2);
7416 else if (rc == ERROR_INSTALL_SUSPEND)
7417 ACTION_PerformActionSequence(package, -4);
7418 else /* failed */
7419 ACTION_PerformActionSequence(package, -3);
7421 /* finish up running custom actions */
7422 ACTION_FinishCustomActions(package);
7424 if (rc == ERROR_SUCCESS && package->need_reboot)
7425 return ERROR_SUCCESS_REBOOT_REQUIRED;
7427 return rc;