server: Set FD_READ in only 1 place (cleanup).
[wine.git] / dlls / msi / action.c
blobc5354de13e7d54215add04902c486871694d218d
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 source, check;
693 if (msi_get_property_int( package->db, szInstalled, 0 ))
695 HKEY hkey;
697 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
698 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
699 RegCloseKey( hkey );
701 else
703 LPWSTR p, db;
704 DWORD len;
706 db = msi_dup_property( package->db, szOriginalDatabase );
707 if (!db)
708 return ERROR_OUTOFMEMORY;
710 p = strrchrW( db, '\\' );
711 if (!p)
713 p = strrchrW( db, '/' );
714 if (!p)
716 msi_free(db);
717 return ERROR_SUCCESS;
721 len = p - db + 2;
722 source = msi_alloc( len * sizeof(WCHAR) );
723 lstrcpynW( source, db, len );
724 msi_free( db );
727 check = msi_dup_property( package->db, cszSourceDir );
728 if (!check || replace)
730 UINT r = msi_set_property( package->db, cszSourceDir, source );
731 if (r == ERROR_SUCCESS)
732 msi_reset_folders( package, TRUE );
734 msi_free( check );
736 check = msi_dup_property( package->db, cszSOURCEDIR );
737 if (!check || replace)
738 msi_set_property( package->db, cszSOURCEDIR, source );
740 msi_free( check );
741 msi_free( source );
743 return ERROR_SUCCESS;
746 static BOOL needs_ui_sequence(MSIPACKAGE *package)
748 INT level = msi_get_property_int(package->db, szUILevel, 0);
749 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
752 UINT msi_set_context(MSIPACKAGE *package)
754 int num;
756 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
758 num = msi_get_property_int(package->db, szAllUsers, 0);
759 if (num == 1 || num == 2)
760 package->Context = MSIINSTALLCONTEXT_MACHINE;
762 return ERROR_SUCCESS;
765 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
767 UINT rc;
768 LPCWSTR cond, action;
769 MSIPACKAGE *package = param;
771 action = MSI_RecordGetString(row,1);
772 if (!action)
774 ERR("Error is retrieving action name\n");
775 return ERROR_FUNCTION_FAILED;
778 /* check conditions */
779 cond = MSI_RecordGetString(row,2);
781 /* this is a hack to skip errors in the condition code */
782 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
784 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
785 return ERROR_SUCCESS;
788 if (needs_ui_sequence(package))
789 rc = ACTION_PerformUIAction(package, action, -1);
790 else
791 rc = ACTION_PerformAction(package, action, -1, FALSE);
793 msi_dialog_check_messages( NULL );
795 if (package->CurrentInstallState != ERROR_SUCCESS)
796 rc = package->CurrentInstallState;
798 if (rc == ERROR_FUNCTION_NOT_CALLED)
799 rc = ERROR_SUCCESS;
801 if (rc != ERROR_SUCCESS)
802 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
804 return rc;
807 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
809 MSIQUERY * view;
810 UINT r;
811 static const WCHAR query[] =
812 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
813 '`','%','s','`',
814 ' ','W','H','E','R','E',' ',
815 '`','S','e','q','u','e','n','c','e','`',' ',
816 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
817 '`','S','e','q','u','e','n','c','e','`',0};
819 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
821 r = MSI_OpenQuery( package->db, &view, query, szTable );
822 if (r == ERROR_SUCCESS)
824 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
825 msiobj_release(&view->hdr);
828 return r;
831 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
833 MSIQUERY * view;
834 UINT rc;
835 static const WCHAR ExecSeqQuery[] =
836 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
837 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
838 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
839 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
840 'O','R','D','E','R',' ', 'B','Y',' ',
841 '`','S','e','q','u','e','n','c','e','`',0 };
842 static const WCHAR IVQuery[] =
843 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
844 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
845 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
846 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
847 ' ','\'', 'I','n','s','t','a','l','l',
848 'V','a','l','i','d','a','t','e','\'', 0};
849 INT seq = 0;
851 if (package->script->ExecuteSequenceRun)
853 TRACE("Execute Sequence already Run\n");
854 return ERROR_SUCCESS;
857 package->script->ExecuteSequenceRun = TRUE;
859 /* get the sequence number */
860 if (UIran)
862 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
863 if( !row )
864 return ERROR_FUNCTION_FAILED;
865 seq = MSI_RecordGetInteger(row,1);
866 msiobj_release(&row->hdr);
869 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
870 if (rc == ERROR_SUCCESS)
872 TRACE("Running the actions\n");
874 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
875 msiobj_release(&view->hdr);
878 return rc;
881 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
883 MSIQUERY * view;
884 UINT rc;
885 static const WCHAR ExecSeqQuery [] =
886 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
887 '`','I','n','s','t','a','l','l',
888 'U','I','S','e','q','u','e','n','c','e','`',
889 ' ','W','H','E','R','E',' ',
890 '`','S','e','q','u','e','n','c','e','`',' ',
891 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
892 '`','S','e','q','u','e','n','c','e','`',0};
894 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
895 if (rc == ERROR_SUCCESS)
897 TRACE("Running the actions\n");
899 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
900 msiobj_release(&view->hdr);
903 return rc;
906 /********************************************************
907 * ACTION helper functions and functions that perform the actions
908 *******************************************************/
909 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
910 UINT* rc, UINT script, BOOL force )
912 BOOL ret=FALSE;
913 UINT arc;
915 arc = ACTION_CustomAction(package, action, script, force);
917 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
919 *rc = arc;
920 ret = TRUE;
922 return ret;
926 * Actual Action Handlers
929 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
931 MSIPACKAGE *package = param;
932 LPCWSTR dir, component;
933 LPWSTR full_path;
934 MSIRECORD *uirow;
935 MSIFOLDER *folder;
936 MSICOMPONENT *comp;
938 component = MSI_RecordGetString(row, 2);
939 comp = get_loaded_component(package, component);
940 if (!comp)
941 return ERROR_SUCCESS;
943 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
945 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
946 comp->Action = comp->Installed;
947 return ERROR_SUCCESS;
949 comp->Action = INSTALLSTATE_LOCAL;
951 dir = MSI_RecordGetString(row,1);
952 if (!dir)
954 ERR("Unable to get folder id\n");
955 return ERROR_SUCCESS;
958 uirow = MSI_CreateRecord(1);
959 MSI_RecordSetStringW(uirow, 1, dir);
960 ui_actiondata(package, szCreateFolders, uirow);
961 msiobj_release(&uirow->hdr);
963 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
964 if (!full_path)
966 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
967 return ERROR_SUCCESS;
970 TRACE("Folder is %s\n",debugstr_w(full_path));
972 if (folder->State == 0)
973 create_full_pathW(full_path);
975 folder->State = 3;
977 msi_free(full_path);
978 return ERROR_SUCCESS;
981 /* FIXME: probably should merge this with the above function */
982 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
984 UINT rc = ERROR_SUCCESS;
985 MSIFOLDER *folder;
986 LPWSTR install_path;
988 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
989 if (!install_path)
990 return ERROR_FUNCTION_FAILED;
992 /* create the path */
993 if (folder->State == 0)
995 create_full_pathW(install_path);
996 folder->State = 2;
998 msi_free(install_path);
1000 return rc;
1003 UINT msi_create_component_directories( MSIPACKAGE *package )
1005 MSICOMPONENT *comp;
1007 /* create all the folders required by the components are going to install */
1008 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1010 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
1011 continue;
1012 msi_create_directory( package, comp->Directory );
1015 return ERROR_SUCCESS;
1018 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1020 static const WCHAR ExecSeqQuery[] =
1021 {'S','E','L','E','C','T',' ',
1022 '`','D','i','r','e','c','t','o','r','y','_','`',
1023 ' ','F','R','O','M',' ',
1024 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1025 UINT rc;
1026 MSIQUERY *view;
1028 /* create all the empty folders specified in the CreateFolder table */
1029 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1030 if (rc != ERROR_SUCCESS)
1031 return ERROR_SUCCESS;
1033 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1034 msiobj_release(&view->hdr);
1036 return rc;
1039 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1041 MSIPACKAGE *package = param;
1042 LPCWSTR dir, component;
1043 LPWSTR full_path;
1044 MSIRECORD *uirow;
1045 MSIFOLDER *folder;
1046 MSICOMPONENT *comp;
1048 component = MSI_RecordGetString(row, 2);
1049 comp = get_loaded_component(package, component);
1050 if (!comp)
1051 return ERROR_SUCCESS;
1053 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1055 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1056 comp->Action = comp->Installed;
1057 return ERROR_SUCCESS;
1059 comp->Action = INSTALLSTATE_ABSENT;
1061 dir = MSI_RecordGetString( row, 1 );
1062 if (!dir)
1064 ERR("Unable to get folder id\n");
1065 return ERROR_SUCCESS;
1068 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1069 if (!full_path)
1071 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1072 return ERROR_SUCCESS;
1075 TRACE("folder is %s\n", debugstr_w(full_path));
1077 uirow = MSI_CreateRecord( 1 );
1078 MSI_RecordSetStringW( uirow, 1, full_path );
1079 ui_actiondata( package, szRemoveFolders, uirow );
1080 msiobj_release( &uirow->hdr );
1082 RemoveDirectoryW( full_path );
1083 folder->State = 0;
1085 msi_free( full_path );
1086 return ERROR_SUCCESS;
1089 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1091 static const WCHAR query[] =
1092 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1093 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1095 MSIQUERY *view;
1096 UINT rc;
1098 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1099 if (rc != ERROR_SUCCESS)
1100 return ERROR_SUCCESS;
1102 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1103 msiobj_release( &view->hdr );
1105 return rc;
1108 static UINT load_component( MSIRECORD *row, LPVOID param )
1110 MSIPACKAGE *package = param;
1111 MSICOMPONENT *comp;
1113 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1114 if (!comp)
1115 return ERROR_FUNCTION_FAILED;
1117 list_add_tail( &package->components, &comp->entry );
1119 /* fill in the data */
1120 comp->Component = msi_dup_record_field( row, 1 );
1122 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1124 comp->ComponentId = msi_dup_record_field( row, 2 );
1125 comp->Directory = msi_dup_record_field( row, 3 );
1126 comp->Attributes = MSI_RecordGetInteger(row,4);
1127 comp->Condition = msi_dup_record_field( row, 5 );
1128 comp->KeyPath = msi_dup_record_field( row, 6 );
1130 comp->Installed = INSTALLSTATE_UNKNOWN;
1131 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1133 return ERROR_SUCCESS;
1136 static UINT load_all_components( MSIPACKAGE *package )
1138 static const WCHAR query[] = {
1139 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1140 '`','C','o','m','p','o','n','e','n','t','`',0 };
1141 MSIQUERY *view;
1142 UINT r;
1144 if (!list_empty(&package->components))
1145 return ERROR_SUCCESS;
1147 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1148 if (r != ERROR_SUCCESS)
1149 return r;
1151 r = MSI_IterateRecords(view, NULL, load_component, package);
1152 msiobj_release(&view->hdr);
1153 return r;
1156 typedef struct {
1157 MSIPACKAGE *package;
1158 MSIFEATURE *feature;
1159 } _ilfs;
1161 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1163 ComponentList *cl;
1165 cl = msi_alloc( sizeof (*cl) );
1166 if ( !cl )
1167 return ERROR_NOT_ENOUGH_MEMORY;
1168 cl->component = comp;
1169 list_add_tail( &feature->Components, &cl->entry );
1171 return ERROR_SUCCESS;
1174 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1176 FeatureList *fl;
1178 fl = msi_alloc( sizeof(*fl) );
1179 if ( !fl )
1180 return ERROR_NOT_ENOUGH_MEMORY;
1181 fl->feature = child;
1182 list_add_tail( &parent->Children, &fl->entry );
1184 return ERROR_SUCCESS;
1187 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1189 _ilfs* ilfs = param;
1190 LPCWSTR component;
1191 MSICOMPONENT *comp;
1193 component = MSI_RecordGetString(row,1);
1195 /* check to see if the component is already loaded */
1196 comp = get_loaded_component( ilfs->package, component );
1197 if (!comp)
1199 ERR("unknown component %s\n", debugstr_w(component));
1200 return ERROR_FUNCTION_FAILED;
1203 add_feature_component( ilfs->feature, comp );
1204 comp->Enabled = TRUE;
1206 return ERROR_SUCCESS;
1209 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1211 MSIFEATURE *feature;
1213 if ( !name )
1214 return NULL;
1216 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1218 if ( !lstrcmpW( feature->Feature, name ) )
1219 return feature;
1222 return NULL;
1225 static UINT load_feature(MSIRECORD * row, LPVOID param)
1227 MSIPACKAGE* package = param;
1228 MSIFEATURE* feature;
1229 static const WCHAR Query1[] =
1230 {'S','E','L','E','C','T',' ',
1231 '`','C','o','m','p','o','n','e','n','t','_','`',
1232 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1233 'C','o','m','p','o','n','e','n','t','s','`',' ',
1234 'W','H','E','R','E',' ',
1235 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1236 MSIQUERY * view;
1237 UINT rc;
1238 _ilfs ilfs;
1240 /* fill in the data */
1242 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1243 if (!feature)
1244 return ERROR_NOT_ENOUGH_MEMORY;
1246 list_init( &feature->Children );
1247 list_init( &feature->Components );
1249 feature->Feature = msi_dup_record_field( row, 1 );
1251 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1253 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1254 feature->Title = msi_dup_record_field( row, 3 );
1255 feature->Description = msi_dup_record_field( row, 4 );
1257 if (!MSI_RecordIsNull(row,5))
1258 feature->Display = MSI_RecordGetInteger(row,5);
1260 feature->Level= MSI_RecordGetInteger(row,6);
1261 feature->Directory = msi_dup_record_field( row, 7 );
1262 feature->Attributes = MSI_RecordGetInteger(row,8);
1264 feature->Installed = INSTALLSTATE_UNKNOWN;
1265 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1267 list_add_tail( &package->features, &feature->entry );
1269 /* load feature components */
1271 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1272 if (rc != ERROR_SUCCESS)
1273 return ERROR_SUCCESS;
1275 ilfs.package = package;
1276 ilfs.feature = feature;
1278 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1279 msiobj_release(&view->hdr);
1281 return ERROR_SUCCESS;
1284 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1286 MSIPACKAGE* package = param;
1287 MSIFEATURE *parent, *child;
1289 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1290 if (!child)
1291 return ERROR_FUNCTION_FAILED;
1293 if (!child->Feature_Parent)
1294 return ERROR_SUCCESS;
1296 parent = find_feature_by_name( package, child->Feature_Parent );
1297 if (!parent)
1298 return ERROR_FUNCTION_FAILED;
1300 add_feature_child( parent, child );
1301 return ERROR_SUCCESS;
1304 static UINT load_all_features( MSIPACKAGE *package )
1306 static const WCHAR query[] = {
1307 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1308 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1309 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1310 MSIQUERY *view;
1311 UINT r;
1313 if (!list_empty(&package->features))
1314 return ERROR_SUCCESS;
1316 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1317 if (r != ERROR_SUCCESS)
1318 return r;
1320 r = MSI_IterateRecords( view, NULL, load_feature, package );
1321 if (r != ERROR_SUCCESS)
1322 return r;
1324 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1325 msiobj_release( &view->hdr );
1327 return r;
1330 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1332 if (!p)
1333 return p;
1334 p = strchrW(p, ch);
1335 if (!p)
1336 return p;
1337 *p = 0;
1338 return p+1;
1341 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1343 static const WCHAR query[] = {
1344 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1345 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1346 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1347 MSIQUERY *view = NULL;
1348 MSIRECORD *row = NULL;
1349 UINT r;
1351 TRACE("%s\n", debugstr_w(file->File));
1353 r = MSI_OpenQuery(package->db, &view, query, file->File);
1354 if (r != ERROR_SUCCESS)
1355 goto done;
1357 r = MSI_ViewExecute(view, NULL);
1358 if (r != ERROR_SUCCESS)
1359 goto done;
1361 r = MSI_ViewFetch(view, &row);
1362 if (r != ERROR_SUCCESS)
1363 goto done;
1365 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1366 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1367 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1368 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1369 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1371 done:
1372 if (view) msiobj_release(&view->hdr);
1373 if (row) msiobj_release(&row->hdr);
1374 return r;
1377 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1379 MSIRECORD *row;
1380 static const WCHAR query[] = {
1381 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1382 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1383 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1385 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1386 if (!row)
1388 WARN("query failed\n");
1389 return ERROR_FUNCTION_FAILED;
1392 file->disk_id = MSI_RecordGetInteger( row, 1 );
1393 msiobj_release( &row->hdr );
1394 return ERROR_SUCCESS;
1397 static UINT load_file(MSIRECORD *row, LPVOID param)
1399 MSIPACKAGE* package = param;
1400 LPCWSTR component;
1401 MSIFILE *file;
1403 /* fill in the data */
1405 file = msi_alloc_zero( sizeof (MSIFILE) );
1406 if (!file)
1407 return ERROR_NOT_ENOUGH_MEMORY;
1409 file->File = msi_dup_record_field( row, 1 );
1411 component = MSI_RecordGetString( row, 2 );
1412 file->Component = get_loaded_component( package, component );
1414 if (!file->Component)
1416 WARN("Component not found: %s\n", debugstr_w(component));
1417 msi_free(file->File);
1418 msi_free(file);
1419 return ERROR_SUCCESS;
1422 file->FileName = msi_dup_record_field( row, 3 );
1423 reduce_to_longfilename( file->FileName );
1425 file->ShortName = msi_dup_record_field( row, 3 );
1426 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1428 file->FileSize = MSI_RecordGetInteger( row, 4 );
1429 file->Version = msi_dup_record_field( row, 5 );
1430 file->Language = msi_dup_record_field( row, 6 );
1431 file->Attributes = MSI_RecordGetInteger( row, 7 );
1432 file->Sequence = MSI_RecordGetInteger( row, 8 );
1434 file->state = msifs_invalid;
1436 /* if the compressed bits are not set in the file attributes,
1437 * then read the information from the package word count property
1439 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1441 file->IsCompressed = FALSE;
1443 else if (file->Attributes &
1444 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1446 file->IsCompressed = TRUE;
1448 else if (file->Attributes & msidbFileAttributesNoncompressed)
1450 file->IsCompressed = FALSE;
1452 else
1454 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1457 load_file_hash(package, file);
1458 load_file_disk_id(package, file);
1460 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1462 list_add_tail( &package->files, &file->entry );
1464 return ERROR_SUCCESS;
1467 static UINT load_all_files(MSIPACKAGE *package)
1469 MSIQUERY * view;
1470 UINT rc;
1471 static const WCHAR Query[] =
1472 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1473 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1474 '`','S','e','q','u','e','n','c','e','`', 0};
1476 if (!list_empty(&package->files))
1477 return ERROR_SUCCESS;
1479 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1480 if (rc != ERROR_SUCCESS)
1481 return ERROR_SUCCESS;
1483 rc = MSI_IterateRecords(view, NULL, load_file, package);
1484 msiobj_release(&view->hdr);
1486 return ERROR_SUCCESS;
1489 static UINT load_folder( MSIRECORD *row, LPVOID param )
1491 MSIPACKAGE *package = param;
1492 static WCHAR szEmpty[] = { 0 };
1493 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1494 MSIFOLDER *folder;
1496 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1497 if (!folder)
1498 return ERROR_NOT_ENOUGH_MEMORY;
1500 folder->Directory = msi_dup_record_field( row, 1 );
1502 TRACE("%s\n", debugstr_w(folder->Directory));
1504 p = msi_dup_record_field(row, 3);
1506 /* split src and target dir */
1507 tgt_short = p;
1508 src_short = folder_split_path( p, ':' );
1510 /* split the long and short paths */
1511 tgt_long = folder_split_path( tgt_short, '|' );
1512 src_long = folder_split_path( src_short, '|' );
1514 /* check for no-op dirs */
1515 if (!lstrcmpW(szDot, tgt_short))
1516 tgt_short = szEmpty;
1517 if (!lstrcmpW(szDot, src_short))
1518 src_short = szEmpty;
1520 if (!tgt_long)
1521 tgt_long = tgt_short;
1523 if (!src_short) {
1524 src_short = tgt_short;
1525 src_long = tgt_long;
1528 if (!src_long)
1529 src_long = src_short;
1531 /* FIXME: use the target short path too */
1532 folder->TargetDefault = strdupW(tgt_long);
1533 folder->SourceShortPath = strdupW(src_short);
1534 folder->SourceLongPath = strdupW(src_long);
1535 msi_free(p);
1537 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1538 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1539 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1541 folder->Parent = msi_dup_record_field( row, 2 );
1543 folder->Property = msi_dup_property( package->db, folder->Directory );
1545 list_add_tail( &package->folders, &folder->entry );
1547 TRACE("returning %p\n", folder);
1549 return ERROR_SUCCESS;
1552 static UINT load_all_folders( MSIPACKAGE *package )
1554 static const WCHAR query[] = {
1555 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1556 '`','D','i','r','e','c','t','o','r','y','`',0 };
1557 MSIQUERY *view;
1558 UINT r;
1560 if (!list_empty(&package->folders))
1561 return ERROR_SUCCESS;
1563 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1564 if (r != ERROR_SUCCESS)
1565 return r;
1567 r = MSI_IterateRecords(view, NULL, load_folder, package);
1568 msiobj_release(&view->hdr);
1569 return r;
1573 * I am not doing any of the costing functionality yet.
1574 * Mostly looking at doing the Component and Feature loading
1576 * The native MSI does A LOT of modification to tables here. Mostly adding
1577 * a lot of temporary columns to the Feature and Component tables.
1579 * note: Native msi also tracks the short filename. But I am only going to
1580 * track the long ones. Also looking at this directory table
1581 * it appears that the directory table does not get the parents
1582 * resolved base on property only based on their entries in the
1583 * directory table.
1585 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1587 static const WCHAR szCosting[] =
1588 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1590 msi_set_property( package->db, szCosting, szZero );
1591 msi_set_property( package->db, cszRootDrive, c_colon );
1593 load_all_folders( package );
1594 load_all_components( package );
1595 load_all_features( package );
1596 load_all_files( package );
1598 return ERROR_SUCCESS;
1601 static UINT execute_script(MSIPACKAGE *package, UINT script )
1603 UINT i;
1604 UINT rc = ERROR_SUCCESS;
1606 TRACE("Executing Script %i\n",script);
1608 if (!package->script)
1610 ERR("no script!\n");
1611 return ERROR_FUNCTION_FAILED;
1614 for (i = 0; i < package->script->ActionCount[script]; i++)
1616 LPWSTR action;
1617 action = package->script->Actions[script][i];
1618 ui_actionstart(package, action);
1619 TRACE("Executing Action (%s)\n",debugstr_w(action));
1620 rc = ACTION_PerformAction(package, action, script, TRUE);
1621 if (rc != ERROR_SUCCESS)
1622 break;
1624 msi_free_action_script(package, script);
1625 return rc;
1628 static UINT ACTION_FileCost(MSIPACKAGE *package)
1630 return ERROR_SUCCESS;
1633 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1635 MSICOMPONENT *comp;
1636 INSTALLSTATE state;
1637 UINT r;
1639 state = MsiQueryProductStateW(package->ProductCode);
1641 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1643 if (!comp->ComponentId)
1644 continue;
1646 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1647 comp->Installed = INSTALLSTATE_ABSENT;
1648 else
1650 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1651 package->Context, comp->ComponentId,
1652 &comp->Installed);
1653 if (r != ERROR_SUCCESS)
1654 comp->Installed = INSTALLSTATE_ABSENT;
1659 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1661 MSIFEATURE *feature;
1662 INSTALLSTATE state;
1664 state = MsiQueryProductStateW(package->ProductCode);
1666 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1668 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1669 feature->Installed = INSTALLSTATE_ABSENT;
1670 else
1672 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1673 feature->Feature);
1678 static BOOL process_state_property(MSIPACKAGE* package, int level,
1679 LPCWSTR property, INSTALLSTATE state)
1681 LPWSTR override;
1682 MSIFEATURE *feature;
1684 override = msi_dup_property( package->db, property );
1685 if (!override)
1686 return FALSE;
1688 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1690 if (lstrcmpW(property, szRemove) &&
1691 (feature->Level <= 0 || feature->Level > level))
1692 continue;
1694 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1696 if (strcmpiW(override, szAll)==0)
1697 msi_feature_set_state(package, feature, state);
1698 else
1700 LPWSTR ptr = override;
1701 LPWSTR ptr2 = strchrW(override,',');
1703 while (ptr)
1705 int len = ptr2 - ptr;
1707 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1708 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1710 msi_feature_set_state(package, feature, state);
1711 break;
1713 if (ptr2)
1715 ptr=ptr2+1;
1716 ptr2 = strchrW(ptr,',');
1718 else
1719 break;
1723 msi_free(override);
1725 return TRUE;
1728 static BOOL process_overrides( MSIPACKAGE *package, int level )
1730 static const WCHAR szAddLocal[] =
1731 {'A','D','D','L','O','C','A','L',0};
1732 static const WCHAR szAddSource[] =
1733 {'A','D','D','S','O','U','R','C','E',0};
1734 static const WCHAR szAdvertise[] =
1735 {'A','D','V','E','R','T','I','S','E',0};
1736 BOOL ret = FALSE;
1738 /* all these activation/deactivation things happen in order and things
1739 * later on the list override things earlier on the list.
1741 * 0 INSTALLLEVEL processing
1742 * 1 ADDLOCAL
1743 * 2 REMOVE
1744 * 3 ADDSOURCE
1745 * 4 ADDDEFAULT
1746 * 5 REINSTALL
1747 * 6 ADVERTISE
1748 * 7 COMPADDLOCAL
1749 * 8 COMPADDSOURCE
1750 * 9 FILEADDLOCAL
1751 * 10 FILEADDSOURCE
1752 * 11 FILEADDDEFAULT
1754 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1755 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1756 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1757 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1758 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1760 if (ret)
1761 msi_set_property( package->db, szPreselected, szOne );
1763 return ret;
1766 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1768 int level;
1769 static const WCHAR szlevel[] =
1770 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1771 MSICOMPONENT* component;
1772 MSIFEATURE *feature;
1774 TRACE("Checking Install Level\n");
1776 level = msi_get_property_int(package->db, szlevel, 1);
1778 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1780 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1782 BOOL feature_state = ((feature->Level > 0) &&
1783 (feature->Level <= level));
1785 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1787 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1788 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1789 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1790 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1791 else
1792 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1796 /* disable child features of unselected parent features */
1797 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1799 FeatureList *fl;
1801 if (feature->Level > 0 && feature->Level <= level)
1802 continue;
1804 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1805 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1810 * now we want to enable or disable components base on feature
1813 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1815 ComponentList *cl;
1817 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1818 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1820 if (!feature->Level)
1821 continue;
1823 /* features with components that have compressed files are made local */
1824 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1826 if (cl->component->Enabled &&
1827 cl->component->ForceLocalState &&
1828 feature->Action == INSTALLSTATE_SOURCE)
1830 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1831 break;
1835 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1837 component = cl->component;
1839 if (!component->Enabled)
1840 continue;
1842 switch (feature->Action)
1844 case INSTALLSTATE_ABSENT:
1845 component->anyAbsent = 1;
1846 break;
1847 case INSTALLSTATE_ADVERTISED:
1848 component->hasAdvertiseFeature = 1;
1849 break;
1850 case INSTALLSTATE_SOURCE:
1851 component->hasSourceFeature = 1;
1852 break;
1853 case INSTALLSTATE_LOCAL:
1854 component->hasLocalFeature = 1;
1855 break;
1856 case INSTALLSTATE_DEFAULT:
1857 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1858 component->hasAdvertiseFeature = 1;
1859 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1860 component->hasSourceFeature = 1;
1861 else
1862 component->hasLocalFeature = 1;
1863 break;
1864 default:
1865 break;
1870 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1872 /* if the component isn't enabled, leave it alone */
1873 if (!component->Enabled)
1874 continue;
1876 /* check if it's local or source */
1877 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1878 (component->hasLocalFeature || component->hasSourceFeature))
1880 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1881 !component->ForceLocalState)
1882 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1883 else
1884 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1885 continue;
1888 /* if any feature is local, the component must be local too */
1889 if (component->hasLocalFeature)
1891 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1892 continue;
1895 if (component->hasSourceFeature)
1897 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1898 continue;
1901 if (component->hasAdvertiseFeature)
1903 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1904 continue;
1907 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1908 if (component->anyAbsent)
1909 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1912 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1914 if (component->Action == INSTALLSTATE_DEFAULT)
1916 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1917 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1920 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1921 debugstr_w(component->Component), component->Installed, component->Action);
1925 return ERROR_SUCCESS;
1928 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1930 MSIPACKAGE *package = param;
1931 LPCWSTR name;
1932 LPWSTR path;
1933 MSIFOLDER *f;
1935 name = MSI_RecordGetString(row,1);
1937 f = get_loaded_folder(package, name);
1938 if (!f) return ERROR_SUCCESS;
1940 /* reset the ResolvedTarget */
1941 msi_free(f->ResolvedTarget);
1942 f->ResolvedTarget = NULL;
1944 /* This helper function now does ALL the work */
1945 TRACE("Dir %s ...\n",debugstr_w(name));
1946 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1947 TRACE("resolves to %s\n",debugstr_w(path));
1948 msi_free(path);
1950 return ERROR_SUCCESS;
1953 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1955 MSIPACKAGE *package = param;
1956 LPCWSTR name;
1957 MSIFEATURE *feature;
1959 name = MSI_RecordGetString( row, 1 );
1961 feature = get_loaded_feature( package, name );
1962 if (!feature)
1963 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1964 else
1966 LPCWSTR Condition;
1967 Condition = MSI_RecordGetString(row,3);
1969 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1971 int level = MSI_RecordGetInteger(row,2);
1972 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1973 feature->Level = level;
1976 return ERROR_SUCCESS;
1979 static LPWSTR get_disk_file_version( LPCWSTR filename )
1981 static const WCHAR name_fmt[] =
1982 {'%','u','.','%','u','.','%','u','.','%','u',0};
1983 static const WCHAR name[] = {'\\',0};
1984 VS_FIXEDFILEINFO *lpVer;
1985 WCHAR filever[0x100];
1986 LPVOID version;
1987 DWORD versize;
1988 DWORD handle;
1989 UINT sz;
1991 TRACE("%s\n", debugstr_w(filename));
1993 versize = GetFileVersionInfoSizeW( filename, &handle );
1994 if (!versize)
1995 return NULL;
1997 version = msi_alloc( versize );
1998 GetFileVersionInfoW( filename, 0, versize, version );
2000 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2002 msi_free( version );
2003 return NULL;
2006 sprintfW( filever, name_fmt,
2007 HIWORD(lpVer->dwFileVersionMS),
2008 LOWORD(lpVer->dwFileVersionMS),
2009 HIWORD(lpVer->dwFileVersionLS),
2010 LOWORD(lpVer->dwFileVersionLS));
2012 msi_free( version );
2014 return strdupW( filever );
2017 static DWORD get_disk_file_size( LPCWSTR filename )
2019 HANDLE file;
2020 DWORD size;
2022 TRACE("%s\n", debugstr_w(filename));
2024 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2025 if (file == INVALID_HANDLE_VALUE)
2026 return INVALID_FILE_SIZE;
2028 size = GetFileSize( file, NULL );
2029 CloseHandle( file );
2030 return size;
2033 static BOOL hash_matches( MSIFILE *file )
2035 UINT r;
2036 MSIFILEHASHINFO hash;
2038 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2039 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2040 if (r != ERROR_SUCCESS)
2041 return FALSE;
2043 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2046 static UINT set_file_install_states( MSIPACKAGE *package )
2048 LPWSTR file_version;
2049 MSIFILE *file;
2051 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2053 MSICOMPONENT* comp = file->Component;
2054 DWORD file_size;
2055 LPWSTR p;
2057 if (!comp)
2058 continue;
2060 if (file->IsCompressed)
2061 comp->ForceLocalState = TRUE;
2063 /* calculate target */
2064 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2066 msi_free(file->TargetPath);
2068 TRACE("file %s is named %s\n",
2069 debugstr_w(file->File), debugstr_w(file->FileName));
2071 file->TargetPath = build_directory_name(2, p, file->FileName);
2073 msi_free(p);
2075 TRACE("file %s resolves to %s\n",
2076 debugstr_w(file->File), debugstr_w(file->TargetPath));
2078 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2080 file->state = msifs_missing;
2081 comp->Cost += file->FileSize;
2082 continue;
2084 if (file->Version && (file_version = get_disk_file_version( file->TargetPath )))
2086 TRACE("new %s old %s\n", debugstr_w(file->Version), debugstr_w(file_version));
2088 if (strcmpiW(file_version, file->Version) < 0)
2090 file->state = msifs_overwrite;
2091 comp->Cost += file->FileSize;
2093 else
2095 TRACE("Destination file version equal or greater, not overwriting\n");
2096 file->state = msifs_present;
2098 msi_free( file_version );
2099 continue;
2101 if ((file_size = get_disk_file_size( file->TargetPath )) != file->FileSize)
2103 file->state = msifs_overwrite;
2104 comp->Cost += file->FileSize - file_size;
2105 continue;
2107 if (file->hash.dwFileHashInfoSize && hash_matches( file ))
2109 TRACE("File hashes match, not overwriting\n");
2110 file->state = msifs_present;
2111 continue;
2113 file->state = msifs_overwrite;
2114 comp->Cost += file->FileSize - file_size;
2117 return ERROR_SUCCESS;
2121 * A lot is done in this function aside from just the costing.
2122 * The costing needs to be implemented at some point but for now I am going
2123 * to focus on the directory building
2126 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2128 static const WCHAR ExecSeqQuery[] =
2129 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2130 '`','D','i','r','e','c','t','o','r','y','`',0};
2131 static const WCHAR ConditionQuery[] =
2132 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2133 '`','C','o','n','d','i','t','i','o','n','`',0};
2134 static const WCHAR szCosting[] =
2135 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2136 static const WCHAR szlevel[] =
2137 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2138 static const WCHAR szOutOfDiskSpace[] =
2139 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2140 MSICOMPONENT *comp;
2141 UINT rc = ERROR_SUCCESS;
2142 MSIQUERY * view;
2143 LPWSTR level;
2145 TRACE("Building Directory properties\n");
2147 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2148 if (rc == ERROR_SUCCESS)
2150 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2151 package);
2152 msiobj_release(&view->hdr);
2155 /* read components states from the registry */
2156 ACTION_GetComponentInstallStates(package);
2157 ACTION_GetFeatureInstallStates(package);
2159 TRACE("Calculating file install states\n");
2160 set_file_install_states( package );
2162 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2164 TRACE("Evaluating feature conditions\n");
2166 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2167 if (rc == ERROR_SUCCESS)
2169 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2170 msiobj_release( &view->hdr );
2173 TRACE("Evaluating component conditions\n");
2175 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2177 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2179 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2180 comp->Enabled = FALSE;
2182 else
2183 comp->Enabled = TRUE;
2186 msi_set_property( package->db, szCosting, szOne );
2187 /* set default run level if not set */
2188 level = msi_dup_property( package->db, szlevel );
2189 if (!level)
2190 msi_set_property( package->db, szlevel, szOne );
2191 msi_free(level);
2193 /* FIXME: check volume disk space */
2194 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2196 return MSI_SetFeatureStates(package);
2199 /* OK this value is "interpreted" and then formatted based on the
2200 first few characters */
2201 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2202 DWORD *size)
2204 LPSTR data = NULL;
2206 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2208 if (value[1]=='x')
2210 LPWSTR ptr;
2211 CHAR byte[5];
2212 LPWSTR deformated = NULL;
2213 int count;
2215 deformat_string(package, &value[2], &deformated);
2217 /* binary value type */
2218 ptr = deformated;
2219 *type = REG_BINARY;
2220 if (strlenW(ptr)%2)
2221 *size = (strlenW(ptr)/2)+1;
2222 else
2223 *size = strlenW(ptr)/2;
2225 data = msi_alloc(*size);
2227 byte[0] = '0';
2228 byte[1] = 'x';
2229 byte[4] = 0;
2230 count = 0;
2231 /* if uneven pad with a zero in front */
2232 if (strlenW(ptr)%2)
2234 byte[2]= '0';
2235 byte[3]= *ptr;
2236 ptr++;
2237 data[count] = (BYTE)strtol(byte,NULL,0);
2238 count ++;
2239 TRACE("Uneven byte count\n");
2241 while (*ptr)
2243 byte[2]= *ptr;
2244 ptr++;
2245 byte[3]= *ptr;
2246 ptr++;
2247 data[count] = (BYTE)strtol(byte,NULL,0);
2248 count ++;
2250 msi_free(deformated);
2252 TRACE("Data %i bytes(%i)\n",*size,count);
2254 else
2256 LPWSTR deformated;
2257 LPWSTR p;
2258 DWORD d = 0;
2259 deformat_string(package, &value[1], &deformated);
2261 *type=REG_DWORD;
2262 *size = sizeof(DWORD);
2263 data = msi_alloc(*size);
2264 p = deformated;
2265 if (*p == '-')
2266 p++;
2267 while (*p)
2269 if ( (*p < '0') || (*p > '9') )
2270 break;
2271 d *= 10;
2272 d += (*p - '0');
2273 p++;
2275 if (deformated[0] == '-')
2276 d = -d;
2277 *(LPDWORD)data = d;
2278 TRACE("DWORD %i\n",*(LPDWORD)data);
2280 msi_free(deformated);
2283 else
2285 static const WCHAR szMulti[] = {'[','~',']',0};
2286 LPCWSTR ptr;
2287 *type=REG_SZ;
2289 if (value[0]=='#')
2291 if (value[1]=='%')
2293 ptr = &value[2];
2294 *type=REG_EXPAND_SZ;
2296 else
2297 ptr = &value[1];
2299 else
2300 ptr=value;
2302 if (strstrW(value,szMulti))
2303 *type = REG_MULTI_SZ;
2305 /* remove initial delimiter */
2306 if (!strncmpW(value, szMulti, 3))
2307 ptr = value + 3;
2309 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2311 /* add double NULL terminator */
2312 if (*type == REG_MULTI_SZ)
2314 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2315 data = msi_realloc_zero(data, *size);
2318 return data;
2321 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2323 const WCHAR *ret;
2325 switch (root)
2327 case -1:
2328 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2330 *root_key = HKEY_LOCAL_MACHINE;
2331 ret = szHLM;
2333 else
2335 *root_key = HKEY_CURRENT_USER;
2336 ret = szHCU;
2338 break;
2339 case 0:
2340 *root_key = HKEY_CLASSES_ROOT;
2341 ret = szHCR;
2342 break;
2343 case 1:
2344 *root_key = HKEY_CURRENT_USER;
2345 ret = szHCU;
2346 break;
2347 case 2:
2348 *root_key = HKEY_LOCAL_MACHINE;
2349 ret = szHLM;
2350 break;
2351 case 3:
2352 *root_key = HKEY_USERS;
2353 ret = szHU;
2354 break;
2355 default:
2356 ERR("Unknown root %i\n", root);
2357 return NULL;
2360 return ret;
2363 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2365 MSIPACKAGE *package = param;
2366 LPSTR value_data = NULL;
2367 HKEY root_key, hkey;
2368 DWORD type,size;
2369 LPWSTR deformated;
2370 LPCWSTR szRoot, component, name, key, value;
2371 MSICOMPONENT *comp;
2372 MSIRECORD * uirow;
2373 LPWSTR uikey;
2374 INT root;
2375 BOOL check_first = FALSE;
2376 UINT rc;
2378 ui_progress(package,2,0,0,0);
2380 value = NULL;
2381 key = NULL;
2382 uikey = NULL;
2383 name = NULL;
2385 component = MSI_RecordGetString(row, 6);
2386 comp = get_loaded_component(package,component);
2387 if (!comp)
2388 return ERROR_SUCCESS;
2390 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2392 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2393 comp->Action = comp->Installed;
2394 return ERROR_SUCCESS;
2396 comp->Action = INSTALLSTATE_LOCAL;
2398 name = MSI_RecordGetString(row, 4);
2399 if( MSI_RecordIsNull(row,5) && name )
2401 /* null values can have special meanings */
2402 if (name[0]=='-' && name[1] == 0)
2403 return ERROR_SUCCESS;
2404 else if ((name[0]=='+' && name[1] == 0) ||
2405 (name[0] == '*' && name[1] == 0))
2406 name = NULL;
2407 check_first = TRUE;
2410 root = MSI_RecordGetInteger(row,2);
2411 key = MSI_RecordGetString(row, 3);
2413 szRoot = get_root_key( package, root, &root_key );
2414 if (!szRoot)
2415 return ERROR_SUCCESS;
2417 deformat_string(package, key , &deformated);
2418 size = strlenW(deformated) + strlenW(szRoot) + 1;
2419 uikey = msi_alloc(size*sizeof(WCHAR));
2420 strcpyW(uikey,szRoot);
2421 strcatW(uikey,deformated);
2423 if (RegCreateKeyW( root_key, deformated, &hkey))
2425 ERR("Could not create key %s\n",debugstr_w(deformated));
2426 msi_free(deformated);
2427 msi_free(uikey);
2428 return ERROR_SUCCESS;
2430 msi_free(deformated);
2432 value = MSI_RecordGetString(row,5);
2433 if (value)
2434 value_data = parse_value(package, value, &type, &size);
2435 else
2437 value_data = (LPSTR)strdupW(szEmpty);
2438 size = sizeof(szEmpty);
2439 type = REG_SZ;
2442 deformat_string(package, name, &deformated);
2444 if (!check_first)
2446 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2447 debugstr_w(uikey));
2448 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2450 else
2452 DWORD sz = 0;
2453 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2454 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2456 TRACE("value %s of %s checked already exists\n",
2457 debugstr_w(deformated), debugstr_w(uikey));
2459 else
2461 TRACE("Checked and setting value %s of %s\n",
2462 debugstr_w(deformated), debugstr_w(uikey));
2463 if (deformated || size)
2464 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2467 RegCloseKey(hkey);
2469 uirow = MSI_CreateRecord(3);
2470 MSI_RecordSetStringW(uirow,2,deformated);
2471 MSI_RecordSetStringW(uirow,1,uikey);
2472 if (type == REG_SZ || type == REG_EXPAND_SZ)
2473 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2474 ui_actiondata(package,szWriteRegistryValues,uirow);
2475 msiobj_release( &uirow->hdr );
2477 msi_free(value_data);
2478 msi_free(deformated);
2479 msi_free(uikey);
2481 return ERROR_SUCCESS;
2484 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2486 UINT rc;
2487 MSIQUERY * view;
2488 static const WCHAR ExecSeqQuery[] =
2489 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2490 '`','R','e','g','i','s','t','r','y','`',0 };
2492 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2493 if (rc != ERROR_SUCCESS)
2494 return ERROR_SUCCESS;
2496 /* increment progress bar each time action data is sent */
2497 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2499 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2501 msiobj_release(&view->hdr);
2502 return rc;
2505 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2507 LONG res;
2508 HKEY hkey;
2509 DWORD num_subkeys, num_values;
2511 if (delete_key)
2513 if ((res = RegDeleteTreeW( hkey_root, key )))
2515 WARN("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2517 return;
2520 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2522 if ((res = RegDeleteValueW( hkey, value )))
2524 WARN("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2526 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2527 NULL, NULL, NULL, NULL );
2528 RegCloseKey( hkey );
2530 if (!res && !num_subkeys && !num_values)
2532 TRACE("Removing empty key %s\n", debugstr_w(key));
2533 RegDeleteKeyW( hkey_root, key );
2535 return;
2537 WARN("Failed to open key %s (%d)\n", debugstr_w(key), res);
2541 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2543 MSIPACKAGE *package = param;
2544 LPCWSTR component, name, key_str, root_key_str;
2545 LPWSTR deformated_key, deformated_name, ui_key_str;
2546 MSICOMPONENT *comp;
2547 MSIRECORD *uirow;
2548 BOOL delete_key = FALSE;
2549 HKEY hkey_root;
2550 UINT size;
2551 INT root;
2553 ui_progress( package, 2, 0, 0, 0 );
2555 component = MSI_RecordGetString( row, 6 );
2556 comp = get_loaded_component( package, component );
2557 if (!comp)
2558 return ERROR_SUCCESS;
2560 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2562 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2563 comp->Action = comp->Installed;
2564 return ERROR_SUCCESS;
2566 comp->Action = INSTALLSTATE_ABSENT;
2568 name = MSI_RecordGetString( row, 4 );
2569 if (MSI_RecordIsNull( row, 5 ) && name )
2571 if (name[0] == '+' && !name[1])
2572 return ERROR_SUCCESS;
2573 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2575 delete_key = TRUE;
2576 name = NULL;
2580 root = MSI_RecordGetInteger( row, 2 );
2581 key_str = MSI_RecordGetString( row, 3 );
2583 root_key_str = get_root_key( package, root, &hkey_root );
2584 if (!root_key_str)
2585 return ERROR_SUCCESS;
2587 deformat_string( package, key_str, &deformated_key );
2588 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2589 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2590 strcpyW( ui_key_str, root_key_str );
2591 strcatW( ui_key_str, deformated_key );
2593 deformat_string( package, name, &deformated_name );
2595 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2596 msi_free( deformated_key );
2598 uirow = MSI_CreateRecord( 2 );
2599 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2600 MSI_RecordSetStringW( uirow, 2, deformated_name );
2602 ui_actiondata( package, szRemoveRegistryValues, uirow );
2603 msiobj_release( &uirow->hdr );
2605 msi_free( ui_key_str );
2606 msi_free( deformated_name );
2607 return ERROR_SUCCESS;
2610 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2612 MSIPACKAGE *package = param;
2613 LPCWSTR component, name, key_str, root_key_str;
2614 LPWSTR deformated_key, deformated_name, ui_key_str;
2615 MSICOMPONENT *comp;
2616 MSIRECORD *uirow;
2617 BOOL delete_key = FALSE;
2618 HKEY hkey_root;
2619 UINT size;
2620 INT root;
2622 ui_progress( package, 2, 0, 0, 0 );
2624 component = MSI_RecordGetString( row, 5 );
2625 comp = get_loaded_component( package, component );
2626 if (!comp)
2627 return ERROR_SUCCESS;
2629 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2631 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2632 comp->Action = comp->Installed;
2633 return ERROR_SUCCESS;
2635 comp->Action = INSTALLSTATE_LOCAL;
2637 if ((name = MSI_RecordGetString( row, 4 )))
2639 if (name[0] == '-' && !name[1])
2641 delete_key = TRUE;
2642 name = NULL;
2646 root = MSI_RecordGetInteger( row, 2 );
2647 key_str = MSI_RecordGetString( row, 3 );
2649 root_key_str = get_root_key( package, root, &hkey_root );
2650 if (!root_key_str)
2651 return ERROR_SUCCESS;
2653 deformat_string( package, key_str, &deformated_key );
2654 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2655 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2656 strcpyW( ui_key_str, root_key_str );
2657 strcatW( ui_key_str, deformated_key );
2659 deformat_string( package, name, &deformated_name );
2661 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2662 msi_free( deformated_key );
2664 uirow = MSI_CreateRecord( 2 );
2665 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2666 MSI_RecordSetStringW( uirow, 2, deformated_name );
2668 ui_actiondata( package, szRemoveRegistryValues, uirow );
2669 msiobj_release( &uirow->hdr );
2671 msi_free( ui_key_str );
2672 msi_free( deformated_name );
2673 return ERROR_SUCCESS;
2676 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2678 UINT rc;
2679 MSIQUERY *view;
2680 static const WCHAR registry_query[] =
2681 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2682 '`','R','e','g','i','s','t','r','y','`',0 };
2683 static const WCHAR remove_registry_query[] =
2684 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2685 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2687 /* increment progress bar each time action data is sent */
2688 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2690 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2691 if (rc == ERROR_SUCCESS)
2693 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2694 msiobj_release( &view->hdr );
2695 if (rc != ERROR_SUCCESS)
2696 return rc;
2699 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2700 if (rc == ERROR_SUCCESS)
2702 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2703 msiobj_release( &view->hdr );
2704 if (rc != ERROR_SUCCESS)
2705 return rc;
2708 return ERROR_SUCCESS;
2711 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2713 package->script->CurrentlyScripting = TRUE;
2715 return ERROR_SUCCESS;
2719 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2721 MSICOMPONENT *comp;
2722 DWORD progress = 0;
2723 DWORD total = 0;
2724 static const WCHAR q1[]=
2725 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2726 '`','R','e','g','i','s','t','r','y','`',0};
2727 UINT rc;
2728 MSIQUERY * view;
2729 MSIFEATURE *feature;
2730 MSIFILE *file;
2732 TRACE("InstallValidate\n");
2734 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2735 if (rc == ERROR_SUCCESS)
2737 MSI_IterateRecords( view, &progress, NULL, package );
2738 msiobj_release( &view->hdr );
2739 total += progress * REG_PROGRESS_VALUE;
2742 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2743 total += COMPONENT_PROGRESS_VALUE;
2745 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2746 total += file->FileSize;
2748 ui_progress(package,0,total,0,0);
2750 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2752 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2753 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2754 feature->ActionRequest);
2757 return ERROR_SUCCESS;
2760 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2762 MSIPACKAGE* package = param;
2763 LPCWSTR cond = NULL;
2764 LPCWSTR message = NULL;
2765 UINT r;
2767 static const WCHAR title[]=
2768 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2770 cond = MSI_RecordGetString(row,1);
2772 r = MSI_EvaluateConditionW(package,cond);
2773 if (r == MSICONDITION_FALSE)
2775 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2777 LPWSTR deformated;
2778 message = MSI_RecordGetString(row,2);
2779 deformat_string(package,message,&deformated);
2780 MessageBoxW(NULL,deformated,title,MB_OK);
2781 msi_free(deformated);
2784 return ERROR_INSTALL_FAILURE;
2787 return ERROR_SUCCESS;
2790 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2792 UINT rc;
2793 MSIQUERY * view = NULL;
2794 static const WCHAR ExecSeqQuery[] =
2795 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2796 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2798 TRACE("Checking launch conditions\n");
2800 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2801 if (rc != ERROR_SUCCESS)
2802 return ERROR_SUCCESS;
2804 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2805 msiobj_release(&view->hdr);
2807 return rc;
2810 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2813 if (!cmp->KeyPath)
2814 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2816 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2818 MSIRECORD * row = 0;
2819 UINT root,len;
2820 LPWSTR deformated,buffer,deformated_name;
2821 LPCWSTR key,name;
2822 static const WCHAR ExecSeqQuery[] =
2823 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2824 '`','R','e','g','i','s','t','r','y','`',' ',
2825 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2826 ' ','=',' ' ,'\'','%','s','\'',0 };
2827 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2828 static const WCHAR fmt2[]=
2829 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2831 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2832 if (!row)
2833 return NULL;
2835 root = MSI_RecordGetInteger(row,2);
2836 key = MSI_RecordGetString(row, 3);
2837 name = MSI_RecordGetString(row, 4);
2838 deformat_string(package, key , &deformated);
2839 deformat_string(package, name, &deformated_name);
2841 len = strlenW(deformated) + 6;
2842 if (deformated_name)
2843 len+=strlenW(deformated_name);
2845 buffer = msi_alloc( len *sizeof(WCHAR));
2847 if (deformated_name)
2848 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2849 else
2850 sprintfW(buffer,fmt,root,deformated);
2852 msi_free(deformated);
2853 msi_free(deformated_name);
2854 msiobj_release(&row->hdr);
2856 return buffer;
2858 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2860 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2861 return NULL;
2863 else
2865 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2867 if (file)
2868 return strdupW( file->TargetPath );
2870 return NULL;
2873 static HKEY openSharedDLLsKey(void)
2875 HKEY hkey=0;
2876 static const WCHAR path[] =
2877 {'S','o','f','t','w','a','r','e','\\',
2878 'M','i','c','r','o','s','o','f','t','\\',
2879 'W','i','n','d','o','w','s','\\',
2880 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2881 'S','h','a','r','e','d','D','L','L','s',0};
2883 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2884 return hkey;
2887 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2889 HKEY hkey;
2890 DWORD count=0;
2891 DWORD type;
2892 DWORD sz = sizeof(count);
2893 DWORD rc;
2895 hkey = openSharedDLLsKey();
2896 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2897 if (rc != ERROR_SUCCESS)
2898 count = 0;
2899 RegCloseKey(hkey);
2900 return count;
2903 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2905 HKEY hkey;
2907 hkey = openSharedDLLsKey();
2908 if (count > 0)
2909 msi_reg_set_val_dword( hkey, path, count );
2910 else
2911 RegDeleteValueW(hkey,path);
2912 RegCloseKey(hkey);
2913 return count;
2917 * Return TRUE if the count should be written out and FALSE if not
2919 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2921 MSIFEATURE *feature;
2922 INT count = 0;
2923 BOOL write = FALSE;
2925 /* only refcount DLLs */
2926 if (comp->KeyPath == NULL ||
2927 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2928 comp->Attributes & msidbComponentAttributesODBCDataSource)
2929 write = FALSE;
2930 else
2932 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2933 write = (count > 0);
2935 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2936 write = TRUE;
2939 /* increment counts */
2940 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2942 ComponentList *cl;
2944 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2945 continue;
2947 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2949 if ( cl->component == comp )
2950 count++;
2954 /* decrement counts */
2955 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2957 ComponentList *cl;
2959 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2960 continue;
2962 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2964 if ( cl->component == comp )
2965 count--;
2969 /* ref count all the files in the component */
2970 if (write)
2972 MSIFILE *file;
2974 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2976 if (file->Component == comp)
2977 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2981 /* add a count for permanent */
2982 if (comp->Attributes & msidbComponentAttributesPermanent)
2983 count ++;
2985 comp->RefCount = count;
2987 if (write)
2988 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2991 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2993 WCHAR squished_pc[GUID_SIZE];
2994 WCHAR squished_cc[GUID_SIZE];
2995 UINT rc;
2996 MSICOMPONENT *comp;
2997 HKEY hkey;
2999 TRACE("\n");
3001 squash_guid(package->ProductCode,squished_pc);
3002 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
3004 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3006 MSIRECORD * uirow;
3008 ui_progress(package,2,0,0,0);
3009 if (!comp->ComponentId)
3010 continue;
3012 squash_guid(comp->ComponentId,squished_cc);
3014 msi_free(comp->FullKeypath);
3015 comp->FullKeypath = resolve_keypath( package, comp );
3017 ACTION_RefCountComponent( package, comp );
3019 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
3020 debugstr_w(comp->Component),
3021 debugstr_w(squished_cc),
3022 debugstr_w(comp->FullKeypath),
3023 comp->RefCount);
3025 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3026 comp->ActionRequest == INSTALLSTATE_SOURCE)
3028 if (!comp->FullKeypath)
3029 continue;
3031 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3032 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
3033 &hkey, TRUE);
3034 else
3035 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
3036 &hkey, TRUE);
3038 if (rc != ERROR_SUCCESS)
3039 continue;
3041 if (comp->Attributes & msidbComponentAttributesPermanent)
3043 static const WCHAR szPermKey[] =
3044 { '0','0','0','0','0','0','0','0','0','0','0','0',
3045 '0','0','0','0','0','0','0','0','0','0','0','0',
3046 '0','0','0','0','0','0','0','0',0 };
3048 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3051 if (comp->Action == INSTALLSTATE_LOCAL)
3052 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3053 else
3055 MSIFILE *file;
3056 MSIRECORD *row;
3057 LPWSTR ptr, ptr2;
3058 WCHAR source[MAX_PATH];
3059 WCHAR base[MAX_PATH];
3060 LPWSTR sourcepath;
3062 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3063 static const WCHAR query[] = {
3064 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3065 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3066 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3067 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3068 '`','D','i','s','k','I','d','`',0};
3070 file = get_loaded_file(package, comp->KeyPath);
3071 if (!file)
3072 continue;
3074 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3075 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3076 ptr2 = strrchrW(source, '\\') + 1;
3077 msiobj_release(&row->hdr);
3079 lstrcpyW(base, package->PackagePath);
3080 ptr = strrchrW(base, '\\');
3081 *(ptr + 1) = '\0';
3083 sourcepath = resolve_file_source(package, file);
3084 ptr = sourcepath + lstrlenW(base);
3085 lstrcpyW(ptr2, ptr);
3086 msi_free(sourcepath);
3088 msi_reg_set_val_str(hkey, squished_pc, source);
3090 RegCloseKey(hkey);
3092 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3094 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3095 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3096 else
3097 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3099 comp->Action = comp->ActionRequest;
3101 /* UI stuff */
3102 uirow = MSI_CreateRecord(3);
3103 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3104 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3105 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3106 ui_actiondata(package,szProcessComponents,uirow);
3107 msiobj_release( &uirow->hdr );
3110 return ERROR_SUCCESS;
3113 typedef struct {
3114 CLSID clsid;
3115 LPWSTR source;
3117 LPWSTR path;
3118 ITypeLib *ptLib;
3119 } typelib_struct;
3121 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3122 LPWSTR lpszName, LONG_PTR lParam)
3124 TLIBATTR *attr;
3125 typelib_struct *tl_struct = (typelib_struct*) lParam;
3126 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3127 int sz;
3128 HRESULT res;
3130 if (!IS_INTRESOURCE(lpszName))
3132 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3133 return TRUE;
3136 sz = strlenW(tl_struct->source)+4;
3137 sz *= sizeof(WCHAR);
3139 if ((INT_PTR)lpszName == 1)
3140 tl_struct->path = strdupW(tl_struct->source);
3141 else
3143 tl_struct->path = msi_alloc(sz);
3144 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3147 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3148 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3149 if (FAILED(res))
3151 msi_free(tl_struct->path);
3152 tl_struct->path = NULL;
3154 return TRUE;
3157 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3158 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3160 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3161 return FALSE;
3164 msi_free(tl_struct->path);
3165 tl_struct->path = NULL;
3167 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3168 ITypeLib_Release(tl_struct->ptLib);
3170 return TRUE;
3173 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3175 MSIPACKAGE* package = param;
3176 LPCWSTR component;
3177 MSICOMPONENT *comp;
3178 MSIFILE *file;
3179 typelib_struct tl_struct;
3180 ITypeLib *tlib;
3181 HMODULE module;
3182 HRESULT hr;
3184 component = MSI_RecordGetString(row,3);
3185 comp = get_loaded_component(package,component);
3186 if (!comp)
3187 return ERROR_SUCCESS;
3189 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3191 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3192 comp->Action = comp->Installed;
3193 return ERROR_SUCCESS;
3195 comp->Action = INSTALLSTATE_LOCAL;
3197 file = get_loaded_file( package, comp->KeyPath );
3198 if (!file)
3199 return ERROR_SUCCESS;
3201 ui_actiondata( package, szRegisterTypeLibraries, row );
3203 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3204 if (module)
3206 LPCWSTR guid;
3207 guid = MSI_RecordGetString(row,1);
3208 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3209 tl_struct.source = strdupW( file->TargetPath );
3210 tl_struct.path = NULL;
3212 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3213 (LONG_PTR)&tl_struct);
3215 if (tl_struct.path)
3217 LPWSTR help = NULL;
3218 LPCWSTR helpid;
3219 HRESULT res;
3221 helpid = MSI_RecordGetString(row,6);
3223 if (helpid)
3224 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3225 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3226 msi_free(help);
3228 if (FAILED(res))
3229 ERR("Failed to register type library %s\n",
3230 debugstr_w(tl_struct.path));
3231 else
3232 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3234 ITypeLib_Release(tl_struct.ptLib);
3235 msi_free(tl_struct.path);
3237 else
3238 ERR("Failed to load type library %s\n",
3239 debugstr_w(tl_struct.source));
3241 FreeLibrary(module);
3242 msi_free(tl_struct.source);
3244 else
3246 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3247 if (FAILED(hr))
3249 ERR("Failed to load type library: %08x\n", hr);
3250 return ERROR_INSTALL_FAILURE;
3253 ITypeLib_Release(tlib);
3256 return ERROR_SUCCESS;
3259 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3262 * OK this is a bit confusing.. I am given a _Component key and I believe
3263 * that the file that is being registered as a type library is the "key file
3264 * of that component" which I interpret to mean "The file in the KeyPath of
3265 * that component".
3267 UINT rc;
3268 MSIQUERY * view;
3269 static const WCHAR Query[] =
3270 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3271 '`','T','y','p','e','L','i','b','`',0};
3273 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3274 if (rc != ERROR_SUCCESS)
3275 return ERROR_SUCCESS;
3277 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3278 msiobj_release(&view->hdr);
3279 return rc;
3282 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3284 MSIPACKAGE *package = param;
3285 LPCWSTR component, guid;
3286 MSICOMPONENT *comp;
3287 GUID libid;
3288 UINT version;
3289 LCID language;
3290 SYSKIND syskind;
3291 HRESULT hr;
3293 component = MSI_RecordGetString( row, 3 );
3294 comp = get_loaded_component( package, component );
3295 if (!comp)
3296 return ERROR_SUCCESS;
3298 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3300 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3301 comp->Action = comp->Installed;
3302 return ERROR_SUCCESS;
3304 comp->Action = INSTALLSTATE_ABSENT;
3306 ui_actiondata( package, szUnregisterTypeLibraries, row );
3308 guid = MSI_RecordGetString( row, 1 );
3309 CLSIDFromString( (LPCWSTR)guid, &libid );
3310 version = MSI_RecordGetInteger( row, 4 );
3311 language = MSI_RecordGetInteger( row, 2 );
3313 #ifdef _WIN64
3314 syskind = SYS_WIN64;
3315 #else
3316 syskind = SYS_WIN32;
3317 #endif
3319 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3320 if (FAILED(hr))
3322 WARN("Failed to unregister typelib: %08x\n", hr);
3325 return ERROR_SUCCESS;
3328 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3330 UINT rc;
3331 MSIQUERY *view;
3332 static const WCHAR query[] =
3333 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3334 '`','T','y','p','e','L','i','b','`',0};
3336 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3337 if (rc != ERROR_SUCCESS)
3338 return ERROR_SUCCESS;
3340 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3341 msiobj_release( &view->hdr );
3342 return rc;
3345 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3347 static const WCHAR szlnk[] = {'.','l','n','k',0};
3348 LPCWSTR directory, extension;
3349 LPWSTR link_folder, link_file, filename;
3351 directory = MSI_RecordGetString( row, 2 );
3352 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3354 /* may be needed because of a bug somewhere else */
3355 create_full_pathW( link_folder );
3357 filename = msi_dup_record_field( row, 3 );
3358 reduce_to_longfilename( filename );
3360 extension = strchrW( filename, '.' );
3361 if (!extension || strcmpiW( extension, szlnk ))
3363 int len = strlenW( filename );
3364 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3365 memcpy( filename + len, szlnk, sizeof(szlnk) );
3367 link_file = build_directory_name( 2, link_folder, filename );
3368 msi_free( link_folder );
3369 msi_free( filename );
3371 return link_file;
3374 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3376 MSIPACKAGE *package = param;
3377 LPWSTR link_file, deformated, path;
3378 LPCWSTR component, target;
3379 MSICOMPONENT *comp;
3380 IShellLinkW *sl = NULL;
3381 IPersistFile *pf = NULL;
3382 HRESULT res;
3384 component = MSI_RecordGetString(row, 4);
3385 comp = get_loaded_component(package, component);
3386 if (!comp)
3387 return ERROR_SUCCESS;
3389 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3391 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3392 comp->Action = comp->Installed;
3393 return ERROR_SUCCESS;
3395 comp->Action = INSTALLSTATE_LOCAL;
3397 ui_actiondata(package,szCreateShortcuts,row);
3399 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3400 &IID_IShellLinkW, (LPVOID *) &sl );
3402 if (FAILED( res ))
3404 ERR("CLSID_ShellLink not available\n");
3405 goto err;
3408 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3409 if (FAILED( res ))
3411 ERR("QueryInterface(IID_IPersistFile) failed\n");
3412 goto err;
3415 target = MSI_RecordGetString(row, 5);
3416 if (strchrW(target, '['))
3418 deformat_string(package, target, &deformated);
3419 IShellLinkW_SetPath(sl,deformated);
3420 msi_free(deformated);
3422 else
3424 FIXME("poorly handled shortcut format, advertised shortcut\n");
3425 IShellLinkW_SetPath(sl,comp->FullKeypath);
3428 if (!MSI_RecordIsNull(row,6))
3430 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3431 deformat_string(package, arguments, &deformated);
3432 IShellLinkW_SetArguments(sl,deformated);
3433 msi_free(deformated);
3436 if (!MSI_RecordIsNull(row,7))
3438 LPCWSTR description = MSI_RecordGetString(row, 7);
3439 IShellLinkW_SetDescription(sl, description);
3442 if (!MSI_RecordIsNull(row,8))
3443 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3445 if (!MSI_RecordIsNull(row,9))
3447 INT index;
3448 LPCWSTR icon = MSI_RecordGetString(row, 9);
3450 path = build_icon_path(package, icon);
3451 index = MSI_RecordGetInteger(row,10);
3453 /* no value means 0 */
3454 if (index == MSI_NULL_INTEGER)
3455 index = 0;
3457 IShellLinkW_SetIconLocation(sl, path, index);
3458 msi_free(path);
3461 if (!MSI_RecordIsNull(row,11))
3462 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3464 if (!MSI_RecordIsNull(row,12))
3466 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3467 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3468 if (path)
3469 IShellLinkW_SetWorkingDirectory(sl, path);
3470 msi_free(path);
3473 link_file = get_link_file(package, row);
3475 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3476 IPersistFile_Save(pf, link_file, FALSE);
3478 msi_free(link_file);
3480 err:
3481 if (pf)
3482 IPersistFile_Release( pf );
3483 if (sl)
3484 IShellLinkW_Release( sl );
3486 return ERROR_SUCCESS;
3489 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3491 UINT rc;
3492 HRESULT res;
3493 MSIQUERY * view;
3494 static const WCHAR Query[] =
3495 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3496 '`','S','h','o','r','t','c','u','t','`',0};
3498 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3499 if (rc != ERROR_SUCCESS)
3500 return ERROR_SUCCESS;
3502 res = CoInitialize( NULL );
3504 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3505 msiobj_release(&view->hdr);
3507 if (SUCCEEDED(res))
3508 CoUninitialize();
3510 return rc;
3513 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3515 MSIPACKAGE *package = param;
3516 LPWSTR link_file;
3517 LPCWSTR component;
3518 MSICOMPONENT *comp;
3520 component = MSI_RecordGetString( row, 4 );
3521 comp = get_loaded_component( package, component );
3522 if (!comp)
3523 return ERROR_SUCCESS;
3525 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3527 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3528 comp->Action = comp->Installed;
3529 return ERROR_SUCCESS;
3531 comp->Action = INSTALLSTATE_ABSENT;
3533 ui_actiondata( package, szRemoveShortcuts, row );
3535 link_file = get_link_file( package, row );
3537 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3538 if (!DeleteFileW( link_file ))
3540 WARN("Failed to remove shortcut file %u\n", GetLastError());
3542 msi_free( link_file );
3544 return ERROR_SUCCESS;
3547 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3549 UINT rc;
3550 MSIQUERY *view;
3551 static const WCHAR query[] =
3552 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3553 '`','S','h','o','r','t','c','u','t','`',0};
3555 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3556 if (rc != ERROR_SUCCESS)
3557 return ERROR_SUCCESS;
3559 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3560 msiobj_release( &view->hdr );
3562 return rc;
3565 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3567 MSIPACKAGE* package = param;
3568 HANDLE the_file;
3569 LPWSTR FilePath;
3570 LPCWSTR FileName;
3571 CHAR buffer[1024];
3572 DWORD sz;
3573 UINT rc;
3575 FileName = MSI_RecordGetString(row,1);
3576 if (!FileName)
3578 ERR("Unable to get FileName\n");
3579 return ERROR_SUCCESS;
3582 FilePath = build_icon_path(package,FileName);
3584 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3586 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3587 FILE_ATTRIBUTE_NORMAL, NULL);
3589 if (the_file == INVALID_HANDLE_VALUE)
3591 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3592 msi_free(FilePath);
3593 return ERROR_SUCCESS;
3598 DWORD write;
3599 sz = 1024;
3600 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3601 if (rc != ERROR_SUCCESS)
3603 ERR("Failed to get stream\n");
3604 CloseHandle(the_file);
3605 DeleteFileW(FilePath);
3606 break;
3608 WriteFile(the_file,buffer,sz,&write,NULL);
3609 } while (sz == 1024);
3611 msi_free(FilePath);
3612 CloseHandle(the_file);
3614 return ERROR_SUCCESS;
3617 static UINT msi_publish_icons(MSIPACKAGE *package)
3619 UINT r;
3620 MSIQUERY *view;
3622 static const WCHAR query[]= {
3623 'S','E','L','E','C','T',' ','*',' ',
3624 'F','R','O','M',' ','`','I','c','o','n','`',0};
3626 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3627 if (r == ERROR_SUCCESS)
3629 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3630 msiobj_release(&view->hdr);
3633 return ERROR_SUCCESS;
3636 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3638 UINT r;
3639 HKEY source;
3640 LPWSTR buffer;
3641 MSIMEDIADISK *disk;
3642 MSISOURCELISTINFO *info;
3644 r = RegCreateKeyW(hkey, szSourceList, &source);
3645 if (r != ERROR_SUCCESS)
3646 return r;
3648 RegCloseKey(source);
3650 buffer = strrchrW(package->PackagePath, '\\') + 1;
3651 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3652 package->Context, MSICODE_PRODUCT,
3653 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3654 if (r != ERROR_SUCCESS)
3655 return r;
3657 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3658 package->Context, MSICODE_PRODUCT,
3659 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3660 if (r != ERROR_SUCCESS)
3661 return r;
3663 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3664 package->Context, MSICODE_PRODUCT,
3665 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3666 if (r != ERROR_SUCCESS)
3667 return r;
3669 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3671 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3672 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3673 info->options, info->value);
3674 else
3675 MsiSourceListSetInfoW(package->ProductCode, NULL,
3676 info->context, info->options,
3677 info->property, info->value);
3680 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3682 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3683 disk->context, disk->options,
3684 disk->disk_id, disk->volume_label, disk->disk_prompt);
3687 return ERROR_SUCCESS;
3690 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3692 MSIHANDLE hdb, suminfo;
3693 WCHAR guids[MAX_PATH];
3694 WCHAR packcode[SQUISH_GUID_SIZE];
3695 LPWSTR buffer;
3696 LPWSTR ptr;
3697 DWORD langid;
3698 DWORD size;
3699 UINT r;
3701 static const WCHAR szProductLanguage[] =
3702 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3703 static const WCHAR szARPProductIcon[] =
3704 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3705 static const WCHAR szProductVersion[] =
3706 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3707 static const WCHAR szAssignment[] =
3708 {'A','s','s','i','g','n','m','e','n','t',0};
3709 static const WCHAR szAdvertiseFlags[] =
3710 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3711 static const WCHAR szClients[] =
3712 {'C','l','i','e','n','t','s',0};
3713 static const WCHAR szColon[] = {':',0};
3715 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3716 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3717 msi_free(buffer);
3719 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3720 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3722 /* FIXME */
3723 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3725 buffer = msi_dup_property(package->db, szARPProductIcon);
3726 if (buffer)
3728 LPWSTR path = build_icon_path(package,buffer);
3729 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3730 msi_free(path);
3731 msi_free(buffer);
3734 buffer = msi_dup_property(package->db, szProductVersion);
3735 if (buffer)
3737 DWORD verdword = msi_version_str_to_dword(buffer);
3738 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3739 msi_free(buffer);
3742 msi_reg_set_val_dword(hkey, szAssignment, 0);
3743 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3744 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3745 msi_reg_set_val_str(hkey, szClients, szColon);
3747 hdb = alloc_msihandle(&package->db->hdr);
3748 if (!hdb)
3749 return ERROR_NOT_ENOUGH_MEMORY;
3751 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3752 MsiCloseHandle(hdb);
3753 if (r != ERROR_SUCCESS)
3754 goto done;
3756 size = MAX_PATH;
3757 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3758 NULL, guids, &size);
3759 if (r != ERROR_SUCCESS)
3760 goto done;
3762 ptr = strchrW(guids, ';');
3763 if (ptr) *ptr = 0;
3764 squash_guid(guids, packcode);
3765 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3767 done:
3768 MsiCloseHandle(suminfo);
3769 return ERROR_SUCCESS;
3772 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3774 UINT r;
3775 HKEY hkey;
3776 LPWSTR upgrade;
3777 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3779 static const WCHAR szUpgradeCode[] =
3780 {'U','p','g','r','a','d','e','C','o','d','e',0};
3782 upgrade = msi_dup_property(package->db, szUpgradeCode);
3783 if (!upgrade)
3784 return ERROR_SUCCESS;
3786 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3788 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3789 if (r != ERROR_SUCCESS)
3790 goto done;
3792 else
3794 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3795 if (r != ERROR_SUCCESS)
3796 goto done;
3799 squash_guid(package->ProductCode, squashed_pc);
3800 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3802 RegCloseKey(hkey);
3804 done:
3805 msi_free(upgrade);
3806 return r;
3809 static BOOL msi_check_publish(MSIPACKAGE *package)
3811 MSIFEATURE *feature;
3813 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3815 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3816 return TRUE;
3819 return FALSE;
3822 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3824 MSIFEATURE *feature;
3826 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3828 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3829 return FALSE;
3832 return TRUE;
3835 static UINT msi_publish_patches( MSIPACKAGE *package, HKEY prodkey )
3837 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
3838 WCHAR patch_squashed[GUID_SIZE];
3839 HKEY patches_key = NULL, product_patches_key;
3840 LONG res;
3841 MSIPATCHINFO *patch;
3842 UINT r;
3843 WCHAR *p, *all_patches = NULL;
3844 DWORD len = 0;
3846 res = RegCreateKeyExW( prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
3847 if (res != ERROR_SUCCESS)
3848 return ERROR_FUNCTION_FAILED;
3850 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
3851 if (r != ERROR_SUCCESS)
3852 goto done;
3854 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3856 squash_guid( patch->patchcode, patch_squashed );
3857 len += strlenW( patch_squashed ) + 1;
3860 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
3861 if (!all_patches)
3862 goto done;
3864 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3866 HKEY patch_key;
3868 squash_guid( patch->patchcode, p );
3869 p += strlenW( p ) + 1;
3871 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
3872 (const BYTE *)patch->transforms,
3873 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
3874 if (res != ERROR_SUCCESS)
3875 goto done;
3877 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
3878 if (r != ERROR_SUCCESS)
3879 goto done;
3881 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
3882 (const BYTE *)patch->localfile,
3883 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
3884 RegCloseKey( patch_key );
3885 if (res != ERROR_SUCCESS)
3886 goto done;
3888 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
3889 RegCloseKey( patch_key );
3890 if (res != ERROR_SUCCESS)
3891 goto done;
3894 all_patches[len] = 0;
3895 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
3896 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3897 if (res != ERROR_SUCCESS)
3898 goto done;
3900 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
3901 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3902 if (res != ERROR_SUCCESS)
3903 r = ERROR_FUNCTION_FAILED;
3905 done:
3906 RegCloseKey( product_patches_key );
3907 RegCloseKey( patches_key );
3908 msi_free( all_patches );
3909 return r;
3913 * 99% of the work done here is only done for
3914 * advertised installs. However this is where the
3915 * Icon table is processed and written out
3916 * so that is what I am going to do here.
3918 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3920 UINT rc;
3921 HKEY hukey = NULL, hudkey = NULL;
3922 MSIRECORD *uirow;
3924 /* FIXME: also need to publish if the product is in advertise mode */
3925 if (!msi_check_publish(package))
3926 return ERROR_SUCCESS;
3928 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3929 &hukey, TRUE);
3930 if (rc != ERROR_SUCCESS)
3931 goto end;
3933 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3934 NULL, &hudkey, TRUE);
3935 if (rc != ERROR_SUCCESS)
3936 goto end;
3938 rc = msi_publish_upgrade_code(package);
3939 if (rc != ERROR_SUCCESS)
3940 goto end;
3942 if (!list_empty(&package->patches))
3944 rc = msi_publish_patches(package, hukey);
3945 if (rc != ERROR_SUCCESS)
3946 goto end;
3949 rc = msi_publish_product_properties(package, hukey);
3950 if (rc != ERROR_SUCCESS)
3951 goto end;
3953 rc = msi_publish_sourcelist(package, hukey);
3954 if (rc != ERROR_SUCCESS)
3955 goto end;
3957 rc = msi_publish_icons(package);
3959 end:
3960 uirow = MSI_CreateRecord( 1 );
3961 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
3962 ui_actiondata( package, szPublishProduct, uirow );
3963 msiobj_release( &uirow->hdr );
3965 RegCloseKey(hukey);
3966 RegCloseKey(hudkey);
3968 return rc;
3971 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
3973 WCHAR *filename, *ptr, *folder, *ret;
3974 const WCHAR *dirprop;
3976 filename = msi_dup_record_field( row, 2 );
3977 if (filename && (ptr = strchrW( filename, '|' )))
3978 ptr++;
3979 else
3980 ptr = filename;
3982 dirprop = MSI_RecordGetString( row, 3 );
3983 if (dirprop)
3985 folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
3986 if (!folder)
3987 folder = msi_dup_property( package->db, dirprop );
3989 else
3990 folder = msi_dup_property( package->db, szWindowsFolder );
3992 if (!folder)
3994 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
3995 msi_free( filename );
3996 return NULL;
3999 ret = build_directory_name( 2, folder, ptr );
4001 msi_free( filename );
4002 msi_free( folder );
4003 return ret;
4006 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4008 MSIPACKAGE *package = param;
4009 LPCWSTR component, section, key, value, identifier;
4010 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4011 MSIRECORD * uirow;
4012 INT action;
4013 MSICOMPONENT *comp;
4015 component = MSI_RecordGetString(row, 8);
4016 comp = get_loaded_component(package,component);
4017 if (!comp)
4018 return ERROR_SUCCESS;
4020 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4022 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4023 comp->Action = comp->Installed;
4024 return ERROR_SUCCESS;
4026 comp->Action = INSTALLSTATE_LOCAL;
4028 identifier = MSI_RecordGetString(row,1);
4029 section = MSI_RecordGetString(row,4);
4030 key = MSI_RecordGetString(row,5);
4031 value = MSI_RecordGetString(row,6);
4032 action = MSI_RecordGetInteger(row,7);
4034 deformat_string(package,section,&deformated_section);
4035 deformat_string(package,key,&deformated_key);
4036 deformat_string(package,value,&deformated_value);
4038 fullname = get_ini_file_name(package, row);
4040 if (action == 0)
4042 TRACE("Adding value %s to section %s in %s\n",
4043 debugstr_w(deformated_key), debugstr_w(deformated_section),
4044 debugstr_w(fullname));
4045 WritePrivateProfileStringW(deformated_section, deformated_key,
4046 deformated_value, fullname);
4048 else if (action == 1)
4050 WCHAR returned[10];
4051 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4052 returned, 10, fullname);
4053 if (returned[0] == 0)
4055 TRACE("Adding value %s to section %s in %s\n",
4056 debugstr_w(deformated_key), debugstr_w(deformated_section),
4057 debugstr_w(fullname));
4059 WritePrivateProfileStringW(deformated_section, deformated_key,
4060 deformated_value, fullname);
4063 else if (action == 3)
4064 FIXME("Append to existing section not yet implemented\n");
4066 uirow = MSI_CreateRecord(4);
4067 MSI_RecordSetStringW(uirow,1,identifier);
4068 MSI_RecordSetStringW(uirow,2,deformated_section);
4069 MSI_RecordSetStringW(uirow,3,deformated_key);
4070 MSI_RecordSetStringW(uirow,4,deformated_value);
4071 ui_actiondata(package,szWriteIniValues,uirow);
4072 msiobj_release( &uirow->hdr );
4074 msi_free(fullname);
4075 msi_free(deformated_key);
4076 msi_free(deformated_value);
4077 msi_free(deformated_section);
4078 return ERROR_SUCCESS;
4081 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4083 UINT rc;
4084 MSIQUERY * view;
4085 static const WCHAR ExecSeqQuery[] =
4086 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4087 '`','I','n','i','F','i','l','e','`',0};
4089 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4090 if (rc != ERROR_SUCCESS)
4092 TRACE("no IniFile table\n");
4093 return ERROR_SUCCESS;
4096 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4097 msiobj_release(&view->hdr);
4098 return rc;
4101 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4103 MSIPACKAGE *package = param;
4104 LPCWSTR component, section, key, value, identifier;
4105 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4106 MSICOMPONENT *comp;
4107 MSIRECORD *uirow;
4108 INT action;
4110 component = MSI_RecordGetString( row, 8 );
4111 comp = get_loaded_component( package, component );
4112 if (!comp)
4113 return ERROR_SUCCESS;
4115 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4117 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4118 comp->Action = comp->Installed;
4119 return ERROR_SUCCESS;
4121 comp->Action = INSTALLSTATE_ABSENT;
4123 identifier = MSI_RecordGetString( row, 1 );
4124 section = MSI_RecordGetString( row, 4 );
4125 key = MSI_RecordGetString( row, 5 );
4126 value = MSI_RecordGetString( row, 6 );
4127 action = MSI_RecordGetInteger( row, 7 );
4129 deformat_string( package, section, &deformated_section );
4130 deformat_string( package, key, &deformated_key );
4131 deformat_string( package, value, &deformated_value );
4133 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4135 filename = get_ini_file_name( package, row );
4137 TRACE("Removing key %s from section %s in %s\n",
4138 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4140 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4142 WARN("Unable to remove key %u\n", GetLastError());
4144 msi_free( filename );
4146 else
4147 FIXME("Unsupported action %d\n", action);
4150 uirow = MSI_CreateRecord( 4 );
4151 MSI_RecordSetStringW( uirow, 1, identifier );
4152 MSI_RecordSetStringW( uirow, 2, deformated_section );
4153 MSI_RecordSetStringW( uirow, 3, deformated_key );
4154 MSI_RecordSetStringW( uirow, 4, deformated_value );
4155 ui_actiondata( package, szRemoveIniValues, uirow );
4156 msiobj_release( &uirow->hdr );
4158 msi_free( deformated_key );
4159 msi_free( deformated_value );
4160 msi_free( deformated_section );
4161 return ERROR_SUCCESS;
4164 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4166 MSIPACKAGE *package = param;
4167 LPCWSTR component, section, key, value, identifier;
4168 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4169 MSICOMPONENT *comp;
4170 MSIRECORD *uirow;
4171 INT action;
4173 component = MSI_RecordGetString( row, 8 );
4174 comp = get_loaded_component( package, component );
4175 if (!comp)
4176 return ERROR_SUCCESS;
4178 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4180 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4181 comp->Action = comp->Installed;
4182 return ERROR_SUCCESS;
4184 comp->Action = INSTALLSTATE_LOCAL;
4186 identifier = MSI_RecordGetString( row, 1 );
4187 section = MSI_RecordGetString( row, 4 );
4188 key = MSI_RecordGetString( row, 5 );
4189 value = MSI_RecordGetString( row, 6 );
4190 action = MSI_RecordGetInteger( row, 7 );
4192 deformat_string( package, section, &deformated_section );
4193 deformat_string( package, key, &deformated_key );
4194 deformat_string( package, value, &deformated_value );
4196 if (action == msidbIniFileActionRemoveLine)
4198 filename = get_ini_file_name( package, row );
4200 TRACE("Removing key %s from section %s in %s\n",
4201 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4203 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4205 WARN("Unable to remove key %u\n", GetLastError());
4207 msi_free( filename );
4209 else
4210 FIXME("Unsupported action %d\n", action);
4212 uirow = MSI_CreateRecord( 4 );
4213 MSI_RecordSetStringW( uirow, 1, identifier );
4214 MSI_RecordSetStringW( uirow, 2, deformated_section );
4215 MSI_RecordSetStringW( uirow, 3, deformated_key );
4216 MSI_RecordSetStringW( uirow, 4, deformated_value );
4217 ui_actiondata( package, szRemoveIniValues, uirow );
4218 msiobj_release( &uirow->hdr );
4220 msi_free( deformated_key );
4221 msi_free( deformated_value );
4222 msi_free( deformated_section );
4223 return ERROR_SUCCESS;
4226 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4228 UINT rc;
4229 MSIQUERY *view;
4230 static const WCHAR query[] =
4231 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4232 '`','I','n','i','F','i','l','e','`',0};
4233 static const WCHAR remove_query[] =
4234 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4235 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4237 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4238 if (rc == ERROR_SUCCESS)
4240 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4241 msiobj_release( &view->hdr );
4242 if (rc != ERROR_SUCCESS)
4243 return rc;
4246 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4247 if (rc == ERROR_SUCCESS)
4249 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4250 msiobj_release( &view->hdr );
4251 if (rc != ERROR_SUCCESS)
4252 return rc;
4255 return ERROR_SUCCESS;
4258 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4260 MSIPACKAGE *package = param;
4261 LPCWSTR filename;
4262 LPWSTR FullName;
4263 MSIFILE *file;
4264 DWORD len;
4265 static const WCHAR ExeStr[] =
4266 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
4267 static const WCHAR close[] = {'\"',0};
4268 STARTUPINFOW si;
4269 PROCESS_INFORMATION info;
4270 BOOL brc;
4271 MSIRECORD *uirow;
4272 LPWSTR uipath, p;
4274 memset(&si,0,sizeof(STARTUPINFOW));
4276 filename = MSI_RecordGetString(row,1);
4277 file = get_loaded_file( package, filename );
4279 if (!file)
4281 ERR("Unable to find file id %s\n",debugstr_w(filename));
4282 return ERROR_SUCCESS;
4285 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
4287 FullName = msi_alloc(len*sizeof(WCHAR));
4288 strcpyW(FullName,ExeStr);
4289 strcatW( FullName, file->TargetPath );
4290 strcatW(FullName,close);
4292 TRACE("Registering %s\n",debugstr_w(FullName));
4293 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
4294 &si, &info);
4296 if (brc)
4298 CloseHandle(info.hThread);
4299 msi_dialog_check_messages(info.hProcess);
4300 CloseHandle(info.hProcess);
4303 uirow = MSI_CreateRecord( 2 );
4304 MSI_RecordSetStringW( uirow, 1, filename );
4305 uipath = strdupW( file->TargetPath );
4306 if ((p = strrchrW( uipath,'\\' ))) *p = 0;
4307 MSI_RecordSetStringW( uirow, 2, uipath );
4308 ui_actiondata( package, szSelfRegModules, uirow );
4309 msiobj_release( &uirow->hdr );
4311 msi_free( FullName );
4312 msi_free( uipath );
4313 return ERROR_SUCCESS;
4316 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4318 UINT rc;
4319 MSIQUERY * view;
4320 static const WCHAR ExecSeqQuery[] =
4321 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4322 '`','S','e','l','f','R','e','g','`',0};
4324 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4325 if (rc != ERROR_SUCCESS)
4327 TRACE("no SelfReg table\n");
4328 return ERROR_SUCCESS;
4331 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4332 msiobj_release(&view->hdr);
4334 return ERROR_SUCCESS;
4337 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4339 static const WCHAR regsvr32[] =
4340 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
4341 static const WCHAR close[] = {'\"',0};
4342 MSIPACKAGE *package = param;
4343 LPCWSTR filename;
4344 LPWSTR cmdline;
4345 MSIFILE *file;
4346 DWORD len;
4347 STARTUPINFOW si;
4348 PROCESS_INFORMATION pi;
4349 BOOL ret;
4350 MSIRECORD *uirow;
4351 LPWSTR uipath, p;
4353 memset( &si, 0, sizeof(STARTUPINFOW) );
4355 filename = MSI_RecordGetString( row, 1 );
4356 file = get_loaded_file( package, filename );
4358 if (!file)
4360 ERR("Unable to find file id %s\n", debugstr_w(filename));
4361 return ERROR_SUCCESS;
4364 len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
4366 cmdline = msi_alloc( len * sizeof(WCHAR) );
4367 strcpyW( cmdline, regsvr32 );
4368 strcatW( cmdline, file->TargetPath );
4369 strcatW( cmdline, close );
4371 TRACE("Unregistering %s\n", debugstr_w(cmdline));
4373 ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
4374 if (ret)
4376 CloseHandle( pi.hThread );
4377 msi_dialog_check_messages( pi.hProcess );
4378 CloseHandle( pi.hProcess );
4381 uirow = MSI_CreateRecord( 2 );
4382 MSI_RecordSetStringW( uirow, 1, filename );
4383 uipath = strdupW( file->TargetPath );
4384 if ((p = strrchrW( uipath,'\\' ))) *p = 0;
4385 MSI_RecordSetStringW( uirow, 2, uipath );
4386 ui_actiondata( package, szSelfUnregModules, uirow );
4387 msiobj_release( &uirow->hdr );
4389 msi_free( cmdline );
4390 msi_free( uipath );
4391 return ERROR_SUCCESS;
4394 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4396 UINT rc;
4397 MSIQUERY *view;
4398 static const WCHAR query[] =
4399 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4400 '`','S','e','l','f','R','e','g','`',0};
4402 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4403 if (rc != ERROR_SUCCESS)
4405 TRACE("no SelfReg table\n");
4406 return ERROR_SUCCESS;
4409 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4410 msiobj_release( &view->hdr );
4412 return ERROR_SUCCESS;
4415 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4417 MSIFEATURE *feature;
4418 UINT rc;
4419 HKEY hkey = NULL, userdata = NULL;
4421 if (!msi_check_publish(package))
4422 return ERROR_SUCCESS;
4424 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4425 &hkey, TRUE);
4426 if (rc != ERROR_SUCCESS)
4427 goto end;
4429 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4430 &userdata, TRUE);
4431 if (rc != ERROR_SUCCESS)
4432 goto end;
4434 /* here the guids are base 85 encoded */
4435 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4437 ComponentList *cl;
4438 LPWSTR data = NULL;
4439 GUID clsid;
4440 INT size;
4441 BOOL absent = FALSE;
4442 MSIRECORD *uirow;
4444 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4445 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4446 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4448 size = 1;
4449 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4451 size += 21;
4453 if (feature->Feature_Parent)
4454 size += strlenW( feature->Feature_Parent )+2;
4456 data = msi_alloc(size * sizeof(WCHAR));
4458 data[0] = 0;
4459 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4461 MSICOMPONENT* component = cl->component;
4462 WCHAR buf[21];
4464 buf[0] = 0;
4465 if (component->ComponentId)
4467 TRACE("From %s\n",debugstr_w(component->ComponentId));
4468 CLSIDFromString(component->ComponentId, &clsid);
4469 encode_base85_guid(&clsid,buf);
4470 TRACE("to %s\n",debugstr_w(buf));
4471 strcatW(data,buf);
4475 if (feature->Feature_Parent)
4477 static const WCHAR sep[] = {'\2',0};
4478 strcatW(data,sep);
4479 strcatW(data,feature->Feature_Parent);
4482 msi_reg_set_val_str( userdata, feature->Feature, data );
4483 msi_free(data);
4485 size = 0;
4486 if (feature->Feature_Parent)
4487 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4488 if (!absent)
4490 size += sizeof(WCHAR);
4491 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4492 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4494 else
4496 size += 2*sizeof(WCHAR);
4497 data = msi_alloc(size);
4498 data[0] = 0x6;
4499 data[1] = 0;
4500 if (feature->Feature_Parent)
4501 strcpyW( &data[1], feature->Feature_Parent );
4502 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4503 (LPBYTE)data,size);
4504 msi_free(data);
4507 /* the UI chunk */
4508 uirow = MSI_CreateRecord( 1 );
4509 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4510 ui_actiondata( package, szPublishFeatures, uirow);
4511 msiobj_release( &uirow->hdr );
4512 /* FIXME: call ui_progress? */
4515 end:
4516 RegCloseKey(hkey);
4517 RegCloseKey(userdata);
4518 return rc;
4521 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4523 UINT r;
4524 HKEY hkey;
4525 MSIRECORD *uirow;
4527 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4529 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4530 &hkey, FALSE);
4531 if (r == ERROR_SUCCESS)
4533 RegDeleteValueW(hkey, feature->Feature);
4534 RegCloseKey(hkey);
4537 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4538 &hkey, FALSE);
4539 if (r == ERROR_SUCCESS)
4541 RegDeleteValueW(hkey, feature->Feature);
4542 RegCloseKey(hkey);
4545 uirow = MSI_CreateRecord( 1 );
4546 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4547 ui_actiondata( package, szUnpublishFeatures, uirow );
4548 msiobj_release( &uirow->hdr );
4550 return ERROR_SUCCESS;
4553 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4555 MSIFEATURE *feature;
4557 if (!msi_check_unpublish(package))
4558 return ERROR_SUCCESS;
4560 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4562 msi_unpublish_feature(package, feature);
4565 return ERROR_SUCCESS;
4568 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4570 SYSTEMTIME systime;
4571 DWORD size, langid;
4572 WCHAR date[9], *val, *buffer;
4573 const WCHAR *prop, *key;
4575 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4576 static const WCHAR szWindowsInstaller[] =
4577 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4578 static const WCHAR modpath_fmt[] =
4579 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4580 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4581 static const WCHAR szModifyPath[] =
4582 {'M','o','d','i','f','y','P','a','t','h',0};
4583 static const WCHAR szUninstallString[] =
4584 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4585 static const WCHAR szEstimatedSize[] =
4586 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4587 static const WCHAR szProductLanguage[] =
4588 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4589 static const WCHAR szProductVersion[] =
4590 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4591 static const WCHAR szDisplayVersion[] =
4592 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4593 static const WCHAR szInstallSource[] =
4594 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4595 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4596 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4597 static const WCHAR szAuthorizedCDFPrefix[] =
4598 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4599 static const WCHAR szARPCONTACT[] =
4600 {'A','R','P','C','O','N','T','A','C','T',0};
4601 static const WCHAR szContact[] =
4602 {'C','o','n','t','a','c','t',0};
4603 static const WCHAR szARPCOMMENTS[] =
4604 {'A','R','P','C','O','M','M','E','N','T','S',0};
4605 static const WCHAR szComments[] =
4606 {'C','o','m','m','e','n','t','s',0};
4607 static const WCHAR szProductName[] =
4608 {'P','r','o','d','u','c','t','N','a','m','e',0};
4609 static const WCHAR szDisplayName[] =
4610 {'D','i','s','p','l','a','y','N','a','m','e',0};
4611 static const WCHAR szARPHELPLINK[] =
4612 {'A','R','P','H','E','L','P','L','I','N','K',0};
4613 static const WCHAR szHelpLink[] =
4614 {'H','e','l','p','L','i','n','k',0};
4615 static const WCHAR szARPHELPTELEPHONE[] =
4616 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4617 static const WCHAR szHelpTelephone[] =
4618 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4619 static const WCHAR szARPINSTALLLOCATION[] =
4620 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4621 static const WCHAR szInstallLocation[] =
4622 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4623 static const WCHAR szManufacturer[] =
4624 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4625 static const WCHAR szPublisher[] =
4626 {'P','u','b','l','i','s','h','e','r',0};
4627 static const WCHAR szARPREADME[] =
4628 {'A','R','P','R','E','A','D','M','E',0};
4629 static const WCHAR szReadme[] =
4630 {'R','e','a','d','M','e',0};
4631 static const WCHAR szARPSIZE[] =
4632 {'A','R','P','S','I','Z','E',0};
4633 static const WCHAR szSize[] =
4634 {'S','i','z','e',0};
4635 static const WCHAR szARPURLINFOABOUT[] =
4636 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4637 static const WCHAR szURLInfoAbout[] =
4638 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4639 static const WCHAR szARPURLUPDATEINFO[] =
4640 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4641 static const WCHAR szURLUpdateInfo[] =
4642 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4644 static const WCHAR *propval[] = {
4645 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4646 szARPCONTACT, szContact,
4647 szARPCOMMENTS, szComments,
4648 szProductName, szDisplayName,
4649 szARPHELPLINK, szHelpLink,
4650 szARPHELPTELEPHONE, szHelpTelephone,
4651 szARPINSTALLLOCATION, szInstallLocation,
4652 cszSourceDir, szInstallSource,
4653 szManufacturer, szPublisher,
4654 szARPREADME, szReadme,
4655 szARPSIZE, szSize,
4656 szARPURLINFOABOUT, szURLInfoAbout,
4657 szARPURLUPDATEINFO, szURLUpdateInfo,
4658 NULL
4660 const WCHAR **p = propval;
4662 while (*p)
4664 prop = *p++;
4665 key = *p++;
4666 val = msi_dup_property(package->db, prop);
4667 msi_reg_set_val_str(hkey, key, val);
4668 msi_free(val);
4671 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4673 size = deformat_string(package, modpath_fmt, &buffer);
4674 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4675 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4676 msi_free(buffer);
4678 /* FIXME: Write real Estimated Size when we have it */
4679 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4681 GetLocalTime(&systime);
4682 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4683 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4685 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4686 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4688 buffer = msi_dup_property(package->db, szProductVersion);
4689 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4690 if (buffer)
4692 DWORD verdword = msi_version_str_to_dword(buffer);
4694 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4695 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4696 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4697 msi_free(buffer);
4700 return ERROR_SUCCESS;
4703 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4705 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4706 MSIRECORD *uirow;
4707 LPWSTR upgrade_code;
4708 HKEY hkey, props;
4709 HKEY upgrade;
4710 UINT rc;
4712 static const WCHAR szUpgradeCode[] = {
4713 'U','p','g','r','a','d','e','C','o','d','e',0};
4715 /* FIXME: also need to publish if the product is in advertise mode */
4716 if (!msi_check_publish(package))
4717 return ERROR_SUCCESS;
4719 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4720 if (rc != ERROR_SUCCESS)
4721 return rc;
4723 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4724 NULL, &props, TRUE);
4725 if (rc != ERROR_SUCCESS)
4726 goto done;
4728 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4729 msi_free( package->db->localfile );
4730 package->db->localfile = NULL;
4732 rc = msi_publish_install_properties(package, hkey);
4733 if (rc != ERROR_SUCCESS)
4734 goto done;
4736 rc = msi_publish_install_properties(package, props);
4737 if (rc != ERROR_SUCCESS)
4738 goto done;
4740 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4741 if (upgrade_code)
4743 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4744 squash_guid(package->ProductCode, squashed_pc);
4745 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4746 RegCloseKey(upgrade);
4747 msi_free(upgrade_code);
4750 done:
4751 uirow = MSI_CreateRecord( 1 );
4752 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4753 ui_actiondata( package, szRegisterProduct, uirow );
4754 msiobj_release( &uirow->hdr );
4756 RegCloseKey(hkey);
4757 return ERROR_SUCCESS;
4760 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4762 return execute_script(package,INSTALL_SCRIPT);
4765 static UINT msi_unpublish_product(MSIPACKAGE *package)
4767 LPWSTR upgrade;
4768 LPWSTR remove = NULL;
4769 LPWSTR *features = NULL;
4770 BOOL full_uninstall = TRUE;
4771 MSIFEATURE *feature;
4772 MSIPATCHINFO *patch;
4774 static const WCHAR szUpgradeCode[] =
4775 {'U','p','g','r','a','d','e','C','o','d','e',0};
4777 remove = msi_dup_property(package->db, szRemove);
4778 if (!remove)
4779 return ERROR_SUCCESS;
4781 features = msi_split_string(remove, ',');
4782 if (!features)
4784 msi_free(remove);
4785 ERR("REMOVE feature list is empty!\n");
4786 return ERROR_FUNCTION_FAILED;
4789 if (!lstrcmpW(features[0], szAll))
4790 full_uninstall = TRUE;
4791 else
4793 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4795 if (feature->Action != INSTALLSTATE_ABSENT)
4796 full_uninstall = FALSE;
4800 if (!full_uninstall)
4801 goto done;
4803 MSIREG_DeleteProductKey(package->ProductCode);
4804 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4805 MSIREG_DeleteUninstallKey(package->ProductCode);
4807 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4809 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4810 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4812 else
4814 MSIREG_DeleteUserProductKey(package->ProductCode);
4815 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4818 upgrade = msi_dup_property(package->db, szUpgradeCode);
4819 if (upgrade)
4821 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4822 msi_free(upgrade);
4825 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4827 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4830 done:
4831 msi_free(remove);
4832 msi_free(features);
4833 return ERROR_SUCCESS;
4836 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4838 UINT rc;
4840 rc = msi_unpublish_product(package);
4841 if (rc != ERROR_SUCCESS)
4842 return rc;
4844 /* turn off scheduling */
4845 package->script->CurrentlyScripting= FALSE;
4847 /* first do the same as an InstallExecute */
4848 rc = ACTION_InstallExecute(package);
4849 if (rc != ERROR_SUCCESS)
4850 return rc;
4852 /* then handle Commit Actions */
4853 rc = execute_script(package,COMMIT_SCRIPT);
4855 return rc;
4858 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4860 static const WCHAR RunOnce[] = {
4861 'S','o','f','t','w','a','r','e','\\',
4862 'M','i','c','r','o','s','o','f','t','\\',
4863 'W','i','n','d','o','w','s','\\',
4864 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4865 'R','u','n','O','n','c','e',0};
4866 static const WCHAR InstallRunOnce[] = {
4867 'S','o','f','t','w','a','r','e','\\',
4868 'M','i','c','r','o','s','o','f','t','\\',
4869 'W','i','n','d','o','w','s','\\',
4870 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4871 'I','n','s','t','a','l','l','e','r','\\',
4872 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4874 static const WCHAR msiexec_fmt[] = {
4875 '%','s',
4876 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4877 '\"','%','s','\"',0};
4878 static const WCHAR install_fmt[] = {
4879 '/','I',' ','\"','%','s','\"',' ',
4880 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4881 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4882 WCHAR buffer[256], sysdir[MAX_PATH];
4883 HKEY hkey;
4884 WCHAR squished_pc[100];
4886 squash_guid(package->ProductCode,squished_pc);
4888 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4889 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4890 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4891 squished_pc);
4893 msi_reg_set_val_str( hkey, squished_pc, buffer );
4894 RegCloseKey(hkey);
4896 TRACE("Reboot command %s\n",debugstr_w(buffer));
4898 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4899 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4901 msi_reg_set_val_str( hkey, squished_pc, buffer );
4902 RegCloseKey(hkey);
4904 return ERROR_INSTALL_SUSPEND;
4907 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4909 DWORD attrib;
4910 UINT rc;
4913 * We are currently doing what should be done here in the top level Install
4914 * however for Administrative and uninstalls this step will be needed
4916 if (!package->PackagePath)
4917 return ERROR_SUCCESS;
4919 msi_set_sourcedir_props(package, TRUE);
4921 attrib = GetFileAttributesW(package->db->path);
4922 if (attrib == INVALID_FILE_ATTRIBUTES)
4924 LPWSTR prompt;
4925 LPWSTR msg;
4926 DWORD size = 0;
4928 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4929 package->Context, MSICODE_PRODUCT,
4930 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4931 if (rc == ERROR_MORE_DATA)
4933 prompt = msi_alloc(size * sizeof(WCHAR));
4934 MsiSourceListGetInfoW(package->ProductCode, NULL,
4935 package->Context, MSICODE_PRODUCT,
4936 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4938 else
4939 prompt = strdupW(package->db->path);
4941 msg = generate_error_string(package,1302,1,prompt);
4942 while(attrib == INVALID_FILE_ATTRIBUTES)
4944 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4945 if (rc == IDCANCEL)
4947 rc = ERROR_INSTALL_USEREXIT;
4948 break;
4950 attrib = GetFileAttributesW(package->db->path);
4952 msi_free(prompt);
4953 rc = ERROR_SUCCESS;
4955 else
4956 return ERROR_SUCCESS;
4958 return rc;
4961 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4963 HKEY hkey = 0;
4964 LPWSTR buffer, productid = NULL;
4965 UINT i, rc = ERROR_SUCCESS;
4966 MSIRECORD *uirow;
4968 static const WCHAR szPropKeys[][80] =
4970 {'P','r','o','d','u','c','t','I','D',0},
4971 {'U','S','E','R','N','A','M','E',0},
4972 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4973 {0},
4976 static const WCHAR szRegKeys[][80] =
4978 {'P','r','o','d','u','c','t','I','D',0},
4979 {'R','e','g','O','w','n','e','r',0},
4980 {'R','e','g','C','o','m','p','a','n','y',0},
4981 {0},
4984 if (msi_check_unpublish(package))
4986 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4987 goto end;
4990 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
4991 if (!productid)
4992 goto end;
4994 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4995 NULL, &hkey, TRUE);
4996 if (rc != ERROR_SUCCESS)
4997 goto end;
4999 for( i = 0; szPropKeys[i][0]; i++ )
5001 buffer = msi_dup_property( package->db, szPropKeys[i] );
5002 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5003 msi_free( buffer );
5006 end:
5007 uirow = MSI_CreateRecord( 1 );
5008 MSI_RecordSetStringW( uirow, 1, productid );
5009 ui_actiondata( package, szRegisterUser, uirow );
5010 msiobj_release( &uirow->hdr );
5012 msi_free(productid);
5013 RegCloseKey(hkey);
5014 return rc;
5018 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5020 UINT rc;
5022 package->script->InWhatSequence |= SEQUENCE_EXEC;
5023 rc = ACTION_ProcessExecSequence(package,FALSE);
5024 return rc;
5028 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5030 MSIPACKAGE *package = param;
5031 LPCWSTR compgroupid, component, feature, qualifier, text;
5032 LPWSTR advertise = NULL, output = NULL;
5033 HKEY hkey = NULL;
5034 UINT rc;
5035 MSICOMPONENT *comp;
5036 MSIFEATURE *feat;
5037 DWORD sz;
5038 MSIRECORD *uirow;
5040 feature = MSI_RecordGetString(rec, 5);
5041 feat = get_loaded_feature(package, feature);
5042 if (!feat)
5043 return ERROR_SUCCESS;
5045 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
5046 feat->ActionRequest != INSTALLSTATE_SOURCE &&
5047 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
5049 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
5050 feat->Action = feat->Installed;
5051 return ERROR_SUCCESS;
5054 component = MSI_RecordGetString(rec, 3);
5055 comp = get_loaded_component(package, component);
5056 if (!comp)
5057 return ERROR_SUCCESS;
5059 compgroupid = MSI_RecordGetString(rec,1);
5060 qualifier = MSI_RecordGetString(rec,2);
5062 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5063 if (rc != ERROR_SUCCESS)
5064 goto end;
5066 text = MSI_RecordGetString(rec,4);
5067 advertise = create_component_advertise_string(package, comp, feature);
5069 sz = strlenW(advertise);
5071 if (text)
5072 sz += lstrlenW(text);
5074 sz+=3;
5075 sz *= sizeof(WCHAR);
5077 output = msi_alloc_zero(sz);
5078 strcpyW(output,advertise);
5079 msi_free(advertise);
5081 if (text)
5082 strcatW(output,text);
5084 msi_reg_set_val_multi_str( hkey, qualifier, output );
5086 end:
5087 RegCloseKey(hkey);
5088 msi_free(output);
5090 /* the UI chunk */
5091 uirow = MSI_CreateRecord( 2 );
5092 MSI_RecordSetStringW( uirow, 1, compgroupid );
5093 MSI_RecordSetStringW( uirow, 2, qualifier);
5094 ui_actiondata( package, szPublishComponents, uirow);
5095 msiobj_release( &uirow->hdr );
5096 /* FIXME: call ui_progress? */
5098 return rc;
5102 * At present I am ignorning the advertised components part of this and only
5103 * focusing on the qualified component sets
5105 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5107 UINT rc;
5108 MSIQUERY * view;
5109 static const WCHAR ExecSeqQuery[] =
5110 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5111 '`','P','u','b','l','i','s','h',
5112 'C','o','m','p','o','n','e','n','t','`',0};
5114 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5115 if (rc != ERROR_SUCCESS)
5116 return ERROR_SUCCESS;
5118 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5119 msiobj_release(&view->hdr);
5121 return rc;
5124 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5126 static const WCHAR szInstallerComponents[] = {
5127 'S','o','f','t','w','a','r','e','\\',
5128 'M','i','c','r','o','s','o','f','t','\\',
5129 'I','n','s','t','a','l','l','e','r','\\',
5130 'C','o','m','p','o','n','e','n','t','s','\\',0};
5132 MSIPACKAGE *package = param;
5133 LPCWSTR compgroupid, component, feature, qualifier;
5134 MSICOMPONENT *comp;
5135 MSIFEATURE *feat;
5136 MSIRECORD *uirow;
5137 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5138 LONG res;
5140 feature = MSI_RecordGetString( rec, 5 );
5141 feat = get_loaded_feature( package, feature );
5142 if (!feat)
5143 return ERROR_SUCCESS;
5145 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5147 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5148 feat->Action = feat->Installed;
5149 return ERROR_SUCCESS;
5152 component = MSI_RecordGetString( rec, 3 );
5153 comp = get_loaded_component( package, component );
5154 if (!comp)
5155 return ERROR_SUCCESS;
5157 compgroupid = MSI_RecordGetString( rec, 1 );
5158 qualifier = MSI_RecordGetString( rec, 2 );
5160 squash_guid( compgroupid, squashed );
5161 strcpyW( keypath, szInstallerComponents );
5162 strcatW( keypath, squashed );
5164 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5165 if (res != ERROR_SUCCESS)
5167 WARN("Unable to delete component key %d\n", res);
5170 uirow = MSI_CreateRecord( 2 );
5171 MSI_RecordSetStringW( uirow, 1, compgroupid );
5172 MSI_RecordSetStringW( uirow, 2, qualifier );
5173 ui_actiondata( package, szUnpublishComponents, uirow );
5174 msiobj_release( &uirow->hdr );
5176 return ERROR_SUCCESS;
5179 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5181 UINT rc;
5182 MSIQUERY *view;
5183 static const WCHAR query[] =
5184 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5185 '`','P','u','b','l','i','s','h',
5186 'C','o','m','p','o','n','e','n','t','`',0};
5188 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5189 if (rc != ERROR_SUCCESS)
5190 return ERROR_SUCCESS;
5192 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5193 msiobj_release( &view->hdr );
5195 return rc;
5198 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5200 MSIPACKAGE *package = param;
5201 MSIRECORD *row;
5202 MSIFILE *file;
5203 SC_HANDLE hscm, service = NULL;
5204 LPCWSTR comp, depends, pass;
5205 LPWSTR name = NULL, disp = NULL;
5206 LPCWSTR load_order, serv_name, key;
5207 DWORD serv_type, start_type;
5208 DWORD err_control;
5210 static const WCHAR query[] =
5211 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5212 '`','C','o','m','p','o','n','e','n','t','`',' ',
5213 'W','H','E','R','E',' ',
5214 '`','C','o','m','p','o','n','e','n','t','`',' ',
5215 '=','\'','%','s','\'',0};
5217 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5218 if (!hscm)
5220 ERR("Failed to open the SC Manager!\n");
5221 goto done;
5224 start_type = MSI_RecordGetInteger(rec, 5);
5225 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5226 goto done;
5228 depends = MSI_RecordGetString(rec, 8);
5229 if (depends && *depends)
5230 FIXME("Dependency list unhandled!\n");
5232 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5233 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5234 serv_type = MSI_RecordGetInteger(rec, 4);
5235 err_control = MSI_RecordGetInteger(rec, 6);
5236 load_order = MSI_RecordGetString(rec, 7);
5237 serv_name = MSI_RecordGetString(rec, 9);
5238 pass = MSI_RecordGetString(rec, 10);
5239 comp = MSI_RecordGetString(rec, 12);
5241 /* fetch the service path */
5242 row = MSI_QueryGetRecord(package->db, query, comp);
5243 if (!row)
5245 ERR("Control query failed!\n");
5246 goto done;
5249 key = MSI_RecordGetString(row, 6);
5251 file = get_loaded_file(package, key);
5252 msiobj_release(&row->hdr);
5253 if (!file)
5255 ERR("Failed to load the service file\n");
5256 goto done;
5259 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5260 start_type, err_control, file->TargetPath,
5261 load_order, NULL, NULL, serv_name, pass);
5262 if (!service)
5264 if (GetLastError() != ERROR_SERVICE_EXISTS)
5265 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5268 done:
5269 CloseServiceHandle(service);
5270 CloseServiceHandle(hscm);
5271 msi_free(name);
5272 msi_free(disp);
5274 return ERROR_SUCCESS;
5277 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5279 UINT rc;
5280 MSIQUERY * view;
5281 static const WCHAR ExecSeqQuery[] =
5282 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5283 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5285 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5286 if (rc != ERROR_SUCCESS)
5287 return ERROR_SUCCESS;
5289 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5290 msiobj_release(&view->hdr);
5292 return rc;
5295 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5296 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5298 LPCWSTR *vector, *temp_vector;
5299 LPWSTR p, q;
5300 DWORD sep_len;
5302 static const WCHAR separator[] = {'[','~',']',0};
5304 *numargs = 0;
5305 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5307 if (!args)
5308 return NULL;
5310 vector = msi_alloc(sizeof(LPWSTR));
5311 if (!vector)
5312 return NULL;
5314 p = args;
5317 (*numargs)++;
5318 vector[*numargs - 1] = p;
5320 if ((q = strstrW(p, separator)))
5322 *q = '\0';
5324 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5325 if (!temp_vector)
5327 msi_free(vector);
5328 return NULL;
5330 vector = temp_vector;
5332 p = q + sep_len;
5334 } while (q);
5336 return vector;
5339 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5341 MSIPACKAGE *package = param;
5342 MSICOMPONENT *comp;
5343 MSIRECORD *uirow;
5344 SC_HANDLE scm = NULL, service = NULL;
5345 LPCWSTR component, *vector = NULL;
5346 LPWSTR name, args, display_name = NULL;
5347 DWORD event, numargs, len;
5348 UINT r = ERROR_FUNCTION_FAILED;
5350 component = MSI_RecordGetString(rec, 6);
5351 comp = get_loaded_component(package, component);
5352 if (!comp)
5353 return ERROR_SUCCESS;
5355 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5357 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5358 comp->Action = comp->Installed;
5359 return ERROR_SUCCESS;
5361 comp->Action = INSTALLSTATE_LOCAL;
5363 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5364 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5365 event = MSI_RecordGetInteger(rec, 3);
5367 if (!(event & msidbServiceControlEventStart))
5369 r = ERROR_SUCCESS;
5370 goto done;
5373 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5374 if (!scm)
5376 ERR("Failed to open the service control manager\n");
5377 goto done;
5380 len = 0;
5381 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5382 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5384 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5385 GetServiceDisplayNameW( scm, name, display_name, &len );
5388 service = OpenServiceW(scm, name, SERVICE_START);
5389 if (!service)
5391 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5392 goto done;
5395 vector = msi_service_args_to_vector(args, &numargs);
5397 if (!StartServiceW(service, numargs, vector) &&
5398 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5400 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5401 goto done;
5404 r = ERROR_SUCCESS;
5406 done:
5407 uirow = MSI_CreateRecord( 2 );
5408 MSI_RecordSetStringW( uirow, 1, display_name );
5409 MSI_RecordSetStringW( uirow, 2, name );
5410 ui_actiondata( package, szStartServices, uirow );
5411 msiobj_release( &uirow->hdr );
5413 CloseServiceHandle(service);
5414 CloseServiceHandle(scm);
5416 msi_free(name);
5417 msi_free(args);
5418 msi_free(vector);
5419 msi_free(display_name);
5420 return r;
5423 static UINT ACTION_StartServices( MSIPACKAGE *package )
5425 UINT rc;
5426 MSIQUERY *view;
5428 static const WCHAR query[] = {
5429 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5430 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5432 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5433 if (rc != ERROR_SUCCESS)
5434 return ERROR_SUCCESS;
5436 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5437 msiobj_release(&view->hdr);
5439 return rc;
5442 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5444 DWORD i, needed, count;
5445 ENUM_SERVICE_STATUSW *dependencies;
5446 SERVICE_STATUS ss;
5447 SC_HANDLE depserv;
5449 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5450 0, &needed, &count))
5451 return TRUE;
5453 if (GetLastError() != ERROR_MORE_DATA)
5454 return FALSE;
5456 dependencies = msi_alloc(needed);
5457 if (!dependencies)
5458 return FALSE;
5460 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5461 needed, &needed, &count))
5462 goto error;
5464 for (i = 0; i < count; i++)
5466 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5467 SERVICE_STOP | SERVICE_QUERY_STATUS);
5468 if (!depserv)
5469 goto error;
5471 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5472 goto error;
5475 return TRUE;
5477 error:
5478 msi_free(dependencies);
5479 return FALSE;
5482 static UINT stop_service( LPCWSTR name )
5484 SC_HANDLE scm = NULL, service = NULL;
5485 SERVICE_STATUS status;
5486 SERVICE_STATUS_PROCESS ssp;
5487 DWORD needed;
5489 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5490 if (!scm)
5492 WARN("Failed to open the SCM: %d\n", GetLastError());
5493 goto done;
5496 service = OpenServiceW(scm, name,
5497 SERVICE_STOP |
5498 SERVICE_QUERY_STATUS |
5499 SERVICE_ENUMERATE_DEPENDENTS);
5500 if (!service)
5502 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5503 goto done;
5506 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5507 sizeof(SERVICE_STATUS_PROCESS), &needed))
5509 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5510 goto done;
5513 if (ssp.dwCurrentState == SERVICE_STOPPED)
5514 goto done;
5516 stop_service_dependents(scm, service);
5518 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5519 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5521 done:
5522 CloseServiceHandle(service);
5523 CloseServiceHandle(scm);
5525 return ERROR_SUCCESS;
5528 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5530 MSIPACKAGE *package = param;
5531 MSICOMPONENT *comp;
5532 MSIRECORD *uirow;
5533 LPCWSTR component;
5534 LPWSTR name = NULL, display_name = NULL;
5535 DWORD event, len;
5536 SC_HANDLE scm;
5538 event = MSI_RecordGetInteger( rec, 3 );
5539 if (!(event & msidbServiceControlEventStop))
5540 return ERROR_SUCCESS;
5542 component = MSI_RecordGetString( rec, 6 );
5543 comp = get_loaded_component( package, component );
5544 if (!comp)
5545 return ERROR_SUCCESS;
5547 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5549 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5550 comp->Action = comp->Installed;
5551 return ERROR_SUCCESS;
5553 comp->Action = INSTALLSTATE_ABSENT;
5555 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5556 if (!scm)
5558 ERR("Failed to open the service control manager\n");
5559 goto done;
5562 len = 0;
5563 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5564 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5566 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5567 GetServiceDisplayNameW( scm, name, display_name, &len );
5569 CloseServiceHandle( scm );
5571 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5572 stop_service( name );
5574 done:
5575 uirow = MSI_CreateRecord( 2 );
5576 MSI_RecordSetStringW( uirow, 1, display_name );
5577 MSI_RecordSetStringW( uirow, 2, name );
5578 ui_actiondata( package, szStopServices, uirow );
5579 msiobj_release( &uirow->hdr );
5581 msi_free( name );
5582 msi_free( display_name );
5583 return ERROR_SUCCESS;
5586 static UINT ACTION_StopServices( MSIPACKAGE *package )
5588 UINT rc;
5589 MSIQUERY *view;
5591 static const WCHAR query[] = {
5592 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5593 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5595 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5596 if (rc != ERROR_SUCCESS)
5597 return ERROR_SUCCESS;
5599 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5600 msiobj_release(&view->hdr);
5602 return rc;
5605 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5607 MSIPACKAGE *package = param;
5608 MSICOMPONENT *comp;
5609 MSIRECORD *uirow;
5610 LPCWSTR component;
5611 LPWSTR name = NULL, display_name = NULL;
5612 DWORD event, len;
5613 SC_HANDLE scm = NULL, service = NULL;
5615 event = MSI_RecordGetInteger( rec, 3 );
5616 if (!(event & msidbServiceControlEventDelete))
5617 return ERROR_SUCCESS;
5619 component = MSI_RecordGetString(rec, 6);
5620 comp = get_loaded_component(package, component);
5621 if (!comp)
5622 return ERROR_SUCCESS;
5624 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5626 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5627 comp->Action = comp->Installed;
5628 return ERROR_SUCCESS;
5630 comp->Action = INSTALLSTATE_ABSENT;
5632 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5633 stop_service( name );
5635 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5636 if (!scm)
5638 WARN("Failed to open the SCM: %d\n", GetLastError());
5639 goto done;
5642 len = 0;
5643 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5644 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5646 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5647 GetServiceDisplayNameW( scm, name, display_name, &len );
5650 service = OpenServiceW( scm, name, DELETE );
5651 if (!service)
5653 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5654 goto done;
5657 if (!DeleteService( service ))
5658 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5660 done:
5661 uirow = MSI_CreateRecord( 2 );
5662 MSI_RecordSetStringW( uirow, 1, display_name );
5663 MSI_RecordSetStringW( uirow, 2, name );
5664 ui_actiondata( package, szDeleteServices, uirow );
5665 msiobj_release( &uirow->hdr );
5667 CloseServiceHandle( service );
5668 CloseServiceHandle( scm );
5669 msi_free( name );
5670 msi_free( display_name );
5672 return ERROR_SUCCESS;
5675 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5677 UINT rc;
5678 MSIQUERY *view;
5680 static const WCHAR query[] = {
5681 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5682 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5684 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5685 if (rc != ERROR_SUCCESS)
5686 return ERROR_SUCCESS;
5688 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5689 msiobj_release( &view->hdr );
5691 return rc;
5694 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5696 MSIFILE *file;
5698 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5700 if (!lstrcmpW(file->File, filename))
5701 return file;
5704 return NULL;
5707 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5709 MSIPACKAGE *package = param;
5710 LPWSTR driver, driver_path, ptr;
5711 WCHAR outpath[MAX_PATH];
5712 MSIFILE *driver_file, *setup_file;
5713 MSIRECORD *uirow;
5714 LPCWSTR desc;
5715 DWORD len, usage;
5716 UINT r = ERROR_SUCCESS;
5718 static const WCHAR driver_fmt[] = {
5719 'D','r','i','v','e','r','=','%','s',0};
5720 static const WCHAR setup_fmt[] = {
5721 'S','e','t','u','p','=','%','s',0};
5722 static const WCHAR usage_fmt[] = {
5723 'F','i','l','e','U','s','a','g','e','=','1',0};
5725 desc = MSI_RecordGetString(rec, 3);
5727 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5728 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5730 if (!driver_file)
5732 ERR("ODBC Driver entry not found!\n");
5733 return ERROR_FUNCTION_FAILED;
5736 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5737 if (setup_file)
5738 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5739 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5741 driver = msi_alloc(len * sizeof(WCHAR));
5742 if (!driver)
5743 return ERROR_OUTOFMEMORY;
5745 ptr = driver;
5746 lstrcpyW(ptr, desc);
5747 ptr += lstrlenW(ptr) + 1;
5749 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5750 ptr += len + 1;
5752 if (setup_file)
5754 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5755 ptr += len + 1;
5758 lstrcpyW(ptr, usage_fmt);
5759 ptr += lstrlenW(ptr) + 1;
5760 *ptr = '\0';
5762 driver_path = strdupW(driver_file->TargetPath);
5763 ptr = strrchrW(driver_path, '\\');
5764 if (ptr) *ptr = '\0';
5766 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5767 NULL, ODBC_INSTALL_COMPLETE, &usage))
5769 ERR("Failed to install SQL driver!\n");
5770 r = ERROR_FUNCTION_FAILED;
5773 uirow = MSI_CreateRecord( 5 );
5774 MSI_RecordSetStringW( uirow, 1, desc );
5775 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5776 MSI_RecordSetStringW( uirow, 3, driver_path );
5777 ui_actiondata( package, szInstallODBC, uirow );
5778 msiobj_release( &uirow->hdr );
5780 msi_free(driver);
5781 msi_free(driver_path);
5783 return r;
5786 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5788 MSIPACKAGE *package = param;
5789 LPWSTR translator, translator_path, ptr;
5790 WCHAR outpath[MAX_PATH];
5791 MSIFILE *translator_file, *setup_file;
5792 MSIRECORD *uirow;
5793 LPCWSTR desc;
5794 DWORD len, usage;
5795 UINT r = ERROR_SUCCESS;
5797 static const WCHAR translator_fmt[] = {
5798 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5799 static const WCHAR setup_fmt[] = {
5800 'S','e','t','u','p','=','%','s',0};
5802 desc = MSI_RecordGetString(rec, 3);
5804 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5805 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5807 if (!translator_file)
5809 ERR("ODBC Translator entry not found!\n");
5810 return ERROR_FUNCTION_FAILED;
5813 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5814 if (setup_file)
5815 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5817 translator = msi_alloc(len * sizeof(WCHAR));
5818 if (!translator)
5819 return ERROR_OUTOFMEMORY;
5821 ptr = translator;
5822 lstrcpyW(ptr, desc);
5823 ptr += lstrlenW(ptr) + 1;
5825 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5826 ptr += len + 1;
5828 if (setup_file)
5830 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5831 ptr += len + 1;
5833 *ptr = '\0';
5835 translator_path = strdupW(translator_file->TargetPath);
5836 ptr = strrchrW(translator_path, '\\');
5837 if (ptr) *ptr = '\0';
5839 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5840 NULL, ODBC_INSTALL_COMPLETE, &usage))
5842 ERR("Failed to install SQL translator!\n");
5843 r = ERROR_FUNCTION_FAILED;
5846 uirow = MSI_CreateRecord( 5 );
5847 MSI_RecordSetStringW( uirow, 1, desc );
5848 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5849 MSI_RecordSetStringW( uirow, 3, translator_path );
5850 ui_actiondata( package, szInstallODBC, uirow );
5851 msiobj_release( &uirow->hdr );
5853 msi_free(translator);
5854 msi_free(translator_path);
5856 return r;
5859 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5861 MSIPACKAGE *package = param;
5862 LPWSTR attrs;
5863 LPCWSTR desc, driver;
5864 WORD request = ODBC_ADD_SYS_DSN;
5865 INT registration;
5866 DWORD len;
5867 UINT r = ERROR_SUCCESS;
5868 MSIRECORD *uirow;
5870 static const WCHAR attrs_fmt[] = {
5871 'D','S','N','=','%','s',0 };
5873 desc = MSI_RecordGetString(rec, 3);
5874 driver = MSI_RecordGetString(rec, 4);
5875 registration = MSI_RecordGetInteger(rec, 5);
5877 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5878 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5880 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5881 attrs = msi_alloc(len * sizeof(WCHAR));
5882 if (!attrs)
5883 return ERROR_OUTOFMEMORY;
5885 len = sprintfW(attrs, attrs_fmt, desc);
5886 attrs[len + 1] = 0;
5888 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5890 ERR("Failed to install SQL data source!\n");
5891 r = ERROR_FUNCTION_FAILED;
5894 uirow = MSI_CreateRecord( 5 );
5895 MSI_RecordSetStringW( uirow, 1, desc );
5896 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5897 MSI_RecordSetInteger( uirow, 3, request );
5898 ui_actiondata( package, szInstallODBC, uirow );
5899 msiobj_release( &uirow->hdr );
5901 msi_free(attrs);
5903 return r;
5906 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5908 UINT rc;
5909 MSIQUERY *view;
5911 static const WCHAR driver_query[] = {
5912 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5913 'O','D','B','C','D','r','i','v','e','r',0 };
5915 static const WCHAR translator_query[] = {
5916 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5917 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5919 static const WCHAR source_query[] = {
5920 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5921 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5923 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5924 if (rc != ERROR_SUCCESS)
5925 return ERROR_SUCCESS;
5927 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5928 msiobj_release(&view->hdr);
5930 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5931 if (rc != ERROR_SUCCESS)
5932 return ERROR_SUCCESS;
5934 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5935 msiobj_release(&view->hdr);
5937 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5938 if (rc != ERROR_SUCCESS)
5939 return ERROR_SUCCESS;
5941 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5942 msiobj_release(&view->hdr);
5944 return rc;
5947 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5949 MSIPACKAGE *package = param;
5950 MSIRECORD *uirow;
5951 DWORD usage;
5952 LPCWSTR desc;
5954 desc = MSI_RecordGetString( rec, 3 );
5955 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5957 WARN("Failed to remove ODBC driver\n");
5959 else if (!usage)
5961 FIXME("Usage count reached 0\n");
5964 uirow = MSI_CreateRecord( 2 );
5965 MSI_RecordSetStringW( uirow, 1, desc );
5966 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5967 ui_actiondata( package, szRemoveODBC, uirow );
5968 msiobj_release( &uirow->hdr );
5970 return ERROR_SUCCESS;
5973 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5975 MSIPACKAGE *package = param;
5976 MSIRECORD *uirow;
5977 DWORD usage;
5978 LPCWSTR desc;
5980 desc = MSI_RecordGetString( rec, 3 );
5981 if (!SQLRemoveTranslatorW( desc, &usage ))
5983 WARN("Failed to remove ODBC translator\n");
5985 else if (!usage)
5987 FIXME("Usage count reached 0\n");
5990 uirow = MSI_CreateRecord( 2 );
5991 MSI_RecordSetStringW( uirow, 1, desc );
5992 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5993 ui_actiondata( package, szRemoveODBC, uirow );
5994 msiobj_release( &uirow->hdr );
5996 return ERROR_SUCCESS;
5999 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6001 MSIPACKAGE *package = param;
6002 MSIRECORD *uirow;
6003 LPWSTR attrs;
6004 LPCWSTR desc, driver;
6005 WORD request = ODBC_REMOVE_SYS_DSN;
6006 INT registration;
6007 DWORD len;
6009 static const WCHAR attrs_fmt[] = {
6010 'D','S','N','=','%','s',0 };
6012 desc = MSI_RecordGetString( rec, 3 );
6013 driver = MSI_RecordGetString( rec, 4 );
6014 registration = MSI_RecordGetInteger( rec, 5 );
6016 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6017 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6019 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6020 attrs = msi_alloc( len * sizeof(WCHAR) );
6021 if (!attrs)
6022 return ERROR_OUTOFMEMORY;
6024 FIXME("Use ODBCSourceAttribute table\n");
6026 len = sprintfW( attrs, attrs_fmt, desc );
6027 attrs[len + 1] = 0;
6029 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6031 WARN("Failed to remove ODBC data source\n");
6033 msi_free( attrs );
6035 uirow = MSI_CreateRecord( 3 );
6036 MSI_RecordSetStringW( uirow, 1, desc );
6037 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6038 MSI_RecordSetInteger( uirow, 3, request );
6039 ui_actiondata( package, szRemoveODBC, uirow );
6040 msiobj_release( &uirow->hdr );
6042 return ERROR_SUCCESS;
6045 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6047 UINT rc;
6048 MSIQUERY *view;
6050 static const WCHAR driver_query[] = {
6051 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6052 'O','D','B','C','D','r','i','v','e','r',0 };
6054 static const WCHAR translator_query[] = {
6055 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6056 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6058 static const WCHAR source_query[] = {
6059 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6060 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6062 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6063 if (rc != ERROR_SUCCESS)
6064 return ERROR_SUCCESS;
6066 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6067 msiobj_release( &view->hdr );
6069 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6070 if (rc != ERROR_SUCCESS)
6071 return ERROR_SUCCESS;
6073 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6074 msiobj_release( &view->hdr );
6076 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6077 if (rc != ERROR_SUCCESS)
6078 return ERROR_SUCCESS;
6080 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6081 msiobj_release( &view->hdr );
6083 return rc;
6086 #define ENV_ACT_SETALWAYS 0x1
6087 #define ENV_ACT_SETABSENT 0x2
6088 #define ENV_ACT_REMOVE 0x4
6089 #define ENV_ACT_REMOVEMATCH 0x8
6091 #define ENV_MOD_MACHINE 0x20000000
6092 #define ENV_MOD_APPEND 0x40000000
6093 #define ENV_MOD_PREFIX 0x80000000
6094 #define ENV_MOD_MASK 0xC0000000
6096 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6098 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6100 LPCWSTR cptr = *name;
6102 static const WCHAR prefix[] = {'[','~',']',0};
6103 static const int prefix_len = 3;
6105 *flags = 0;
6106 while (*cptr)
6108 if (*cptr == '=')
6109 *flags |= ENV_ACT_SETALWAYS;
6110 else if (*cptr == '+')
6111 *flags |= ENV_ACT_SETABSENT;
6112 else if (*cptr == '-')
6113 *flags |= ENV_ACT_REMOVE;
6114 else if (*cptr == '!')
6115 *flags |= ENV_ACT_REMOVEMATCH;
6116 else if (*cptr == '*')
6117 *flags |= ENV_MOD_MACHINE;
6118 else
6119 break;
6121 cptr++;
6122 (*name)++;
6125 if (!*cptr)
6127 ERR("Missing environment variable\n");
6128 return ERROR_FUNCTION_FAILED;
6131 if (*value)
6133 LPCWSTR ptr = *value;
6134 if (!strncmpW(ptr, prefix, prefix_len))
6136 if (ptr[prefix_len] == szSemiColon[0])
6138 *flags |= ENV_MOD_APPEND;
6139 *value += lstrlenW(prefix);
6141 else
6143 *value = NULL;
6146 else if (lstrlenW(*value) >= prefix_len)
6148 ptr += lstrlenW(ptr) - prefix_len;
6149 if (!lstrcmpW(ptr, prefix))
6151 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6153 *flags |= ENV_MOD_PREFIX;
6154 /* the "[~]" will be removed by deformat_string */;
6156 else
6158 *value = NULL;
6164 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6165 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6166 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6167 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6169 ERR("Invalid flags: %08x\n", *flags);
6170 return ERROR_FUNCTION_FAILED;
6173 if (!*flags)
6174 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6176 return ERROR_SUCCESS;
6179 static UINT open_env_key( DWORD flags, HKEY *key )
6181 static const WCHAR user_env[] =
6182 {'E','n','v','i','r','o','n','m','e','n','t',0};
6183 static const WCHAR machine_env[] =
6184 {'S','y','s','t','e','m','\\',
6185 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6186 'C','o','n','t','r','o','l','\\',
6187 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6188 'E','n','v','i','r','o','n','m','e','n','t',0};
6189 const WCHAR *env;
6190 HKEY root;
6191 LONG res;
6193 if (flags & ENV_MOD_MACHINE)
6195 env = machine_env;
6196 root = HKEY_LOCAL_MACHINE;
6198 else
6200 env = user_env;
6201 root = HKEY_CURRENT_USER;
6204 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6205 if (res != ERROR_SUCCESS)
6207 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6208 return ERROR_FUNCTION_FAILED;
6211 return ERROR_SUCCESS;
6214 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6216 MSIPACKAGE *package = param;
6217 LPCWSTR name, value, component;
6218 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6219 DWORD flags, type, size;
6220 UINT res;
6221 HKEY env = NULL;
6222 MSICOMPONENT *comp;
6223 MSIRECORD *uirow;
6224 int action = 0;
6226 component = MSI_RecordGetString(rec, 4);
6227 comp = get_loaded_component(package, component);
6228 if (!comp)
6229 return ERROR_SUCCESS;
6231 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6233 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6234 comp->Action = comp->Installed;
6235 return ERROR_SUCCESS;
6237 comp->Action = INSTALLSTATE_LOCAL;
6239 name = MSI_RecordGetString(rec, 2);
6240 value = MSI_RecordGetString(rec, 3);
6242 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6244 res = env_parse_flags(&name, &value, &flags);
6245 if (res != ERROR_SUCCESS || !value)
6246 goto done;
6248 if (value && !deformat_string(package, value, &deformatted))
6250 res = ERROR_OUTOFMEMORY;
6251 goto done;
6254 value = deformatted;
6256 res = open_env_key( flags, &env );
6257 if (res != ERROR_SUCCESS)
6258 goto done;
6260 if (flags & ENV_MOD_MACHINE)
6261 action |= 0x20000000;
6263 size = 0;
6264 type = REG_SZ;
6265 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6266 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6267 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6268 goto done;
6270 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6272 action = 0x2;
6274 /* Nothing to do. */
6275 if (!value)
6277 res = ERROR_SUCCESS;
6278 goto done;
6281 /* If we are appending but the string was empty, strip ; */
6282 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6284 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6285 newval = strdupW(value);
6286 if (!newval)
6288 res = ERROR_OUTOFMEMORY;
6289 goto done;
6292 else
6294 action = 0x1;
6296 /* Contrary to MSDN, +-variable to [~];path works */
6297 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6299 res = ERROR_SUCCESS;
6300 goto done;
6303 data = msi_alloc(size);
6304 if (!data)
6306 RegCloseKey(env);
6307 return ERROR_OUTOFMEMORY;
6310 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6311 if (res != ERROR_SUCCESS)
6312 goto done;
6314 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
6316 action = 0x4;
6317 res = RegDeleteValueW(env, name);
6318 if (res != ERROR_SUCCESS)
6319 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6320 goto done;
6323 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6324 if (flags & ENV_MOD_MASK)
6326 DWORD mod_size;
6327 int multiplier = 0;
6328 if (flags & ENV_MOD_APPEND) multiplier++;
6329 if (flags & ENV_MOD_PREFIX) multiplier++;
6330 mod_size = lstrlenW(value) * multiplier;
6331 size += mod_size * sizeof(WCHAR);
6334 newval = msi_alloc(size);
6335 ptr = newval;
6336 if (!newval)
6338 res = ERROR_OUTOFMEMORY;
6339 goto done;
6342 if (flags & ENV_MOD_PREFIX)
6344 lstrcpyW(newval, value);
6345 ptr = newval + lstrlenW(value);
6346 action |= 0x80000000;
6349 lstrcpyW(ptr, data);
6351 if (flags & ENV_MOD_APPEND)
6353 lstrcatW(newval, value);
6354 action |= 0x40000000;
6357 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6358 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6359 if (res)
6361 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6364 done:
6365 uirow = MSI_CreateRecord( 3 );
6366 MSI_RecordSetStringW( uirow, 1, name );
6367 MSI_RecordSetStringW( uirow, 2, newval );
6368 MSI_RecordSetInteger( uirow, 3, action );
6369 ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6370 msiobj_release( &uirow->hdr );
6372 if (env) RegCloseKey(env);
6373 msi_free(deformatted);
6374 msi_free(data);
6375 msi_free(newval);
6376 return res;
6379 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6381 UINT rc;
6382 MSIQUERY * view;
6383 static const WCHAR ExecSeqQuery[] =
6384 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6385 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6386 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6387 if (rc != ERROR_SUCCESS)
6388 return ERROR_SUCCESS;
6390 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6391 msiobj_release(&view->hdr);
6393 return rc;
6396 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6398 MSIPACKAGE *package = param;
6399 LPCWSTR name, value, component;
6400 LPWSTR deformatted = NULL;
6401 DWORD flags;
6402 HKEY env;
6403 MSICOMPONENT *comp;
6404 MSIRECORD *uirow;
6405 int action = 0;
6406 LONG res;
6407 UINT r;
6409 component = MSI_RecordGetString( rec, 4 );
6410 comp = get_loaded_component( package, component );
6411 if (!comp)
6412 return ERROR_SUCCESS;
6414 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6416 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6417 comp->Action = comp->Installed;
6418 return ERROR_SUCCESS;
6420 comp->Action = INSTALLSTATE_ABSENT;
6422 name = MSI_RecordGetString( rec, 2 );
6423 value = MSI_RecordGetString( rec, 3 );
6425 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6427 r = env_parse_flags( &name, &value, &flags );
6428 if (r != ERROR_SUCCESS)
6429 return r;
6431 if (!(flags & ENV_ACT_REMOVE))
6433 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6434 return ERROR_SUCCESS;
6437 if (value && !deformat_string( package, value, &deformatted ))
6438 return ERROR_OUTOFMEMORY;
6440 value = deformatted;
6442 r = open_env_key( flags, &env );
6443 if (r != ERROR_SUCCESS)
6445 r = ERROR_SUCCESS;
6446 goto done;
6449 if (flags & ENV_MOD_MACHINE)
6450 action |= 0x20000000;
6452 TRACE("Removing %s\n", debugstr_w(name));
6454 res = RegDeleteValueW( env, name );
6455 if (res != ERROR_SUCCESS)
6457 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6458 r = ERROR_SUCCESS;
6461 done:
6462 uirow = MSI_CreateRecord( 3 );
6463 MSI_RecordSetStringW( uirow, 1, name );
6464 MSI_RecordSetStringW( uirow, 2, value );
6465 MSI_RecordSetInteger( uirow, 3, action );
6466 ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6467 msiobj_release( &uirow->hdr );
6469 if (env) RegCloseKey( env );
6470 msi_free( deformatted );
6471 return r;
6474 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6476 UINT rc;
6477 MSIQUERY *view;
6478 static const WCHAR query[] =
6479 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6480 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6482 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6483 if (rc != ERROR_SUCCESS)
6484 return ERROR_SUCCESS;
6486 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6487 msiobj_release( &view->hdr );
6489 return rc;
6492 typedef struct tagMSIASSEMBLY
6494 struct list entry;
6495 MSICOMPONENT *component;
6496 MSIFEATURE *feature;
6497 MSIFILE *file;
6498 LPWSTR manifest;
6499 LPWSTR application;
6500 LPWSTR display_name;
6501 DWORD attributes;
6502 BOOL installed;
6503 } MSIASSEMBLY;
6505 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6506 DWORD dwReserved);
6507 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6508 LPVOID pvReserved, HMODULE *phModDll);
6510 static BOOL init_functionpointers(void)
6512 HRESULT hr;
6513 HMODULE hfusion;
6514 HMODULE hmscoree;
6516 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6518 hmscoree = LoadLibraryA("mscoree.dll");
6519 if (!hmscoree)
6521 WARN("mscoree.dll not available\n");
6522 return FALSE;
6525 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6526 if (!pLoadLibraryShim)
6528 WARN("LoadLibraryShim not available\n");
6529 FreeLibrary(hmscoree);
6530 return FALSE;
6533 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6534 if (FAILED(hr))
6536 WARN("fusion.dll not available\n");
6537 FreeLibrary(hmscoree);
6538 return FALSE;
6541 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6543 FreeLibrary(hmscoree);
6544 return TRUE;
6547 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6548 LPWSTR path)
6550 IAssemblyCache *cache;
6551 MSIRECORD *uirow;
6552 HRESULT hr;
6553 UINT r = ERROR_FUNCTION_FAILED;
6555 TRACE("installing assembly: %s\n", debugstr_w(path));
6557 uirow = MSI_CreateRecord( 2 );
6558 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
6559 ui_actiondata( package, szMsiPublishAssemblies, uirow );
6560 msiobj_release( &uirow->hdr );
6562 if (assembly->feature)
6563 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6565 if (assembly->manifest)
6566 FIXME("Manifest unhandled\n");
6568 if (assembly->application)
6570 FIXME("Assembly should be privately installed\n");
6571 return ERROR_SUCCESS;
6574 if (assembly->attributes == msidbAssemblyAttributesWin32)
6576 FIXME("Win32 assemblies not handled\n");
6577 return ERROR_SUCCESS;
6580 hr = pCreateAssemblyCache(&cache, 0);
6581 if (FAILED(hr))
6582 goto done;
6584 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6585 if (FAILED(hr))
6586 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6588 r = ERROR_SUCCESS;
6590 done:
6591 IAssemblyCache_Release(cache);
6592 return r;
6595 typedef struct tagASSEMBLY_LIST
6597 MSIPACKAGE *package;
6598 IAssemblyCache *cache;
6599 struct list *assemblies;
6600 } ASSEMBLY_LIST;
6602 typedef struct tagASSEMBLY_NAME
6604 LPWSTR name;
6605 LPWSTR version;
6606 LPWSTR culture;
6607 LPWSTR pubkeytoken;
6608 } ASSEMBLY_NAME;
6610 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6612 ASSEMBLY_NAME *asmname = param;
6613 LPCWSTR name = MSI_RecordGetString(rec, 2);
6614 LPWSTR val = msi_dup_record_field(rec, 3);
6616 static const WCHAR Name[] = {'N','a','m','e',0};
6617 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6618 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6619 static const WCHAR PublicKeyToken[] = {
6620 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6622 if (!strcmpiW(name, Name))
6623 asmname->name = val;
6624 else if (!strcmpiW(name, Version))
6625 asmname->version = val;
6626 else if (!strcmpiW(name, Culture))
6627 asmname->culture = val;
6628 else if (!strcmpiW(name, PublicKeyToken))
6629 asmname->pubkeytoken = val;
6630 else
6631 msi_free(val);
6633 return ERROR_SUCCESS;
6636 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6638 if (!*str)
6640 *size = lstrlenW(append) + 1;
6641 *str = msi_alloc((*size) * sizeof(WCHAR));
6642 lstrcpyW(*str, append);
6643 return;
6646 (*size) += lstrlenW(append);
6647 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6648 lstrcatW(*str, append);
6651 static WCHAR *get_assembly_display_name( MSIDATABASE *db, MSICOMPONENT *comp )
6653 static const WCHAR separator[] = {',',' ',0};
6654 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6655 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6656 static const WCHAR PublicKeyToken[] = {'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6657 static const WCHAR query[] = {
6658 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6659 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6660 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6661 '=','\'','%','s','\'',0};
6662 ASSEMBLY_NAME name;
6663 MSIQUERY *view;
6664 LPWSTR display_name;
6665 DWORD size;
6666 UINT r;
6668 display_name = NULL;
6669 memset( &name, 0, sizeof(ASSEMBLY_NAME) );
6671 r = MSI_OpenQuery( db, &view, query, comp->Component );
6672 if (r != ERROR_SUCCESS)
6673 return NULL;
6675 MSI_IterateRecords( view, NULL, parse_assembly_name, &name );
6676 msiobj_release( &view->hdr );
6678 if (!name.name)
6680 ERR("No assembly name specified!\n");
6681 return NULL;
6684 append_str( &display_name, &size, name.name );
6686 if (name.version)
6688 append_str( &display_name, &size, separator );
6689 append_str( &display_name, &size, Version );
6690 append_str( &display_name, &size, name.version );
6692 if (name.culture)
6694 append_str( &display_name, &size, separator );
6695 append_str( &display_name, &size, Culture );
6696 append_str( &display_name, &size, name.culture );
6698 if (name.pubkeytoken)
6700 append_str( &display_name, &size, separator );
6701 append_str( &display_name, &size, PublicKeyToken );
6702 append_str( &display_name, &size, name.pubkeytoken );
6705 msi_free( name.name );
6706 msi_free( name.version );
6707 msi_free( name.culture );
6708 msi_free( name.pubkeytoken );
6710 return display_name;
6713 static BOOL check_assembly_installed( MSIDATABASE *db, IAssemblyCache *cache, MSICOMPONENT *comp )
6715 ASSEMBLY_INFO asminfo;
6716 LPWSTR disp;
6717 BOOL found = FALSE;
6718 HRESULT hr;
6720 disp = get_assembly_display_name( db, comp );
6721 if (!disp)
6722 return FALSE;
6724 memset( &asminfo, 0, sizeof(ASSEMBLY_INFO) );
6725 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6727 hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_VALIDATE, disp, &asminfo );
6728 if (SUCCEEDED(hr))
6729 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6731 msi_free( disp );
6732 return found;
6735 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6737 ASSEMBLY_LIST *list = param;
6738 MSIASSEMBLY *assembly;
6739 LPCWSTR component;
6741 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6742 if (!assembly)
6743 return ERROR_OUTOFMEMORY;
6745 component = MSI_RecordGetString(rec, 1);
6746 assembly->component = get_loaded_component(list->package, component);
6747 if (!assembly->component)
6748 return ERROR_SUCCESS;
6750 if (assembly->component->ActionRequest != INSTALLSTATE_LOCAL &&
6751 assembly->component->ActionRequest != INSTALLSTATE_SOURCE)
6753 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6754 assembly->component->Action = assembly->component->Installed;
6755 return ERROR_SUCCESS;
6757 assembly->component->Action = assembly->component->ActionRequest;
6759 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6760 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6762 if (!assembly->file)
6764 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6765 return ERROR_FUNCTION_FAILED;
6768 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6769 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6770 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6772 if (assembly->application)
6774 WCHAR version[24];
6775 DWORD size = sizeof(version)/sizeof(WCHAR);
6777 /* FIXME: we should probably check the manifest file here */
6779 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6780 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6782 assembly->installed = TRUE;
6785 else
6786 assembly->installed = check_assembly_installed(list->package->db,
6787 list->cache,
6788 assembly->component);
6790 list_add_head(list->assemblies, &assembly->entry);
6791 return ERROR_SUCCESS;
6794 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6796 IAssemblyCache *cache = NULL;
6797 ASSEMBLY_LIST list;
6798 MSIQUERY *view;
6799 HRESULT hr;
6800 UINT r;
6802 static const WCHAR query[] =
6803 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6804 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6806 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6807 if (r != ERROR_SUCCESS)
6808 return ERROR_SUCCESS;
6810 hr = pCreateAssemblyCache(&cache, 0);
6811 if (FAILED(hr))
6812 return ERROR_FUNCTION_FAILED;
6814 list.package = package;
6815 list.cache = cache;
6816 list.assemblies = assemblies;
6818 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6819 msiobj_release(&view->hdr);
6821 IAssemblyCache_Release(cache);
6823 return r;
6826 static void free_assemblies(struct list *assemblies)
6828 struct list *item, *cursor;
6830 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6832 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6834 list_remove(&assembly->entry);
6835 msi_free(assembly->application);
6836 msi_free(assembly->manifest);
6837 msi_free(assembly->display_name);
6838 msi_free(assembly);
6842 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6844 MSIASSEMBLY *assembly;
6846 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6848 if (!lstrcmpW(assembly->file->File, file))
6850 *out = assembly;
6851 return TRUE;
6855 return FALSE;
6858 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6859 LPWSTR *path, DWORD *attrs, PVOID user)
6861 MSIASSEMBLY *assembly;
6862 WCHAR temppath[MAX_PATH];
6863 struct list *assemblies = user;
6864 UINT r;
6866 if (!find_assembly(assemblies, file, &assembly))
6867 return FALSE;
6869 GetTempPathW(MAX_PATH, temppath);
6870 PathAddBackslashW(temppath);
6871 lstrcatW(temppath, assembly->file->FileName);
6873 if (action == MSICABEXTRACT_BEGINEXTRACT)
6875 if (assembly->installed)
6876 return FALSE;
6878 *path = strdupW(temppath);
6879 *attrs = assembly->file->Attributes;
6881 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6883 assembly->installed = TRUE;
6885 r = install_assembly(package, assembly, temppath);
6886 if (r != ERROR_SUCCESS)
6887 ERR("Failed to install assembly\n");
6890 return TRUE;
6893 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6895 UINT r;
6896 struct list assemblies = LIST_INIT(assemblies);
6897 MSIASSEMBLY *assembly;
6898 MSIMEDIAINFO *mi;
6900 if (!init_functionpointers() || !pCreateAssemblyCache)
6901 return ERROR_FUNCTION_FAILED;
6903 r = load_assemblies(package, &assemblies);
6904 if (r != ERROR_SUCCESS)
6905 goto done;
6907 if (list_empty(&assemblies))
6908 goto done;
6910 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6911 if (!mi)
6913 r = ERROR_OUTOFMEMORY;
6914 goto done;
6917 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6919 if (assembly->installed && !mi->is_continuous)
6920 continue;
6922 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6923 (assembly->file->IsCompressed && !mi->is_extracted))
6925 MSICABDATA data;
6927 r = ready_media(package, assembly->file, mi);
6928 if (r != ERROR_SUCCESS)
6930 ERR("Failed to ready media\n");
6931 break;
6934 data.mi = mi;
6935 data.package = package;
6936 data.cb = installassembly_cb;
6937 data.user = &assemblies;
6939 if (assembly->file->IsCompressed &&
6940 !msi_cabextract(package, mi, &data))
6942 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6943 r = ERROR_FUNCTION_FAILED;
6944 break;
6948 if (!assembly->file->IsCompressed)
6950 LPWSTR source = resolve_file_source(package, assembly->file);
6952 r = install_assembly(package, assembly, source);
6953 if (r != ERROR_SUCCESS)
6954 ERR("Failed to install assembly\n");
6956 msi_free(source);
6959 /* FIXME: write Installer assembly reg values */
6962 done:
6963 free_assemblies(&assemblies);
6964 return r;
6967 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6969 LPWSTR key, template, id;
6970 UINT r = ERROR_SUCCESS;
6972 id = msi_dup_property( package->db, szProductID );
6973 if (id)
6975 msi_free( id );
6976 return ERROR_SUCCESS;
6978 template = msi_dup_property( package->db, szPIDTemplate );
6979 key = msi_dup_property( package->db, szPIDKEY );
6981 if (key && template)
6983 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6984 r = msi_set_property( package->db, szProductID, key );
6986 msi_free( template );
6987 msi_free( key );
6988 return r;
6991 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6993 TRACE("\n");
6994 package->need_reboot = 1;
6995 return ERROR_SUCCESS;
6998 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7000 static const WCHAR szAvailableFreeReg[] =
7001 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7002 MSIRECORD *uirow;
7003 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7005 TRACE("%p %d kilobytes\n", package, space);
7007 uirow = MSI_CreateRecord( 1 );
7008 MSI_RecordSetInteger( uirow, 1, space );
7009 ui_actiondata( package, szAllocateRegistrySpace, uirow );
7010 msiobj_release( &uirow->hdr );
7012 return ERROR_SUCCESS;
7015 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7017 FIXME("%p\n", package);
7018 return ERROR_SUCCESS;
7021 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7023 FIXME("%p\n", package);
7024 return ERROR_SUCCESS;
7027 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7029 UINT r, count;
7030 MSIQUERY *view;
7032 static const WCHAR driver_query[] = {
7033 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7034 'O','D','B','C','D','r','i','v','e','r',0 };
7036 static const WCHAR translator_query[] = {
7037 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7038 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7040 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7041 if (r == ERROR_SUCCESS)
7043 count = 0;
7044 r = MSI_IterateRecords( view, &count, NULL, package );
7045 msiobj_release( &view->hdr );
7046 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7049 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7050 if (r == ERROR_SUCCESS)
7052 count = 0;
7053 r = MSI_IterateRecords( view, &count, NULL, package );
7054 msiobj_release( &view->hdr );
7055 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7058 return ERROR_SUCCESS;
7061 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7062 LPCSTR action, LPCWSTR table )
7064 static const WCHAR query[] = {
7065 'S','E','L','E','C','T',' ','*',' ',
7066 'F','R','O','M',' ','`','%','s','`',0 };
7067 MSIQUERY *view = NULL;
7068 DWORD count = 0;
7069 UINT r;
7071 r = MSI_OpenQuery( package->db, &view, query, table );
7072 if (r == ERROR_SUCCESS)
7074 r = MSI_IterateRecords(view, &count, NULL, package);
7075 msiobj_release(&view->hdr);
7078 if (count)
7079 FIXME("%s -> %u ignored %s table values\n",
7080 action, count, debugstr_w(table));
7082 return ERROR_SUCCESS;
7085 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7087 static const WCHAR table[] = { 'P','a','t','c','h',0 };
7088 return msi_unimplemented_action_stub( package, "PatchFiles", table );
7091 static UINT ACTION_BindImage( MSIPACKAGE *package )
7093 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7094 return msi_unimplemented_action_stub( package, "BindImage", table );
7097 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7099 static const WCHAR table[] = {
7100 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7101 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7104 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7106 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7107 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
7110 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
7112 static const WCHAR table[] = {
7113 'M','s','i','A','s','s','e','m','b','l','y',0 };
7114 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
7117 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7119 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7120 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7123 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7125 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7126 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7129 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7131 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7132 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7135 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7137 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7138 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7141 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7143 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7144 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
7147 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7149 static const struct
7151 const WCHAR *action;
7152 UINT (*handler)(MSIPACKAGE *);
7154 StandardActions[] =
7156 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7157 { szAppSearch, ACTION_AppSearch },
7158 { szBindImage, ACTION_BindImage },
7159 { szCCPSearch, ACTION_CCPSearch },
7160 { szCostFinalize, ACTION_CostFinalize },
7161 { szCostInitialize, ACTION_CostInitialize },
7162 { szCreateFolders, ACTION_CreateFolders },
7163 { szCreateShortcuts, ACTION_CreateShortcuts },
7164 { szDeleteServices, ACTION_DeleteServices },
7165 { szDisableRollback, ACTION_DisableRollback },
7166 { szDuplicateFiles, ACTION_DuplicateFiles },
7167 { szExecuteAction, ACTION_ExecuteAction },
7168 { szFileCost, ACTION_FileCost },
7169 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7170 { szForceReboot, ACTION_ForceReboot },
7171 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7172 { szInstallExecute, ACTION_InstallExecute },
7173 { szInstallExecuteAgain, ACTION_InstallExecute },
7174 { szInstallFiles, ACTION_InstallFiles},
7175 { szInstallFinalize, ACTION_InstallFinalize },
7176 { szInstallInitialize, ACTION_InstallInitialize },
7177 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7178 { szInstallValidate, ACTION_InstallValidate },
7179 { szIsolateComponents, ACTION_IsolateComponents },
7180 { szLaunchConditions, ACTION_LaunchConditions },
7181 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7182 { szMoveFiles, ACTION_MoveFiles },
7183 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7184 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7185 { szInstallODBC, ACTION_InstallODBC },
7186 { szInstallServices, ACTION_InstallServices },
7187 { szPatchFiles, ACTION_PatchFiles },
7188 { szProcessComponents, ACTION_ProcessComponents },
7189 { szPublishComponents, ACTION_PublishComponents },
7190 { szPublishFeatures, ACTION_PublishFeatures },
7191 { szPublishProduct, ACTION_PublishProduct },
7192 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7193 { szRegisterComPlus, ACTION_RegisterComPlus},
7194 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7195 { szRegisterFonts, ACTION_RegisterFonts },
7196 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7197 { szRegisterProduct, ACTION_RegisterProduct },
7198 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7199 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7200 { szRegisterUser, ACTION_RegisterUser },
7201 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7202 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7203 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7204 { szRemoveFiles, ACTION_RemoveFiles },
7205 { szRemoveFolders, ACTION_RemoveFolders },
7206 { szRemoveIniValues, ACTION_RemoveIniValues },
7207 { szRemoveODBC, ACTION_RemoveODBC },
7208 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7209 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7210 { szResolveSource, ACTION_ResolveSource },
7211 { szRMCCPSearch, ACTION_RMCCPSearch },
7212 { szScheduleReboot, ACTION_ScheduleReboot },
7213 { szSelfRegModules, ACTION_SelfRegModules },
7214 { szSelfUnregModules, ACTION_SelfUnregModules },
7215 { szSetODBCFolders, ACTION_SetODBCFolders },
7216 { szStartServices, ACTION_StartServices },
7217 { szStopServices, ACTION_StopServices },
7218 { szUnpublishComponents, ACTION_UnpublishComponents },
7219 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7220 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7221 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7222 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7223 { szUnregisterFonts, ACTION_UnregisterFonts },
7224 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7225 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7226 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7227 { szValidateProductID, ACTION_ValidateProductID },
7228 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7229 { szWriteIniValues, ACTION_WriteIniValues },
7230 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7231 { NULL, NULL },
7234 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
7235 UINT* rc, BOOL force )
7237 BOOL ret = FALSE;
7238 BOOL run = force;
7239 int i;
7241 if (!run && !package->script->CurrentlyScripting)
7242 run = TRUE;
7244 if (!run)
7246 if (strcmpW(action,szInstallFinalize) == 0 ||
7247 strcmpW(action,szInstallExecute) == 0 ||
7248 strcmpW(action,szInstallExecuteAgain) == 0)
7249 run = TRUE;
7252 i = 0;
7253 while (StandardActions[i].action != NULL)
7255 if (strcmpW(StandardActions[i].action, action)==0)
7257 if (!run)
7259 ui_actioninfo(package, action, TRUE, 0);
7260 *rc = schedule_action(package,INSTALL_SCRIPT,action);
7261 ui_actioninfo(package, action, FALSE, *rc);
7263 else
7265 ui_actionstart(package, action);
7266 if (StandardActions[i].handler)
7268 *rc = StandardActions[i].handler(package);
7270 else
7272 FIXME("unhandled standard action %s\n",debugstr_w(action));
7273 *rc = ERROR_SUCCESS;
7276 ret = TRUE;
7277 break;
7279 i++;
7281 return ret;
7284 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
7286 UINT rc = ERROR_SUCCESS;
7287 BOOL handled;
7289 TRACE("Performing action (%s)\n", debugstr_w(action));
7291 handled = ACTION_HandleStandardAction(package, action, &rc, force);
7293 if (!handled)
7294 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
7296 if (!handled)
7298 WARN("unhandled msi action %s\n", debugstr_w(action));
7299 rc = ERROR_FUNCTION_NOT_CALLED;
7302 return rc;
7305 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7307 UINT rc = ERROR_SUCCESS;
7308 BOOL handled = FALSE;
7310 TRACE("Performing action (%s)\n", debugstr_w(action));
7312 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
7314 if (!handled)
7315 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7317 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7318 handled = TRUE;
7320 if (!handled)
7322 WARN("unhandled msi action %s\n", debugstr_w(action));
7323 rc = ERROR_FUNCTION_NOT_CALLED;
7326 return rc;
7329 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7331 UINT rc = ERROR_SUCCESS;
7332 MSIRECORD *row;
7334 static const WCHAR ExecSeqQuery[] =
7335 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7336 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7337 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7338 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7339 static const WCHAR UISeqQuery[] =
7340 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7341 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7342 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7343 ' ', '=',' ','%','i',0};
7345 if (needs_ui_sequence(package))
7346 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7347 else
7348 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7350 if (row)
7352 LPCWSTR action, cond;
7354 TRACE("Running the actions\n");
7356 /* check conditions */
7357 cond = MSI_RecordGetString(row, 2);
7359 /* this is a hack to skip errors in the condition code */
7360 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7362 msiobj_release(&row->hdr);
7363 return ERROR_SUCCESS;
7366 action = MSI_RecordGetString(row, 1);
7367 if (!action)
7369 ERR("failed to fetch action\n");
7370 msiobj_release(&row->hdr);
7371 return ERROR_FUNCTION_FAILED;
7374 if (needs_ui_sequence(package))
7375 rc = ACTION_PerformUIAction(package, action, -1);
7376 else
7377 rc = ACTION_PerformAction(package, action, -1, FALSE);
7379 msiobj_release(&row->hdr);
7382 return rc;
7385 /****************************************************
7386 * TOP level entry points
7387 *****************************************************/
7389 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7390 LPCWSTR szCommandLine )
7392 UINT rc;
7393 BOOL ui_exists;
7395 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7396 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7398 msi_set_property( package->db, szAction, szInstall );
7400 package->script->InWhatSequence = SEQUENCE_INSTALL;
7402 if (szPackagePath)
7404 LPWSTR p, dir;
7405 LPCWSTR file;
7407 dir = strdupW(szPackagePath);
7408 p = strrchrW(dir, '\\');
7409 if (p)
7411 *(++p) = 0;
7412 file = szPackagePath + (p - dir);
7414 else
7416 msi_free(dir);
7417 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7418 GetCurrentDirectoryW(MAX_PATH, dir);
7419 lstrcatW(dir, szBackSlash);
7420 file = szPackagePath;
7423 msi_free( package->PackagePath );
7424 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7425 if (!package->PackagePath)
7427 msi_free(dir);
7428 return ERROR_OUTOFMEMORY;
7431 lstrcpyW(package->PackagePath, dir);
7432 lstrcatW(package->PackagePath, file);
7433 msi_free(dir);
7435 msi_set_sourcedir_props(package, FALSE);
7438 msi_parse_command_line( package, szCommandLine, FALSE );
7440 msi_apply_transforms( package );
7441 msi_apply_patches( package );
7443 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7445 TRACE("setting reinstall property\n");
7446 msi_set_property( package->db, szReinstall, szAll );
7449 /* properties may have been added by a transform */
7450 msi_clone_properties( package );
7451 msi_set_context( package );
7453 if (needs_ui_sequence( package))
7455 package->script->InWhatSequence |= SEQUENCE_UI;
7456 rc = ACTION_ProcessUISequence(package);
7457 ui_exists = ui_sequence_exists(package);
7458 if (rc == ERROR_SUCCESS || !ui_exists)
7460 package->script->InWhatSequence |= SEQUENCE_EXEC;
7461 rc = ACTION_ProcessExecSequence(package, ui_exists);
7464 else
7465 rc = ACTION_ProcessExecSequence(package, FALSE);
7467 package->script->CurrentlyScripting = FALSE;
7469 /* process the ending type action */
7470 if (rc == ERROR_SUCCESS)
7471 ACTION_PerformActionSequence(package, -1);
7472 else if (rc == ERROR_INSTALL_USEREXIT)
7473 ACTION_PerformActionSequence(package, -2);
7474 else if (rc == ERROR_INSTALL_SUSPEND)
7475 ACTION_PerformActionSequence(package, -4);
7476 else /* failed */
7477 ACTION_PerformActionSequence(package, -3);
7479 /* finish up running custom actions */
7480 ACTION_FinishCustomActions(package);
7482 if (rc == ERROR_SUCCESS && package->need_reboot)
7483 return ERROR_SUCCESS_REBOOT_REQUIRED;
7485 return rc;