msi: Create directories right before installing files.
[wine/hramrach.git] / dlls / msi / action.c
blob565ef664a0181a49134884579cb557a94236b41b
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;
499 WCHAR *p;
501 pi = msi_alloc_zero( sizeof(MSIPATCHINFO) );
502 if (!pi)
503 return ERROR_OUTOFMEMORY;
505 pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER );
506 if (!pi->patchcode)
508 msi_free( pi );
509 return ERROR_OUTOFMEMORY;
512 p = pi->patchcode;
513 if (*p != '{')
515 msi_free( pi->patchcode );
516 msi_free( pi );
517 return ERROR_PATCH_PACKAGE_INVALID;
520 p = strchrW( p + 1, '}' );
521 if (!p)
523 msi_free( pi->patchcode );
524 msi_free( pi );
525 return ERROR_PATCH_PACKAGE_INVALID;
528 if (p[1])
530 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
531 p[1] = 0;
534 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
536 pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
537 if (!pi->transforms)
539 msi_free( pi->patchcode );
540 msi_free( pi );
541 return ERROR_OUTOFMEMORY;
544 *patch = pi;
545 return r;
548 UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
550 UINT i, r = ERROR_SUCCESS;
551 WCHAR **substorage;
553 /* apply substorage transforms */
554 substorage = msi_split_string( patch->transforms, ';' );
555 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
556 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
558 msi_free( substorage );
559 if (r != ERROR_SUCCESS)
560 return r;
562 msi_set_media_source_prop( package );
565 * There might be a CAB file in the patch package,
566 * so append it to the list of storages to search for streams.
568 append_storage_to_db( package->db, patch_db->storage );
570 list_add_tail( &package->patches, &patch->entry );
571 return ERROR_SUCCESS;
574 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
576 static const WCHAR dotmsp[] = {'.','m','s','p',0};
577 MSIDATABASE *patch_db = NULL;
578 WCHAR localfile[MAX_PATH];
579 MSISUMMARYINFO *si;
580 MSIPATCHINFO *patch = NULL;
581 UINT r = ERROR_SUCCESS;
583 TRACE("%p %s\n", package, debugstr_w( file ) );
585 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
586 if ( r != ERROR_SUCCESS )
588 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
589 return r;
592 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
593 if (!si)
595 msiobj_release( &patch_db->hdr );
596 return ERROR_FUNCTION_FAILED;
599 r = msi_check_patch_applicable( package, si );
600 if (r != ERROR_SUCCESS)
602 TRACE("patch not applicable\n");
603 r = ERROR_SUCCESS;
604 goto done;
607 r = msi_parse_patch_summary( si, &patch );
608 if ( r != ERROR_SUCCESS )
609 goto done;
611 r = msi_get_local_package_name( localfile, dotmsp );
612 if ( r != ERROR_SUCCESS )
613 goto done;
615 TRACE("copying to local package %s\n", debugstr_w(localfile));
617 if (!CopyFileW( file, localfile, FALSE ))
619 ERR("Unable to copy package (%s -> %s) (error %u)\n",
620 debugstr_w(file), debugstr_w(localfile), GetLastError());
621 r = GetLastError();
622 goto done;
624 patch->localfile = strdupW( localfile );
626 r = msi_apply_patch_db( package, patch_db, patch );
627 if ( r != ERROR_SUCCESS )
628 WARN("patch failed to apply %u\n", r);
630 done:
631 msiobj_release( &si->hdr );
632 msiobj_release( &patch_db->hdr );
633 if (patch && r != ERROR_SUCCESS)
635 if (patch->localfile)
636 DeleteFileW( patch->localfile );
638 msi_free( patch->patchcode );
639 msi_free( patch->transforms );
640 msi_free( patch->localfile );
641 msi_free( patch );
643 return r;
646 /* get the PATCH property, and apply all the patches it specifies */
647 static UINT msi_apply_patches( MSIPACKAGE *package )
649 LPWSTR patch_list, *patches;
650 UINT i, r = ERROR_SUCCESS;
652 patch_list = msi_dup_property( package->db, szPatch );
654 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
656 patches = msi_split_string( patch_list, ';' );
657 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
658 r = msi_apply_patch_package( package, patches[i] );
660 msi_free( patches );
661 msi_free( patch_list );
663 return r;
666 static UINT msi_apply_transforms( MSIPACKAGE *package )
668 static const WCHAR szTransforms[] = {
669 'T','R','A','N','S','F','O','R','M','S',0 };
670 LPWSTR xform_list, *xforms;
671 UINT i, r = ERROR_SUCCESS;
673 xform_list = msi_dup_property( package->db, szTransforms );
674 xforms = msi_split_string( xform_list, ';' );
676 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
678 if (xforms[i][0] == ':')
679 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
680 else
681 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
684 msi_free( xforms );
685 msi_free( xform_list );
687 return r;
690 static BOOL ui_sequence_exists( MSIPACKAGE *package )
692 MSIQUERY *view;
693 UINT rc;
695 static const WCHAR ExecSeqQuery [] =
696 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
697 '`','I','n','s','t','a','l','l',
698 'U','I','S','e','q','u','e','n','c','e','`',
699 ' ','W','H','E','R','E',' ',
700 '`','S','e','q','u','e','n','c','e','`',' ',
701 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
702 '`','S','e','q','u','e','n','c','e','`',0};
704 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
705 if (rc == ERROR_SUCCESS)
707 msiobj_release(&view->hdr);
708 return TRUE;
711 return FALSE;
714 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
716 LPWSTR source, check;
718 if (msi_get_property_int( package->db, szInstalled, 0 ))
720 HKEY hkey;
722 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
723 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
724 RegCloseKey( hkey );
726 else
728 LPWSTR p, db;
729 DWORD len;
731 db = msi_dup_property( package->db, szOriginalDatabase );
732 if (!db)
733 return ERROR_OUTOFMEMORY;
735 p = strrchrW( db, '\\' );
736 if (!p)
738 p = strrchrW( db, '/' );
739 if (!p)
741 msi_free(db);
742 return ERROR_SUCCESS;
746 len = p - db + 2;
747 source = msi_alloc( len * sizeof(WCHAR) );
748 lstrcpynW( source, db, len );
749 msi_free( db );
752 check = msi_dup_property( package->db, cszSourceDir );
753 if (!check || replace)
755 UINT r = msi_set_property( package->db, cszSourceDir, source );
756 if (r == ERROR_SUCCESS)
757 msi_reset_folders( package, TRUE );
759 msi_free( check );
761 check = msi_dup_property( package->db, cszSOURCEDIR );
762 if (!check || replace)
763 msi_set_property( package->db, cszSOURCEDIR, source );
765 msi_free( check );
766 msi_free( source );
768 return ERROR_SUCCESS;
771 static BOOL needs_ui_sequence(MSIPACKAGE *package)
773 INT level = msi_get_property_int(package->db, szUILevel, 0);
774 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
777 UINT msi_set_context(MSIPACKAGE *package)
779 int num;
781 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
783 num = msi_get_property_int(package->db, szAllUsers, 0);
784 if (num == 1 || num == 2)
785 package->Context = MSIINSTALLCONTEXT_MACHINE;
787 return ERROR_SUCCESS;
790 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
792 UINT rc;
793 LPCWSTR cond, action;
794 MSIPACKAGE *package = param;
796 action = MSI_RecordGetString(row,1);
797 if (!action)
799 ERR("Error is retrieving action name\n");
800 return ERROR_FUNCTION_FAILED;
803 /* check conditions */
804 cond = MSI_RecordGetString(row,2);
806 /* this is a hack to skip errors in the condition code */
807 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
809 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
810 return ERROR_SUCCESS;
813 if (needs_ui_sequence(package))
814 rc = ACTION_PerformUIAction(package, action, -1);
815 else
816 rc = ACTION_PerformAction(package, action, -1, FALSE);
818 msi_dialog_check_messages( NULL );
820 if (package->CurrentInstallState != ERROR_SUCCESS)
821 rc = package->CurrentInstallState;
823 if (rc == ERROR_FUNCTION_NOT_CALLED)
824 rc = ERROR_SUCCESS;
826 if (rc != ERROR_SUCCESS)
827 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
829 return rc;
832 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
834 MSIQUERY * view;
835 UINT r;
836 static const WCHAR query[] =
837 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
838 '`','%','s','`',
839 ' ','W','H','E','R','E',' ',
840 '`','S','e','q','u','e','n','c','e','`',' ',
841 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
842 '`','S','e','q','u','e','n','c','e','`',0};
844 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
846 r = MSI_OpenQuery( package->db, &view, query, szTable );
847 if (r == ERROR_SUCCESS)
849 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
850 msiobj_release(&view->hdr);
853 return r;
856 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
858 MSIQUERY * view;
859 UINT rc;
860 static const WCHAR ExecSeqQuery[] =
861 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
862 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
863 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
864 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
865 'O','R','D','E','R',' ', 'B','Y',' ',
866 '`','S','e','q','u','e','n','c','e','`',0 };
867 static const WCHAR IVQuery[] =
868 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
869 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
870 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
871 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
872 ' ','\'', 'I','n','s','t','a','l','l',
873 'V','a','l','i','d','a','t','e','\'', 0};
874 INT seq = 0;
876 if (package->script->ExecuteSequenceRun)
878 TRACE("Execute Sequence already Run\n");
879 return ERROR_SUCCESS;
882 package->script->ExecuteSequenceRun = TRUE;
884 /* get the sequence number */
885 if (UIran)
887 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
888 if( !row )
889 return ERROR_FUNCTION_FAILED;
890 seq = MSI_RecordGetInteger(row,1);
891 msiobj_release(&row->hdr);
894 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
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 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
908 MSIQUERY * view;
909 UINT rc;
910 static const WCHAR ExecSeqQuery [] =
911 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
912 '`','I','n','s','t','a','l','l',
913 'U','I','S','e','q','u','e','n','c','e','`',
914 ' ','W','H','E','R','E',' ',
915 '`','S','e','q','u','e','n','c','e','`',' ',
916 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
917 '`','S','e','q','u','e','n','c','e','`',0};
919 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
920 if (rc == ERROR_SUCCESS)
922 TRACE("Running the actions\n");
924 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
925 msiobj_release(&view->hdr);
928 return rc;
931 /********************************************************
932 * ACTION helper functions and functions that perform the actions
933 *******************************************************/
934 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
935 UINT* rc, UINT script, BOOL force )
937 BOOL ret=FALSE;
938 UINT arc;
940 arc = ACTION_CustomAction(package, action, script, force);
942 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
944 *rc = arc;
945 ret = TRUE;
947 return ret;
951 * Actual Action Handlers
954 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
956 MSIPACKAGE *package = param;
957 LPCWSTR dir, component;
958 LPWSTR full_path;
959 MSIRECORD *uirow;
960 MSIFOLDER *folder;
961 MSICOMPONENT *comp;
963 component = MSI_RecordGetString(row, 2);
964 comp = get_loaded_component(package, component);
965 if (!comp)
966 return ERROR_SUCCESS;
968 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
970 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
971 comp->Action = comp->Installed;
972 return ERROR_SUCCESS;
974 comp->Action = INSTALLSTATE_LOCAL;
976 dir = MSI_RecordGetString(row,1);
977 if (!dir)
979 ERR("Unable to get folder id\n");
980 return ERROR_SUCCESS;
983 uirow = MSI_CreateRecord(1);
984 MSI_RecordSetStringW(uirow, 1, dir);
985 ui_actiondata(package, szCreateFolders, uirow);
986 msiobj_release(&uirow->hdr);
988 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
989 if (!full_path)
991 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
992 return ERROR_SUCCESS;
995 TRACE("Folder is %s\n",debugstr_w(full_path));
997 if (folder->State == 0)
998 create_full_pathW(full_path);
1000 folder->State = 3;
1002 msi_free(full_path);
1003 return ERROR_SUCCESS;
1006 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1008 static const WCHAR ExecSeqQuery[] =
1009 {'S','E','L','E','C','T',' ',
1010 '`','D','i','r','e','c','t','o','r','y','_','`',
1011 ' ','F','R','O','M',' ',
1012 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1013 UINT rc;
1014 MSIQUERY *view;
1016 /* create all the empty folders specified in the CreateFolder table */
1017 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1018 if (rc != ERROR_SUCCESS)
1019 return ERROR_SUCCESS;
1021 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1022 msiobj_release(&view->hdr);
1024 return rc;
1027 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1029 MSIPACKAGE *package = param;
1030 LPCWSTR dir, component;
1031 LPWSTR full_path;
1032 MSIRECORD *uirow;
1033 MSIFOLDER *folder;
1034 MSICOMPONENT *comp;
1036 component = MSI_RecordGetString(row, 2);
1037 comp = get_loaded_component(package, component);
1038 if (!comp)
1039 return ERROR_SUCCESS;
1041 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1043 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1044 comp->Action = comp->Installed;
1045 return ERROR_SUCCESS;
1047 comp->Action = INSTALLSTATE_ABSENT;
1049 dir = MSI_RecordGetString( row, 1 );
1050 if (!dir)
1052 ERR("Unable to get folder id\n");
1053 return ERROR_SUCCESS;
1056 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1057 if (!full_path)
1059 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1060 return ERROR_SUCCESS;
1063 TRACE("folder is %s\n", debugstr_w(full_path));
1065 uirow = MSI_CreateRecord( 1 );
1066 MSI_RecordSetStringW( uirow, 1, full_path );
1067 ui_actiondata( package, szRemoveFolders, uirow );
1068 msiobj_release( &uirow->hdr );
1070 RemoveDirectoryW( full_path );
1071 folder->State = 0;
1073 msi_free( full_path );
1074 return ERROR_SUCCESS;
1077 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1079 static const WCHAR query[] =
1080 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1081 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1083 MSIQUERY *view;
1084 UINT rc;
1086 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1087 if (rc != ERROR_SUCCESS)
1088 return ERROR_SUCCESS;
1090 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1091 msiobj_release( &view->hdr );
1093 return rc;
1096 static UINT load_component( MSIRECORD *row, LPVOID param )
1098 MSIPACKAGE *package = param;
1099 MSICOMPONENT *comp;
1101 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1102 if (!comp)
1103 return ERROR_FUNCTION_FAILED;
1105 list_add_tail( &package->components, &comp->entry );
1107 /* fill in the data */
1108 comp->Component = msi_dup_record_field( row, 1 );
1110 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1112 comp->ComponentId = msi_dup_record_field( row, 2 );
1113 comp->Directory = msi_dup_record_field( row, 3 );
1114 comp->Attributes = MSI_RecordGetInteger(row,4);
1115 comp->Condition = msi_dup_record_field( row, 5 );
1116 comp->KeyPath = msi_dup_record_field( row, 6 );
1118 comp->Installed = INSTALLSTATE_UNKNOWN;
1119 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1121 return ERROR_SUCCESS;
1124 static UINT load_all_components( MSIPACKAGE *package )
1126 static const WCHAR query[] = {
1127 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1128 '`','C','o','m','p','o','n','e','n','t','`',0 };
1129 MSIQUERY *view;
1130 UINT r;
1132 if (!list_empty(&package->components))
1133 return ERROR_SUCCESS;
1135 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1136 if (r != ERROR_SUCCESS)
1137 return r;
1139 r = MSI_IterateRecords(view, NULL, load_component, package);
1140 msiobj_release(&view->hdr);
1141 return r;
1144 typedef struct {
1145 MSIPACKAGE *package;
1146 MSIFEATURE *feature;
1147 } _ilfs;
1149 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1151 ComponentList *cl;
1153 cl = msi_alloc( sizeof (*cl) );
1154 if ( !cl )
1155 return ERROR_NOT_ENOUGH_MEMORY;
1156 cl->component = comp;
1157 list_add_tail( &feature->Components, &cl->entry );
1159 return ERROR_SUCCESS;
1162 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1164 FeatureList *fl;
1166 fl = msi_alloc( sizeof(*fl) );
1167 if ( !fl )
1168 return ERROR_NOT_ENOUGH_MEMORY;
1169 fl->feature = child;
1170 list_add_tail( &parent->Children, &fl->entry );
1172 return ERROR_SUCCESS;
1175 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1177 _ilfs* ilfs = param;
1178 LPCWSTR component;
1179 MSICOMPONENT *comp;
1181 component = MSI_RecordGetString(row,1);
1183 /* check to see if the component is already loaded */
1184 comp = get_loaded_component( ilfs->package, component );
1185 if (!comp)
1187 ERR("unknown component %s\n", debugstr_w(component));
1188 return ERROR_FUNCTION_FAILED;
1191 add_feature_component( ilfs->feature, comp );
1192 comp->Enabled = TRUE;
1194 return ERROR_SUCCESS;
1197 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1199 MSIFEATURE *feature;
1201 if ( !name )
1202 return NULL;
1204 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1206 if ( !lstrcmpW( feature->Feature, name ) )
1207 return feature;
1210 return NULL;
1213 static UINT load_feature(MSIRECORD * row, LPVOID param)
1215 MSIPACKAGE* package = param;
1216 MSIFEATURE* feature;
1217 static const WCHAR Query1[] =
1218 {'S','E','L','E','C','T',' ',
1219 '`','C','o','m','p','o','n','e','n','t','_','`',
1220 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1221 'C','o','m','p','o','n','e','n','t','s','`',' ',
1222 'W','H','E','R','E',' ',
1223 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1224 MSIQUERY * view;
1225 UINT rc;
1226 _ilfs ilfs;
1228 /* fill in the data */
1230 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1231 if (!feature)
1232 return ERROR_NOT_ENOUGH_MEMORY;
1234 list_init( &feature->Children );
1235 list_init( &feature->Components );
1237 feature->Feature = msi_dup_record_field( row, 1 );
1239 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1241 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1242 feature->Title = msi_dup_record_field( row, 3 );
1243 feature->Description = msi_dup_record_field( row, 4 );
1245 if (!MSI_RecordIsNull(row,5))
1246 feature->Display = MSI_RecordGetInteger(row,5);
1248 feature->Level= MSI_RecordGetInteger(row,6);
1249 feature->Directory = msi_dup_record_field( row, 7 );
1250 feature->Attributes = MSI_RecordGetInteger(row,8);
1252 feature->Installed = INSTALLSTATE_UNKNOWN;
1253 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1255 list_add_tail( &package->features, &feature->entry );
1257 /* load feature components */
1259 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1260 if (rc != ERROR_SUCCESS)
1261 return ERROR_SUCCESS;
1263 ilfs.package = package;
1264 ilfs.feature = feature;
1266 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1267 msiobj_release(&view->hdr);
1269 return ERROR_SUCCESS;
1272 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1274 MSIPACKAGE* package = param;
1275 MSIFEATURE *parent, *child;
1277 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1278 if (!child)
1279 return ERROR_FUNCTION_FAILED;
1281 if (!child->Feature_Parent)
1282 return ERROR_SUCCESS;
1284 parent = find_feature_by_name( package, child->Feature_Parent );
1285 if (!parent)
1286 return ERROR_FUNCTION_FAILED;
1288 add_feature_child( parent, child );
1289 return ERROR_SUCCESS;
1292 static UINT load_all_features( MSIPACKAGE *package )
1294 static const WCHAR query[] = {
1295 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1296 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1297 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1298 MSIQUERY *view;
1299 UINT r;
1301 if (!list_empty(&package->features))
1302 return ERROR_SUCCESS;
1304 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1305 if (r != ERROR_SUCCESS)
1306 return r;
1308 r = MSI_IterateRecords( view, NULL, load_feature, package );
1309 if (r != ERROR_SUCCESS)
1310 return r;
1312 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1313 msiobj_release( &view->hdr );
1315 return r;
1318 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1320 if (!p)
1321 return p;
1322 p = strchrW(p, ch);
1323 if (!p)
1324 return p;
1325 *p = 0;
1326 return p+1;
1329 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1331 static const WCHAR query[] = {
1332 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1333 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1334 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1335 MSIQUERY *view = NULL;
1336 MSIRECORD *row = NULL;
1337 UINT r;
1339 TRACE("%s\n", debugstr_w(file->File));
1341 r = MSI_OpenQuery(package->db, &view, query, file->File);
1342 if (r != ERROR_SUCCESS)
1343 goto done;
1345 r = MSI_ViewExecute(view, NULL);
1346 if (r != ERROR_SUCCESS)
1347 goto done;
1349 r = MSI_ViewFetch(view, &row);
1350 if (r != ERROR_SUCCESS)
1351 goto done;
1353 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1354 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1355 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1356 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1357 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1359 done:
1360 if (view) msiobj_release(&view->hdr);
1361 if (row) msiobj_release(&row->hdr);
1362 return r;
1365 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1367 MSIRECORD *row;
1368 static const WCHAR query[] = {
1369 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1370 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1371 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1373 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1374 if (!row)
1376 WARN("query failed\n");
1377 return ERROR_FUNCTION_FAILED;
1380 file->disk_id = MSI_RecordGetInteger( row, 1 );
1381 msiobj_release( &row->hdr );
1382 return ERROR_SUCCESS;
1385 static UINT load_file(MSIRECORD *row, LPVOID param)
1387 MSIPACKAGE* package = param;
1388 LPCWSTR component;
1389 MSIFILE *file;
1391 /* fill in the data */
1393 file = msi_alloc_zero( sizeof (MSIFILE) );
1394 if (!file)
1395 return ERROR_NOT_ENOUGH_MEMORY;
1397 file->File = msi_dup_record_field( row, 1 );
1399 component = MSI_RecordGetString( row, 2 );
1400 file->Component = get_loaded_component( package, component );
1402 if (!file->Component)
1404 WARN("Component not found: %s\n", debugstr_w(component));
1405 msi_free(file->File);
1406 msi_free(file);
1407 return ERROR_SUCCESS;
1410 file->FileName = msi_dup_record_field( row, 3 );
1411 reduce_to_longfilename( file->FileName );
1413 file->ShortName = msi_dup_record_field( row, 3 );
1414 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1416 file->FileSize = MSI_RecordGetInteger( row, 4 );
1417 file->Version = msi_dup_record_field( row, 5 );
1418 file->Language = msi_dup_record_field( row, 6 );
1419 file->Attributes = MSI_RecordGetInteger( row, 7 );
1420 file->Sequence = MSI_RecordGetInteger( row, 8 );
1422 file->state = msifs_invalid;
1424 /* if the compressed bits are not set in the file attributes,
1425 * then read the information from the package word count property
1427 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1429 file->IsCompressed = FALSE;
1431 else if (file->Attributes &
1432 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1434 file->IsCompressed = TRUE;
1436 else if (file->Attributes & msidbFileAttributesNoncompressed)
1438 file->IsCompressed = FALSE;
1440 else
1442 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1445 load_file_hash(package, file);
1446 load_file_disk_id(package, file);
1448 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1450 list_add_tail( &package->files, &file->entry );
1452 return ERROR_SUCCESS;
1455 static UINT load_all_files(MSIPACKAGE *package)
1457 MSIQUERY * view;
1458 UINT rc;
1459 static const WCHAR Query[] =
1460 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1461 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1462 '`','S','e','q','u','e','n','c','e','`', 0};
1464 if (!list_empty(&package->files))
1465 return ERROR_SUCCESS;
1467 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1468 if (rc != ERROR_SUCCESS)
1469 return ERROR_SUCCESS;
1471 rc = MSI_IterateRecords(view, NULL, load_file, package);
1472 msiobj_release(&view->hdr);
1474 return ERROR_SUCCESS;
1477 static UINT load_folder( MSIRECORD *row, LPVOID param )
1479 MSIPACKAGE *package = param;
1480 static WCHAR szEmpty[] = { 0 };
1481 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1482 MSIFOLDER *folder;
1484 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1485 if (!folder)
1486 return ERROR_NOT_ENOUGH_MEMORY;
1488 folder->Directory = msi_dup_record_field( row, 1 );
1490 TRACE("%s\n", debugstr_w(folder->Directory));
1492 p = msi_dup_record_field(row, 3);
1494 /* split src and target dir */
1495 tgt_short = p;
1496 src_short = folder_split_path( p, ':' );
1498 /* split the long and short paths */
1499 tgt_long = folder_split_path( tgt_short, '|' );
1500 src_long = folder_split_path( src_short, '|' );
1502 /* check for no-op dirs */
1503 if (!lstrcmpW(szDot, tgt_short))
1504 tgt_short = szEmpty;
1505 if (!lstrcmpW(szDot, src_short))
1506 src_short = szEmpty;
1508 if (!tgt_long)
1509 tgt_long = tgt_short;
1511 if (!src_short) {
1512 src_short = tgt_short;
1513 src_long = tgt_long;
1516 if (!src_long)
1517 src_long = src_short;
1519 /* FIXME: use the target short path too */
1520 folder->TargetDefault = strdupW(tgt_long);
1521 folder->SourceShortPath = strdupW(src_short);
1522 folder->SourceLongPath = strdupW(src_long);
1523 msi_free(p);
1525 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1526 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1527 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1529 folder->Parent = msi_dup_record_field( row, 2 );
1531 folder->Property = msi_dup_property( package->db, folder->Directory );
1533 list_add_tail( &package->folders, &folder->entry );
1535 TRACE("returning %p\n", folder);
1537 return ERROR_SUCCESS;
1540 static UINT load_all_folders( MSIPACKAGE *package )
1542 static const WCHAR query[] = {
1543 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1544 '`','D','i','r','e','c','t','o','r','y','`',0 };
1545 MSIQUERY *view;
1546 UINT r;
1548 if (!list_empty(&package->folders))
1549 return ERROR_SUCCESS;
1551 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1552 if (r != ERROR_SUCCESS)
1553 return r;
1555 r = MSI_IterateRecords(view, NULL, load_folder, package);
1556 msiobj_release(&view->hdr);
1557 return r;
1561 * I am not doing any of the costing functionality yet.
1562 * Mostly looking at doing the Component and Feature loading
1564 * The native MSI does A LOT of modification to tables here. Mostly adding
1565 * a lot of temporary columns to the Feature and Component tables.
1567 * note: Native msi also tracks the short filename. But I am only going to
1568 * track the long ones. Also looking at this directory table
1569 * it appears that the directory table does not get the parents
1570 * resolved base on property only based on their entries in the
1571 * directory table.
1573 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1575 static const WCHAR szCosting[] =
1576 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1578 msi_set_property( package->db, szCosting, szZero );
1579 msi_set_property( package->db, cszRootDrive, c_colon );
1581 load_all_folders( package );
1582 load_all_components( package );
1583 load_all_features( package );
1584 load_all_files( package );
1586 return ERROR_SUCCESS;
1589 static UINT execute_script(MSIPACKAGE *package, UINT script )
1591 UINT i;
1592 UINT rc = ERROR_SUCCESS;
1594 TRACE("Executing Script %i\n",script);
1596 if (!package->script)
1598 ERR("no script!\n");
1599 return ERROR_FUNCTION_FAILED;
1602 for (i = 0; i < package->script->ActionCount[script]; i++)
1604 LPWSTR action;
1605 action = package->script->Actions[script][i];
1606 ui_actionstart(package, action);
1607 TRACE("Executing Action (%s)\n",debugstr_w(action));
1608 rc = ACTION_PerformAction(package, action, script, TRUE);
1609 if (rc != ERROR_SUCCESS)
1610 break;
1612 msi_free_action_script(package, script);
1613 return rc;
1616 static UINT ACTION_FileCost(MSIPACKAGE *package)
1618 return ERROR_SUCCESS;
1621 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1623 MSICOMPONENT *comp;
1624 INSTALLSTATE state;
1625 UINT r;
1627 state = MsiQueryProductStateW(package->ProductCode);
1629 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1631 if (!comp->ComponentId)
1632 continue;
1634 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1635 comp->Installed = INSTALLSTATE_ABSENT;
1636 else
1638 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1639 package->Context, comp->ComponentId,
1640 &comp->Installed);
1641 if (r != ERROR_SUCCESS)
1642 comp->Installed = INSTALLSTATE_ABSENT;
1647 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1649 MSIFEATURE *feature;
1650 INSTALLSTATE state;
1652 state = MsiQueryProductStateW(package->ProductCode);
1654 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1656 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1657 feature->Installed = INSTALLSTATE_ABSENT;
1658 else
1660 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1661 feature->Feature);
1666 static BOOL process_state_property(MSIPACKAGE* package, int level,
1667 LPCWSTR property, INSTALLSTATE state)
1669 LPWSTR override;
1670 MSIFEATURE *feature;
1672 override = msi_dup_property( package->db, property );
1673 if (!override)
1674 return FALSE;
1676 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1678 if (lstrcmpW(property, szRemove) &&
1679 (feature->Level <= 0 || feature->Level > level))
1680 continue;
1682 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1684 if (strcmpiW(override, szAll)==0)
1685 msi_feature_set_state(package, feature, state);
1686 else
1688 LPWSTR ptr = override;
1689 LPWSTR ptr2 = strchrW(override,',');
1691 while (ptr)
1693 int len = ptr2 - ptr;
1695 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1696 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1698 msi_feature_set_state(package, feature, state);
1699 break;
1701 if (ptr2)
1703 ptr=ptr2+1;
1704 ptr2 = strchrW(ptr,',');
1706 else
1707 break;
1711 msi_free(override);
1713 return TRUE;
1716 static BOOL process_overrides( MSIPACKAGE *package, int level )
1718 static const WCHAR szAddLocal[] =
1719 {'A','D','D','L','O','C','A','L',0};
1720 static const WCHAR szAddSource[] =
1721 {'A','D','D','S','O','U','R','C','E',0};
1722 static const WCHAR szAdvertise[] =
1723 {'A','D','V','E','R','T','I','S','E',0};
1724 BOOL ret = FALSE;
1726 /* all these activation/deactivation things happen in order and things
1727 * later on the list override things earlier on the list.
1729 * 0 INSTALLLEVEL processing
1730 * 1 ADDLOCAL
1731 * 2 REMOVE
1732 * 3 ADDSOURCE
1733 * 4 ADDDEFAULT
1734 * 5 REINSTALL
1735 * 6 ADVERTISE
1736 * 7 COMPADDLOCAL
1737 * 8 COMPADDSOURCE
1738 * 9 FILEADDLOCAL
1739 * 10 FILEADDSOURCE
1740 * 11 FILEADDDEFAULT
1742 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1743 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1744 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1745 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1746 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1748 if (ret)
1749 msi_set_property( package->db, szPreselected, szOne );
1751 return ret;
1754 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1756 int level;
1757 static const WCHAR szlevel[] =
1758 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1759 MSICOMPONENT* component;
1760 MSIFEATURE *feature;
1762 TRACE("Checking Install Level\n");
1764 level = msi_get_property_int(package->db, szlevel, 1);
1766 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1768 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1770 BOOL feature_state = ((feature->Level > 0) &&
1771 (feature->Level <= level));
1773 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1775 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1776 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1777 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1778 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1779 else
1780 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1784 /* disable child features of unselected parent features */
1785 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1787 FeatureList *fl;
1789 if (feature->Level > 0 && feature->Level <= level)
1790 continue;
1792 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1793 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1798 * now we want to enable or disable components base on feature
1801 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1803 ComponentList *cl;
1805 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1806 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1808 if (!feature->Level)
1809 continue;
1811 /* features with components that have compressed files are made local */
1812 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1814 if (cl->component->Enabled &&
1815 cl->component->ForceLocalState &&
1816 feature->Action == INSTALLSTATE_SOURCE)
1818 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1819 break;
1823 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1825 component = cl->component;
1827 if (!component->Enabled)
1828 continue;
1830 switch (feature->Action)
1832 case INSTALLSTATE_ABSENT:
1833 component->anyAbsent = 1;
1834 break;
1835 case INSTALLSTATE_ADVERTISED:
1836 component->hasAdvertiseFeature = 1;
1837 break;
1838 case INSTALLSTATE_SOURCE:
1839 component->hasSourceFeature = 1;
1840 break;
1841 case INSTALLSTATE_LOCAL:
1842 component->hasLocalFeature = 1;
1843 break;
1844 case INSTALLSTATE_DEFAULT:
1845 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1846 component->hasAdvertiseFeature = 1;
1847 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1848 component->hasSourceFeature = 1;
1849 else
1850 component->hasLocalFeature = 1;
1851 break;
1852 default:
1853 break;
1858 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1860 /* if the component isn't enabled, leave it alone */
1861 if (!component->Enabled)
1862 continue;
1864 /* check if it's local or source */
1865 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1866 (component->hasLocalFeature || component->hasSourceFeature))
1868 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1869 !component->ForceLocalState)
1870 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1871 else
1872 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1873 continue;
1876 /* if any feature is local, the component must be local too */
1877 if (component->hasLocalFeature)
1879 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1880 continue;
1883 if (component->hasSourceFeature)
1885 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1886 continue;
1889 if (component->hasAdvertiseFeature)
1891 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1892 continue;
1895 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1896 if (component->anyAbsent)
1897 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1900 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1902 if (component->Action == INSTALLSTATE_DEFAULT)
1904 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1905 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1908 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1909 debugstr_w(component->Component), component->Installed, component->Action);
1913 return ERROR_SUCCESS;
1916 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1918 MSIPACKAGE *package = param;
1919 LPCWSTR name;
1920 LPWSTR path;
1921 MSIFOLDER *f;
1923 name = MSI_RecordGetString(row,1);
1925 f = get_loaded_folder(package, name);
1926 if (!f) return ERROR_SUCCESS;
1928 /* reset the ResolvedTarget */
1929 msi_free(f->ResolvedTarget);
1930 f->ResolvedTarget = NULL;
1932 /* This helper function now does ALL the work */
1933 TRACE("Dir %s ...\n",debugstr_w(name));
1934 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1935 TRACE("resolves to %s\n",debugstr_w(path));
1936 msi_free(path);
1938 return ERROR_SUCCESS;
1941 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1943 MSIPACKAGE *package = param;
1944 LPCWSTR name;
1945 MSIFEATURE *feature;
1947 name = MSI_RecordGetString( row, 1 );
1949 feature = get_loaded_feature( package, name );
1950 if (!feature)
1951 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1952 else
1954 LPCWSTR Condition;
1955 Condition = MSI_RecordGetString(row,3);
1957 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1959 int level = MSI_RecordGetInteger(row,2);
1960 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1961 feature->Level = level;
1964 return ERROR_SUCCESS;
1967 static LPWSTR get_disk_file_version( LPCWSTR filename )
1969 static const WCHAR name_fmt[] =
1970 {'%','u','.','%','u','.','%','u','.','%','u',0};
1971 static const WCHAR name[] = {'\\',0};
1972 VS_FIXEDFILEINFO *lpVer;
1973 WCHAR filever[0x100];
1974 LPVOID version;
1975 DWORD versize;
1976 DWORD handle;
1977 UINT sz;
1979 TRACE("%s\n", debugstr_w(filename));
1981 versize = GetFileVersionInfoSizeW( filename, &handle );
1982 if (!versize)
1983 return NULL;
1985 version = msi_alloc( versize );
1986 GetFileVersionInfoW( filename, 0, versize, version );
1988 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1990 msi_free( version );
1991 return NULL;
1994 sprintfW( filever, name_fmt,
1995 HIWORD(lpVer->dwFileVersionMS),
1996 LOWORD(lpVer->dwFileVersionMS),
1997 HIWORD(lpVer->dwFileVersionLS),
1998 LOWORD(lpVer->dwFileVersionLS));
2000 msi_free( version );
2002 return strdupW( filever );
2005 static DWORD get_disk_file_size( LPCWSTR filename )
2007 HANDLE file;
2008 DWORD size;
2010 TRACE("%s\n", debugstr_w(filename));
2012 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2013 if (file == INVALID_HANDLE_VALUE)
2014 return INVALID_FILE_SIZE;
2016 size = GetFileSize( file, NULL );
2017 CloseHandle( file );
2018 return size;
2021 static BOOL hash_matches( MSIFILE *file )
2023 UINT r;
2024 MSIFILEHASHINFO hash;
2026 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2027 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2028 if (r != ERROR_SUCCESS)
2029 return FALSE;
2031 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2034 static UINT set_file_install_states( MSIPACKAGE *package )
2036 LPWSTR file_version;
2037 MSIFILE *file;
2039 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2041 MSICOMPONENT* comp = file->Component;
2042 DWORD file_size;
2043 LPWSTR p;
2045 if (!comp)
2046 continue;
2048 if (file->IsCompressed)
2049 comp->ForceLocalState = TRUE;
2051 /* calculate target */
2052 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2054 msi_free(file->TargetPath);
2056 TRACE("file %s is named %s\n",
2057 debugstr_w(file->File), debugstr_w(file->FileName));
2059 file->TargetPath = build_directory_name(2, p, file->FileName);
2061 msi_free(p);
2063 TRACE("file %s resolves to %s\n",
2064 debugstr_w(file->File), debugstr_w(file->TargetPath));
2066 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2068 file->state = msifs_missing;
2069 comp->Cost += file->FileSize;
2070 continue;
2072 if (file->Version && (file_version = get_disk_file_version( file->TargetPath )))
2074 TRACE("new %s old %s\n", debugstr_w(file->Version), debugstr_w(file_version));
2076 if (strcmpiW(file_version, file->Version) < 0)
2078 file->state = msifs_overwrite;
2079 comp->Cost += file->FileSize;
2081 else
2083 TRACE("Destination file version equal or greater, not overwriting\n");
2084 file->state = msifs_present;
2086 msi_free( file_version );
2087 continue;
2089 if ((file_size = get_disk_file_size( file->TargetPath )) != file->FileSize)
2091 file->state = msifs_overwrite;
2092 comp->Cost += file->FileSize - file_size;
2093 continue;
2095 if (file->hash.dwFileHashInfoSize && hash_matches( file ))
2097 TRACE("File hashes match, not overwriting\n");
2098 file->state = msifs_present;
2099 continue;
2101 file->state = msifs_overwrite;
2102 comp->Cost += file->FileSize - file_size;
2105 return ERROR_SUCCESS;
2109 * A lot is done in this function aside from just the costing.
2110 * The costing needs to be implemented at some point but for now I am going
2111 * to focus on the directory building
2114 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2116 static const WCHAR ExecSeqQuery[] =
2117 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2118 '`','D','i','r','e','c','t','o','r','y','`',0};
2119 static const WCHAR ConditionQuery[] =
2120 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2121 '`','C','o','n','d','i','t','i','o','n','`',0};
2122 static const WCHAR szCosting[] =
2123 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2124 static const WCHAR szlevel[] =
2125 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2126 static const WCHAR szOutOfDiskSpace[] =
2127 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2128 MSICOMPONENT *comp;
2129 UINT rc = ERROR_SUCCESS;
2130 MSIQUERY * view;
2131 LPWSTR level;
2133 TRACE("Building Directory properties\n");
2135 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2136 if (rc == ERROR_SUCCESS)
2138 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2139 package);
2140 msiobj_release(&view->hdr);
2143 /* read components states from the registry */
2144 ACTION_GetComponentInstallStates(package);
2145 ACTION_GetFeatureInstallStates(package);
2147 TRACE("Calculating file install states\n");
2148 set_file_install_states( package );
2150 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2152 TRACE("Evaluating feature conditions\n");
2154 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2155 if (rc == ERROR_SUCCESS)
2157 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2158 msiobj_release( &view->hdr );
2161 TRACE("Evaluating component conditions\n");
2163 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2165 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2167 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2168 comp->Enabled = FALSE;
2170 else
2171 comp->Enabled = TRUE;
2174 msi_set_property( package->db, szCosting, szOne );
2175 /* set default run level if not set */
2176 level = msi_dup_property( package->db, szlevel );
2177 if (!level)
2178 msi_set_property( package->db, szlevel, szOne );
2179 msi_free(level);
2181 /* FIXME: check volume disk space */
2182 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2184 return MSI_SetFeatureStates(package);
2187 /* OK this value is "interpreted" and then formatted based on the
2188 first few characters */
2189 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2190 DWORD *size)
2192 LPSTR data = NULL;
2194 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2196 if (value[1]=='x')
2198 LPWSTR ptr;
2199 CHAR byte[5];
2200 LPWSTR deformated = NULL;
2201 int count;
2203 deformat_string(package, &value[2], &deformated);
2205 /* binary value type */
2206 ptr = deformated;
2207 *type = REG_BINARY;
2208 if (strlenW(ptr)%2)
2209 *size = (strlenW(ptr)/2)+1;
2210 else
2211 *size = strlenW(ptr)/2;
2213 data = msi_alloc(*size);
2215 byte[0] = '0';
2216 byte[1] = 'x';
2217 byte[4] = 0;
2218 count = 0;
2219 /* if uneven pad with a zero in front */
2220 if (strlenW(ptr)%2)
2222 byte[2]= '0';
2223 byte[3]= *ptr;
2224 ptr++;
2225 data[count] = (BYTE)strtol(byte,NULL,0);
2226 count ++;
2227 TRACE("Uneven byte count\n");
2229 while (*ptr)
2231 byte[2]= *ptr;
2232 ptr++;
2233 byte[3]= *ptr;
2234 ptr++;
2235 data[count] = (BYTE)strtol(byte,NULL,0);
2236 count ++;
2238 msi_free(deformated);
2240 TRACE("Data %i bytes(%i)\n",*size,count);
2242 else
2244 LPWSTR deformated;
2245 LPWSTR p;
2246 DWORD d = 0;
2247 deformat_string(package, &value[1], &deformated);
2249 *type=REG_DWORD;
2250 *size = sizeof(DWORD);
2251 data = msi_alloc(*size);
2252 p = deformated;
2253 if (*p == '-')
2254 p++;
2255 while (*p)
2257 if ( (*p < '0') || (*p > '9') )
2258 break;
2259 d *= 10;
2260 d += (*p - '0');
2261 p++;
2263 if (deformated[0] == '-')
2264 d = -d;
2265 *(LPDWORD)data = d;
2266 TRACE("DWORD %i\n",*(LPDWORD)data);
2268 msi_free(deformated);
2271 else
2273 static const WCHAR szMulti[] = {'[','~',']',0};
2274 LPCWSTR ptr;
2275 *type=REG_SZ;
2277 if (value[0]=='#')
2279 if (value[1]=='%')
2281 ptr = &value[2];
2282 *type=REG_EXPAND_SZ;
2284 else
2285 ptr = &value[1];
2287 else
2288 ptr=value;
2290 if (strstrW(value,szMulti))
2291 *type = REG_MULTI_SZ;
2293 /* remove initial delimiter */
2294 if (!strncmpW(value, szMulti, 3))
2295 ptr = value + 3;
2297 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2299 /* add double NULL terminator */
2300 if (*type == REG_MULTI_SZ)
2302 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2303 data = msi_realloc_zero(data, *size);
2306 return data;
2309 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2311 const WCHAR *ret;
2313 switch (root)
2315 case -1:
2316 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2318 *root_key = HKEY_LOCAL_MACHINE;
2319 ret = szHLM;
2321 else
2323 *root_key = HKEY_CURRENT_USER;
2324 ret = szHCU;
2326 break;
2327 case 0:
2328 *root_key = HKEY_CLASSES_ROOT;
2329 ret = szHCR;
2330 break;
2331 case 1:
2332 *root_key = HKEY_CURRENT_USER;
2333 ret = szHCU;
2334 break;
2335 case 2:
2336 *root_key = HKEY_LOCAL_MACHINE;
2337 ret = szHLM;
2338 break;
2339 case 3:
2340 *root_key = HKEY_USERS;
2341 ret = szHU;
2342 break;
2343 default:
2344 ERR("Unknown root %i\n", root);
2345 return NULL;
2348 return ret;
2351 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2353 MSIPACKAGE *package = param;
2354 LPSTR value_data = NULL;
2355 HKEY root_key, hkey;
2356 DWORD type,size;
2357 LPWSTR deformated;
2358 LPCWSTR szRoot, component, name, key, value;
2359 MSICOMPONENT *comp;
2360 MSIRECORD * uirow;
2361 LPWSTR uikey;
2362 INT root;
2363 BOOL check_first = FALSE;
2364 UINT rc;
2366 ui_progress(package,2,0,0,0);
2368 value = NULL;
2369 key = NULL;
2370 uikey = NULL;
2371 name = NULL;
2373 component = MSI_RecordGetString(row, 6);
2374 comp = get_loaded_component(package,component);
2375 if (!comp)
2376 return ERROR_SUCCESS;
2378 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2380 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2381 comp->Action = comp->Installed;
2382 return ERROR_SUCCESS;
2384 comp->Action = INSTALLSTATE_LOCAL;
2386 name = MSI_RecordGetString(row, 4);
2387 if( MSI_RecordIsNull(row,5) && name )
2389 /* null values can have special meanings */
2390 if (name[0]=='-' && name[1] == 0)
2391 return ERROR_SUCCESS;
2392 else if ((name[0]=='+' && name[1] == 0) ||
2393 (name[0] == '*' && name[1] == 0))
2394 name = NULL;
2395 check_first = TRUE;
2398 root = MSI_RecordGetInteger(row,2);
2399 key = MSI_RecordGetString(row, 3);
2401 szRoot = get_root_key( package, root, &root_key );
2402 if (!szRoot)
2403 return ERROR_SUCCESS;
2405 deformat_string(package, key , &deformated);
2406 size = strlenW(deformated) + strlenW(szRoot) + 1;
2407 uikey = msi_alloc(size*sizeof(WCHAR));
2408 strcpyW(uikey,szRoot);
2409 strcatW(uikey,deformated);
2411 if (RegCreateKeyW( root_key, deformated, &hkey))
2413 ERR("Could not create key %s\n",debugstr_w(deformated));
2414 msi_free(deformated);
2415 msi_free(uikey);
2416 return ERROR_SUCCESS;
2418 msi_free(deformated);
2420 value = MSI_RecordGetString(row,5);
2421 if (value)
2422 value_data = parse_value(package, value, &type, &size);
2423 else
2425 value_data = (LPSTR)strdupW(szEmpty);
2426 size = sizeof(szEmpty);
2427 type = REG_SZ;
2430 deformat_string(package, name, &deformated);
2432 if (!check_first)
2434 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2435 debugstr_w(uikey));
2436 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2438 else
2440 DWORD sz = 0;
2441 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2442 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2444 TRACE("value %s of %s checked already exists\n",
2445 debugstr_w(deformated), debugstr_w(uikey));
2447 else
2449 TRACE("Checked and setting value %s of %s\n",
2450 debugstr_w(deformated), debugstr_w(uikey));
2451 if (deformated || size)
2452 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2455 RegCloseKey(hkey);
2457 uirow = MSI_CreateRecord(3);
2458 MSI_RecordSetStringW(uirow,2,deformated);
2459 MSI_RecordSetStringW(uirow,1,uikey);
2460 if (type == REG_SZ || type == REG_EXPAND_SZ)
2461 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2462 ui_actiondata(package,szWriteRegistryValues,uirow);
2463 msiobj_release( &uirow->hdr );
2465 msi_free(value_data);
2466 msi_free(deformated);
2467 msi_free(uikey);
2469 return ERROR_SUCCESS;
2472 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2474 UINT rc;
2475 MSIQUERY * view;
2476 static const WCHAR ExecSeqQuery[] =
2477 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2478 '`','R','e','g','i','s','t','r','y','`',0 };
2480 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2481 if (rc != ERROR_SUCCESS)
2482 return ERROR_SUCCESS;
2484 /* increment progress bar each time action data is sent */
2485 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2487 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2489 msiobj_release(&view->hdr);
2490 return rc;
2493 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2495 LONG res;
2496 HKEY hkey;
2497 DWORD num_subkeys, num_values;
2499 if (delete_key)
2501 if ((res = RegDeleteTreeW( hkey_root, key )))
2503 WARN("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2505 return;
2508 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2510 if ((res = RegDeleteValueW( hkey, value )))
2512 WARN("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2514 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2515 NULL, NULL, NULL, NULL );
2516 RegCloseKey( hkey );
2518 if (!res && !num_subkeys && !num_values)
2520 TRACE("Removing empty key %s\n", debugstr_w(key));
2521 RegDeleteKeyW( hkey_root, key );
2523 return;
2525 WARN("Failed to open key %s (%d)\n", debugstr_w(key), res);
2529 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2531 MSIPACKAGE *package = param;
2532 LPCWSTR component, name, key_str, root_key_str;
2533 LPWSTR deformated_key, deformated_name, ui_key_str;
2534 MSICOMPONENT *comp;
2535 MSIRECORD *uirow;
2536 BOOL delete_key = FALSE;
2537 HKEY hkey_root;
2538 UINT size;
2539 INT root;
2541 ui_progress( package, 2, 0, 0, 0 );
2543 component = MSI_RecordGetString( row, 6 );
2544 comp = get_loaded_component( package, component );
2545 if (!comp)
2546 return ERROR_SUCCESS;
2548 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2550 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2551 comp->Action = comp->Installed;
2552 return ERROR_SUCCESS;
2554 comp->Action = INSTALLSTATE_ABSENT;
2556 name = MSI_RecordGetString( row, 4 );
2557 if (MSI_RecordIsNull( row, 5 ) && name )
2559 if (name[0] == '+' && !name[1])
2560 return ERROR_SUCCESS;
2561 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2563 delete_key = TRUE;
2564 name = NULL;
2568 root = MSI_RecordGetInteger( row, 2 );
2569 key_str = MSI_RecordGetString( row, 3 );
2571 root_key_str = get_root_key( package, root, &hkey_root );
2572 if (!root_key_str)
2573 return ERROR_SUCCESS;
2575 deformat_string( package, key_str, &deformated_key );
2576 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2577 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2578 strcpyW( ui_key_str, root_key_str );
2579 strcatW( ui_key_str, deformated_key );
2581 deformat_string( package, name, &deformated_name );
2583 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2584 msi_free( deformated_key );
2586 uirow = MSI_CreateRecord( 2 );
2587 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2588 MSI_RecordSetStringW( uirow, 2, deformated_name );
2590 ui_actiondata( package, szRemoveRegistryValues, uirow );
2591 msiobj_release( &uirow->hdr );
2593 msi_free( ui_key_str );
2594 msi_free( deformated_name );
2595 return ERROR_SUCCESS;
2598 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2600 MSIPACKAGE *package = param;
2601 LPCWSTR component, name, key_str, root_key_str;
2602 LPWSTR deformated_key, deformated_name, ui_key_str;
2603 MSICOMPONENT *comp;
2604 MSIRECORD *uirow;
2605 BOOL delete_key = FALSE;
2606 HKEY hkey_root;
2607 UINT size;
2608 INT root;
2610 ui_progress( package, 2, 0, 0, 0 );
2612 component = MSI_RecordGetString( row, 5 );
2613 comp = get_loaded_component( package, component );
2614 if (!comp)
2615 return ERROR_SUCCESS;
2617 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2619 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2620 comp->Action = comp->Installed;
2621 return ERROR_SUCCESS;
2623 comp->Action = INSTALLSTATE_LOCAL;
2625 if ((name = MSI_RecordGetString( row, 4 )))
2627 if (name[0] == '-' && !name[1])
2629 delete_key = TRUE;
2630 name = NULL;
2634 root = MSI_RecordGetInteger( row, 2 );
2635 key_str = MSI_RecordGetString( row, 3 );
2637 root_key_str = get_root_key( package, root, &hkey_root );
2638 if (!root_key_str)
2639 return ERROR_SUCCESS;
2641 deformat_string( package, key_str, &deformated_key );
2642 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2643 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2644 strcpyW( ui_key_str, root_key_str );
2645 strcatW( ui_key_str, deformated_key );
2647 deformat_string( package, name, &deformated_name );
2649 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2650 msi_free( deformated_key );
2652 uirow = MSI_CreateRecord( 2 );
2653 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2654 MSI_RecordSetStringW( uirow, 2, deformated_name );
2656 ui_actiondata( package, szRemoveRegistryValues, uirow );
2657 msiobj_release( &uirow->hdr );
2659 msi_free( ui_key_str );
2660 msi_free( deformated_name );
2661 return ERROR_SUCCESS;
2664 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2666 UINT rc;
2667 MSIQUERY *view;
2668 static const WCHAR registry_query[] =
2669 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2670 '`','R','e','g','i','s','t','r','y','`',0 };
2671 static const WCHAR remove_registry_query[] =
2672 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2673 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2675 /* increment progress bar each time action data is sent */
2676 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2678 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2679 if (rc == ERROR_SUCCESS)
2681 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2682 msiobj_release( &view->hdr );
2683 if (rc != ERROR_SUCCESS)
2684 return rc;
2687 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2688 if (rc == ERROR_SUCCESS)
2690 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2691 msiobj_release( &view->hdr );
2692 if (rc != ERROR_SUCCESS)
2693 return rc;
2696 return ERROR_SUCCESS;
2699 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2701 package->script->CurrentlyScripting = TRUE;
2703 return ERROR_SUCCESS;
2707 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2709 MSICOMPONENT *comp;
2710 DWORD progress = 0;
2711 DWORD total = 0;
2712 static const WCHAR q1[]=
2713 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2714 '`','R','e','g','i','s','t','r','y','`',0};
2715 UINT rc;
2716 MSIQUERY * view;
2717 MSIFEATURE *feature;
2718 MSIFILE *file;
2720 TRACE("InstallValidate\n");
2722 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2723 if (rc == ERROR_SUCCESS)
2725 MSI_IterateRecords( view, &progress, NULL, package );
2726 msiobj_release( &view->hdr );
2727 total += progress * REG_PROGRESS_VALUE;
2730 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2731 total += COMPONENT_PROGRESS_VALUE;
2733 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2734 total += file->FileSize;
2736 ui_progress(package,0,total,0,0);
2738 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2740 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2741 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2742 feature->ActionRequest);
2745 return ERROR_SUCCESS;
2748 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2750 MSIPACKAGE* package = param;
2751 LPCWSTR cond = NULL;
2752 LPCWSTR message = NULL;
2753 UINT r;
2755 static const WCHAR title[]=
2756 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2758 cond = MSI_RecordGetString(row,1);
2760 r = MSI_EvaluateConditionW(package,cond);
2761 if (r == MSICONDITION_FALSE)
2763 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2765 LPWSTR deformated;
2766 message = MSI_RecordGetString(row,2);
2767 deformat_string(package,message,&deformated);
2768 MessageBoxW(NULL,deformated,title,MB_OK);
2769 msi_free(deformated);
2772 return ERROR_INSTALL_FAILURE;
2775 return ERROR_SUCCESS;
2778 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2780 UINT rc;
2781 MSIQUERY * view = NULL;
2782 static const WCHAR ExecSeqQuery[] =
2783 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2784 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2786 TRACE("Checking launch conditions\n");
2788 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2789 if (rc != ERROR_SUCCESS)
2790 return ERROR_SUCCESS;
2792 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2793 msiobj_release(&view->hdr);
2795 return rc;
2798 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2801 if (!cmp->KeyPath)
2802 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2804 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2806 MSIRECORD * row = 0;
2807 UINT root,len;
2808 LPWSTR deformated,buffer,deformated_name;
2809 LPCWSTR key,name;
2810 static const WCHAR ExecSeqQuery[] =
2811 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2812 '`','R','e','g','i','s','t','r','y','`',' ',
2813 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2814 ' ','=',' ' ,'\'','%','s','\'',0 };
2815 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2816 static const WCHAR fmt2[]=
2817 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2819 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2820 if (!row)
2821 return NULL;
2823 root = MSI_RecordGetInteger(row,2);
2824 key = MSI_RecordGetString(row, 3);
2825 name = MSI_RecordGetString(row, 4);
2826 deformat_string(package, key , &deformated);
2827 deformat_string(package, name, &deformated_name);
2829 len = strlenW(deformated) + 6;
2830 if (deformated_name)
2831 len+=strlenW(deformated_name);
2833 buffer = msi_alloc( len *sizeof(WCHAR));
2835 if (deformated_name)
2836 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2837 else
2838 sprintfW(buffer,fmt,root,deformated);
2840 msi_free(deformated);
2841 msi_free(deformated_name);
2842 msiobj_release(&row->hdr);
2844 return buffer;
2846 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2848 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2849 return NULL;
2851 else
2853 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2855 if (file)
2856 return strdupW( file->TargetPath );
2858 return NULL;
2861 static HKEY openSharedDLLsKey(void)
2863 HKEY hkey=0;
2864 static const WCHAR path[] =
2865 {'S','o','f','t','w','a','r','e','\\',
2866 'M','i','c','r','o','s','o','f','t','\\',
2867 'W','i','n','d','o','w','s','\\',
2868 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2869 'S','h','a','r','e','d','D','L','L','s',0};
2871 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2872 return hkey;
2875 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2877 HKEY hkey;
2878 DWORD count=0;
2879 DWORD type;
2880 DWORD sz = sizeof(count);
2881 DWORD rc;
2883 hkey = openSharedDLLsKey();
2884 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2885 if (rc != ERROR_SUCCESS)
2886 count = 0;
2887 RegCloseKey(hkey);
2888 return count;
2891 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2893 HKEY hkey;
2895 hkey = openSharedDLLsKey();
2896 if (count > 0)
2897 msi_reg_set_val_dword( hkey, path, count );
2898 else
2899 RegDeleteValueW(hkey,path);
2900 RegCloseKey(hkey);
2901 return count;
2905 * Return TRUE if the count should be written out and FALSE if not
2907 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2909 MSIFEATURE *feature;
2910 INT count = 0;
2911 BOOL write = FALSE;
2913 /* only refcount DLLs */
2914 if (comp->KeyPath == NULL ||
2915 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2916 comp->Attributes & msidbComponentAttributesODBCDataSource)
2917 write = FALSE;
2918 else
2920 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2921 write = (count > 0);
2923 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2924 write = TRUE;
2927 /* increment counts */
2928 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2930 ComponentList *cl;
2932 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2933 continue;
2935 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2937 if ( cl->component == comp )
2938 count++;
2942 /* decrement counts */
2943 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2945 ComponentList *cl;
2947 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2948 continue;
2950 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2952 if ( cl->component == comp )
2953 count--;
2957 /* ref count all the files in the component */
2958 if (write)
2960 MSIFILE *file;
2962 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2964 if (file->Component == comp)
2965 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2969 /* add a count for permanent */
2970 if (comp->Attributes & msidbComponentAttributesPermanent)
2971 count ++;
2973 comp->RefCount = count;
2975 if (write)
2976 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2979 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2981 WCHAR squished_pc[GUID_SIZE];
2982 WCHAR squished_cc[GUID_SIZE];
2983 UINT rc;
2984 MSICOMPONENT *comp;
2985 HKEY hkey;
2987 TRACE("\n");
2989 squash_guid(package->ProductCode,squished_pc);
2990 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2992 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2994 MSIRECORD * uirow;
2996 ui_progress(package,2,0,0,0);
2997 if (!comp->ComponentId)
2998 continue;
3000 squash_guid(comp->ComponentId,squished_cc);
3002 msi_free(comp->FullKeypath);
3003 comp->FullKeypath = resolve_keypath( package, comp );
3005 ACTION_RefCountComponent( package, comp );
3007 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
3008 debugstr_w(comp->Component),
3009 debugstr_w(squished_cc),
3010 debugstr_w(comp->FullKeypath),
3011 comp->RefCount);
3013 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3014 comp->ActionRequest == INSTALLSTATE_SOURCE)
3016 if (!comp->FullKeypath)
3017 continue;
3019 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3020 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
3021 &hkey, TRUE);
3022 else
3023 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
3024 &hkey, TRUE);
3026 if (rc != ERROR_SUCCESS)
3027 continue;
3029 if (comp->Attributes & msidbComponentAttributesPermanent)
3031 static const WCHAR szPermKey[] =
3032 { '0','0','0','0','0','0','0','0','0','0','0','0',
3033 '0','0','0','0','0','0','0','0','0','0','0','0',
3034 '0','0','0','0','0','0','0','0',0 };
3036 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3039 if (comp->Action == INSTALLSTATE_LOCAL)
3040 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3041 else
3043 MSIFILE *file;
3044 MSIRECORD *row;
3045 LPWSTR ptr, ptr2;
3046 WCHAR source[MAX_PATH];
3047 WCHAR base[MAX_PATH];
3048 LPWSTR sourcepath;
3050 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3051 static const WCHAR query[] = {
3052 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3053 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3054 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3055 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3056 '`','D','i','s','k','I','d','`',0};
3058 file = get_loaded_file(package, comp->KeyPath);
3059 if (!file)
3060 continue;
3062 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3063 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3064 ptr2 = strrchrW(source, '\\') + 1;
3065 msiobj_release(&row->hdr);
3067 lstrcpyW(base, package->PackagePath);
3068 ptr = strrchrW(base, '\\');
3069 *(ptr + 1) = '\0';
3071 sourcepath = resolve_file_source(package, file);
3072 ptr = sourcepath + lstrlenW(base);
3073 lstrcpyW(ptr2, ptr);
3074 msi_free(sourcepath);
3076 msi_reg_set_val_str(hkey, squished_pc, source);
3078 RegCloseKey(hkey);
3080 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3082 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3083 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3084 else
3085 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3087 comp->Action = comp->ActionRequest;
3089 /* UI stuff */
3090 uirow = MSI_CreateRecord(3);
3091 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3092 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3093 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3094 ui_actiondata(package,szProcessComponents,uirow);
3095 msiobj_release( &uirow->hdr );
3098 return ERROR_SUCCESS;
3101 typedef struct {
3102 CLSID clsid;
3103 LPWSTR source;
3105 LPWSTR path;
3106 ITypeLib *ptLib;
3107 } typelib_struct;
3109 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3110 LPWSTR lpszName, LONG_PTR lParam)
3112 TLIBATTR *attr;
3113 typelib_struct *tl_struct = (typelib_struct*) lParam;
3114 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3115 int sz;
3116 HRESULT res;
3118 if (!IS_INTRESOURCE(lpszName))
3120 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3121 return TRUE;
3124 sz = strlenW(tl_struct->source)+4;
3125 sz *= sizeof(WCHAR);
3127 if ((INT_PTR)lpszName == 1)
3128 tl_struct->path = strdupW(tl_struct->source);
3129 else
3131 tl_struct->path = msi_alloc(sz);
3132 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3135 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3136 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3137 if (FAILED(res))
3139 msi_free(tl_struct->path);
3140 tl_struct->path = NULL;
3142 return TRUE;
3145 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3146 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3148 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3149 return FALSE;
3152 msi_free(tl_struct->path);
3153 tl_struct->path = NULL;
3155 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3156 ITypeLib_Release(tl_struct->ptLib);
3158 return TRUE;
3161 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3163 MSIPACKAGE* package = param;
3164 LPCWSTR component;
3165 MSICOMPONENT *comp;
3166 MSIFILE *file;
3167 typelib_struct tl_struct;
3168 ITypeLib *tlib;
3169 HMODULE module;
3170 HRESULT hr;
3172 component = MSI_RecordGetString(row,3);
3173 comp = get_loaded_component(package,component);
3174 if (!comp)
3175 return ERROR_SUCCESS;
3177 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3179 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3180 comp->Action = comp->Installed;
3181 return ERROR_SUCCESS;
3183 comp->Action = INSTALLSTATE_LOCAL;
3185 file = get_loaded_file( package, comp->KeyPath );
3186 if (!file)
3187 return ERROR_SUCCESS;
3189 ui_actiondata( package, szRegisterTypeLibraries, row );
3191 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3192 if (module)
3194 LPCWSTR guid;
3195 guid = MSI_RecordGetString(row,1);
3196 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3197 tl_struct.source = strdupW( file->TargetPath );
3198 tl_struct.path = NULL;
3200 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3201 (LONG_PTR)&tl_struct);
3203 if (tl_struct.path)
3205 LPWSTR help = NULL;
3206 LPCWSTR helpid;
3207 HRESULT res;
3209 helpid = MSI_RecordGetString(row,6);
3211 if (helpid)
3212 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3213 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3214 msi_free(help);
3216 if (FAILED(res))
3217 ERR("Failed to register type library %s\n",
3218 debugstr_w(tl_struct.path));
3219 else
3220 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3222 ITypeLib_Release(tl_struct.ptLib);
3223 msi_free(tl_struct.path);
3225 else
3226 ERR("Failed to load type library %s\n",
3227 debugstr_w(tl_struct.source));
3229 FreeLibrary(module);
3230 msi_free(tl_struct.source);
3232 else
3234 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3235 if (FAILED(hr))
3237 ERR("Failed to load type library: %08x\n", hr);
3238 return ERROR_INSTALL_FAILURE;
3241 ITypeLib_Release(tlib);
3244 return ERROR_SUCCESS;
3247 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3250 * OK this is a bit confusing.. I am given a _Component key and I believe
3251 * that the file that is being registered as a type library is the "key file
3252 * of that component" which I interpret to mean "The file in the KeyPath of
3253 * that component".
3255 UINT rc;
3256 MSIQUERY * view;
3257 static const WCHAR Query[] =
3258 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3259 '`','T','y','p','e','L','i','b','`',0};
3261 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3262 if (rc != ERROR_SUCCESS)
3263 return ERROR_SUCCESS;
3265 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3266 msiobj_release(&view->hdr);
3267 return rc;
3270 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3272 MSIPACKAGE *package = param;
3273 LPCWSTR component, guid;
3274 MSICOMPONENT *comp;
3275 GUID libid;
3276 UINT version;
3277 LCID language;
3278 SYSKIND syskind;
3279 HRESULT hr;
3281 component = MSI_RecordGetString( row, 3 );
3282 comp = get_loaded_component( package, component );
3283 if (!comp)
3284 return ERROR_SUCCESS;
3286 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3288 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3289 comp->Action = comp->Installed;
3290 return ERROR_SUCCESS;
3292 comp->Action = INSTALLSTATE_ABSENT;
3294 ui_actiondata( package, szUnregisterTypeLibraries, row );
3296 guid = MSI_RecordGetString( row, 1 );
3297 CLSIDFromString( (LPCWSTR)guid, &libid );
3298 version = MSI_RecordGetInteger( row, 4 );
3299 language = MSI_RecordGetInteger( row, 2 );
3301 #ifdef _WIN64
3302 syskind = SYS_WIN64;
3303 #else
3304 syskind = SYS_WIN32;
3305 #endif
3307 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3308 if (FAILED(hr))
3310 WARN("Failed to unregister typelib: %08x\n", hr);
3313 return ERROR_SUCCESS;
3316 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3318 UINT rc;
3319 MSIQUERY *view;
3320 static const WCHAR query[] =
3321 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3322 '`','T','y','p','e','L','i','b','`',0};
3324 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3325 if (rc != ERROR_SUCCESS)
3326 return ERROR_SUCCESS;
3328 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3329 msiobj_release( &view->hdr );
3330 return rc;
3333 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3335 static const WCHAR szlnk[] = {'.','l','n','k',0};
3336 LPCWSTR directory, extension;
3337 LPWSTR link_folder, link_file, filename;
3339 directory = MSI_RecordGetString( row, 2 );
3340 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3342 /* may be needed because of a bug somewhere else */
3343 create_full_pathW( link_folder );
3345 filename = msi_dup_record_field( row, 3 );
3346 reduce_to_longfilename( filename );
3348 extension = strchrW( filename, '.' );
3349 if (!extension || strcmpiW( extension, szlnk ))
3351 int len = strlenW( filename );
3352 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3353 memcpy( filename + len, szlnk, sizeof(szlnk) );
3355 link_file = build_directory_name( 2, link_folder, filename );
3356 msi_free( link_folder );
3357 msi_free( filename );
3359 return link_file;
3362 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3364 MSIPACKAGE *package = param;
3365 LPWSTR link_file, deformated, path;
3366 LPCWSTR component, target;
3367 MSICOMPONENT *comp;
3368 IShellLinkW *sl = NULL;
3369 IPersistFile *pf = NULL;
3370 HRESULT res;
3372 component = MSI_RecordGetString(row, 4);
3373 comp = get_loaded_component(package, component);
3374 if (!comp)
3375 return ERROR_SUCCESS;
3377 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3379 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3380 comp->Action = comp->Installed;
3381 return ERROR_SUCCESS;
3383 comp->Action = INSTALLSTATE_LOCAL;
3385 ui_actiondata(package,szCreateShortcuts,row);
3387 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3388 &IID_IShellLinkW, (LPVOID *) &sl );
3390 if (FAILED( res ))
3392 ERR("CLSID_ShellLink not available\n");
3393 goto err;
3396 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3397 if (FAILED( res ))
3399 ERR("QueryInterface(IID_IPersistFile) failed\n");
3400 goto err;
3403 target = MSI_RecordGetString(row, 5);
3404 if (strchrW(target, '['))
3406 deformat_string(package, target, &deformated);
3407 IShellLinkW_SetPath(sl,deformated);
3408 msi_free(deformated);
3410 else
3412 FIXME("poorly handled shortcut format, advertised shortcut\n");
3413 IShellLinkW_SetPath(sl,comp->FullKeypath);
3416 if (!MSI_RecordIsNull(row,6))
3418 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3419 deformat_string(package, arguments, &deformated);
3420 IShellLinkW_SetArguments(sl,deformated);
3421 msi_free(deformated);
3424 if (!MSI_RecordIsNull(row,7))
3426 LPCWSTR description = MSI_RecordGetString(row, 7);
3427 IShellLinkW_SetDescription(sl, description);
3430 if (!MSI_RecordIsNull(row,8))
3431 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3433 if (!MSI_RecordIsNull(row,9))
3435 INT index;
3436 LPCWSTR icon = MSI_RecordGetString(row, 9);
3438 path = build_icon_path(package, icon);
3439 index = MSI_RecordGetInteger(row,10);
3441 /* no value means 0 */
3442 if (index == MSI_NULL_INTEGER)
3443 index = 0;
3445 IShellLinkW_SetIconLocation(sl, path, index);
3446 msi_free(path);
3449 if (!MSI_RecordIsNull(row,11))
3450 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3452 if (!MSI_RecordIsNull(row,12))
3454 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3455 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3456 if (path)
3457 IShellLinkW_SetWorkingDirectory(sl, path);
3458 msi_free(path);
3461 link_file = get_link_file(package, row);
3463 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3464 IPersistFile_Save(pf, link_file, FALSE);
3466 msi_free(link_file);
3468 err:
3469 if (pf)
3470 IPersistFile_Release( pf );
3471 if (sl)
3472 IShellLinkW_Release( sl );
3474 return ERROR_SUCCESS;
3477 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3479 UINT rc;
3480 HRESULT res;
3481 MSIQUERY * view;
3482 static const WCHAR Query[] =
3483 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3484 '`','S','h','o','r','t','c','u','t','`',0};
3486 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3487 if (rc != ERROR_SUCCESS)
3488 return ERROR_SUCCESS;
3490 res = CoInitialize( NULL );
3492 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3493 msiobj_release(&view->hdr);
3495 if (SUCCEEDED(res))
3496 CoUninitialize();
3498 return rc;
3501 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3503 MSIPACKAGE *package = param;
3504 LPWSTR link_file;
3505 LPCWSTR component;
3506 MSICOMPONENT *comp;
3508 component = MSI_RecordGetString( row, 4 );
3509 comp = get_loaded_component( package, component );
3510 if (!comp)
3511 return ERROR_SUCCESS;
3513 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3515 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3516 comp->Action = comp->Installed;
3517 return ERROR_SUCCESS;
3519 comp->Action = INSTALLSTATE_ABSENT;
3521 ui_actiondata( package, szRemoveShortcuts, row );
3523 link_file = get_link_file( package, row );
3525 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3526 if (!DeleteFileW( link_file ))
3528 WARN("Failed to remove shortcut file %u\n", GetLastError());
3530 msi_free( link_file );
3532 return ERROR_SUCCESS;
3535 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3537 UINT rc;
3538 MSIQUERY *view;
3539 static const WCHAR query[] =
3540 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3541 '`','S','h','o','r','t','c','u','t','`',0};
3543 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3544 if (rc != ERROR_SUCCESS)
3545 return ERROR_SUCCESS;
3547 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3548 msiobj_release( &view->hdr );
3550 return rc;
3553 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3555 MSIPACKAGE* package = param;
3556 HANDLE the_file;
3557 LPWSTR FilePath;
3558 LPCWSTR FileName;
3559 CHAR buffer[1024];
3560 DWORD sz;
3561 UINT rc;
3563 FileName = MSI_RecordGetString(row,1);
3564 if (!FileName)
3566 ERR("Unable to get FileName\n");
3567 return ERROR_SUCCESS;
3570 FilePath = build_icon_path(package,FileName);
3572 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3574 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3575 FILE_ATTRIBUTE_NORMAL, NULL);
3577 if (the_file == INVALID_HANDLE_VALUE)
3579 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3580 msi_free(FilePath);
3581 return ERROR_SUCCESS;
3586 DWORD write;
3587 sz = 1024;
3588 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3589 if (rc != ERROR_SUCCESS)
3591 ERR("Failed to get stream\n");
3592 CloseHandle(the_file);
3593 DeleteFileW(FilePath);
3594 break;
3596 WriteFile(the_file,buffer,sz,&write,NULL);
3597 } while (sz == 1024);
3599 msi_free(FilePath);
3600 CloseHandle(the_file);
3602 return ERROR_SUCCESS;
3605 static UINT msi_publish_icons(MSIPACKAGE *package)
3607 UINT r;
3608 MSIQUERY *view;
3610 static const WCHAR query[]= {
3611 'S','E','L','E','C','T',' ','*',' ',
3612 'F','R','O','M',' ','`','I','c','o','n','`',0};
3614 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3615 if (r == ERROR_SUCCESS)
3617 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3618 msiobj_release(&view->hdr);
3621 return ERROR_SUCCESS;
3624 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3626 UINT r;
3627 HKEY source;
3628 LPWSTR buffer;
3629 MSIMEDIADISK *disk;
3630 MSISOURCELISTINFO *info;
3632 r = RegCreateKeyW(hkey, szSourceList, &source);
3633 if (r != ERROR_SUCCESS)
3634 return r;
3636 RegCloseKey(source);
3638 buffer = strrchrW(package->PackagePath, '\\') + 1;
3639 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3640 package->Context, MSICODE_PRODUCT,
3641 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3642 if (r != ERROR_SUCCESS)
3643 return r;
3645 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3646 package->Context, MSICODE_PRODUCT,
3647 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3648 if (r != ERROR_SUCCESS)
3649 return r;
3651 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3652 package->Context, MSICODE_PRODUCT,
3653 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3654 if (r != ERROR_SUCCESS)
3655 return r;
3657 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3659 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3660 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3661 info->options, info->value);
3662 else
3663 MsiSourceListSetInfoW(package->ProductCode, NULL,
3664 info->context, info->options,
3665 info->property, info->value);
3668 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3670 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3671 disk->context, disk->options,
3672 disk->disk_id, disk->volume_label, disk->disk_prompt);
3675 return ERROR_SUCCESS;
3678 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3680 MSIHANDLE hdb, suminfo;
3681 WCHAR guids[MAX_PATH];
3682 WCHAR packcode[SQUISH_GUID_SIZE];
3683 LPWSTR buffer;
3684 LPWSTR ptr;
3685 DWORD langid;
3686 DWORD size;
3687 UINT r;
3689 static const WCHAR szProductLanguage[] =
3690 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3691 static const WCHAR szARPProductIcon[] =
3692 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3693 static const WCHAR szProductVersion[] =
3694 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3695 static const WCHAR szAssignment[] =
3696 {'A','s','s','i','g','n','m','e','n','t',0};
3697 static const WCHAR szAdvertiseFlags[] =
3698 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3699 static const WCHAR szClients[] =
3700 {'C','l','i','e','n','t','s',0};
3701 static const WCHAR szColon[] = {':',0};
3703 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3704 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3705 msi_free(buffer);
3707 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3708 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3710 /* FIXME */
3711 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3713 buffer = msi_dup_property(package->db, szARPProductIcon);
3714 if (buffer)
3716 LPWSTR path = build_icon_path(package,buffer);
3717 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3718 msi_free(path);
3719 msi_free(buffer);
3722 buffer = msi_dup_property(package->db, szProductVersion);
3723 if (buffer)
3725 DWORD verdword = msi_version_str_to_dword(buffer);
3726 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3727 msi_free(buffer);
3730 msi_reg_set_val_dword(hkey, szAssignment, 0);
3731 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3732 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3733 msi_reg_set_val_str(hkey, szClients, szColon);
3735 hdb = alloc_msihandle(&package->db->hdr);
3736 if (!hdb)
3737 return ERROR_NOT_ENOUGH_MEMORY;
3739 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3740 MsiCloseHandle(hdb);
3741 if (r != ERROR_SUCCESS)
3742 goto done;
3744 size = MAX_PATH;
3745 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3746 NULL, guids, &size);
3747 if (r != ERROR_SUCCESS)
3748 goto done;
3750 ptr = strchrW(guids, ';');
3751 if (ptr) *ptr = 0;
3752 squash_guid(guids, packcode);
3753 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3755 done:
3756 MsiCloseHandle(suminfo);
3757 return ERROR_SUCCESS;
3760 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3762 UINT r;
3763 HKEY hkey;
3764 LPWSTR upgrade;
3765 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3767 static const WCHAR szUpgradeCode[] =
3768 {'U','p','g','r','a','d','e','C','o','d','e',0};
3770 upgrade = msi_dup_property(package->db, szUpgradeCode);
3771 if (!upgrade)
3772 return ERROR_SUCCESS;
3774 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3776 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3777 if (r != ERROR_SUCCESS)
3778 goto done;
3780 else
3782 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3783 if (r != ERROR_SUCCESS)
3784 goto done;
3787 squash_guid(package->ProductCode, squashed_pc);
3788 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3790 RegCloseKey(hkey);
3792 done:
3793 msi_free(upgrade);
3794 return r;
3797 static BOOL msi_check_publish(MSIPACKAGE *package)
3799 MSIFEATURE *feature;
3801 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3803 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3804 return TRUE;
3807 return FALSE;
3810 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3812 MSIFEATURE *feature;
3814 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3816 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3817 return FALSE;
3820 return TRUE;
3823 static UINT msi_publish_patches( MSIPACKAGE *package, HKEY prodkey )
3825 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
3826 WCHAR patch_squashed[GUID_SIZE];
3827 HKEY patches_key = NULL, product_patches_key;
3828 LONG res;
3829 MSIPATCHINFO *patch;
3830 UINT r;
3831 WCHAR *p, *all_patches = NULL;
3832 DWORD len = 0;
3834 res = RegCreateKeyExW( prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
3835 if (res != ERROR_SUCCESS)
3836 return ERROR_FUNCTION_FAILED;
3838 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
3839 if (r != ERROR_SUCCESS)
3840 goto done;
3842 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3844 squash_guid( patch->patchcode, patch_squashed );
3845 len += strlenW( patch_squashed ) + 1;
3848 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
3849 if (!all_patches)
3850 goto done;
3852 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3854 HKEY patch_key;
3856 squash_guid( patch->patchcode, p );
3857 p += strlenW( p ) + 1;
3859 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
3860 (const BYTE *)patch->transforms,
3861 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
3862 if (res != ERROR_SUCCESS)
3863 goto done;
3865 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
3866 if (r != ERROR_SUCCESS)
3867 goto done;
3869 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
3870 (const BYTE *)patch->localfile,
3871 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
3872 RegCloseKey( patch_key );
3873 if (res != ERROR_SUCCESS)
3874 goto done;
3876 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
3877 RegCloseKey( patch_key );
3878 if (res != ERROR_SUCCESS)
3879 goto done;
3882 all_patches[len] = 0;
3883 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
3884 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3885 if (res != ERROR_SUCCESS)
3886 goto done;
3888 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
3889 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3890 if (res != ERROR_SUCCESS)
3891 r = ERROR_FUNCTION_FAILED;
3893 done:
3894 RegCloseKey( product_patches_key );
3895 RegCloseKey( patches_key );
3896 msi_free( all_patches );
3897 return r;
3901 * 99% of the work done here is only done for
3902 * advertised installs. However this is where the
3903 * Icon table is processed and written out
3904 * so that is what I am going to do here.
3906 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3908 UINT rc;
3909 HKEY hukey = NULL, hudkey = NULL;
3910 MSIRECORD *uirow;
3912 /* FIXME: also need to publish if the product is in advertise mode */
3913 if (!msi_check_publish(package))
3914 return ERROR_SUCCESS;
3916 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3917 &hukey, TRUE);
3918 if (rc != ERROR_SUCCESS)
3919 goto end;
3921 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3922 NULL, &hudkey, TRUE);
3923 if (rc != ERROR_SUCCESS)
3924 goto end;
3926 rc = msi_publish_upgrade_code(package);
3927 if (rc != ERROR_SUCCESS)
3928 goto end;
3930 if (!list_empty(&package->patches))
3932 rc = msi_publish_patches(package, hukey);
3933 if (rc != ERROR_SUCCESS)
3934 goto end;
3937 rc = msi_publish_product_properties(package, hukey);
3938 if (rc != ERROR_SUCCESS)
3939 goto end;
3941 rc = msi_publish_sourcelist(package, hukey);
3942 if (rc != ERROR_SUCCESS)
3943 goto end;
3945 rc = msi_publish_icons(package);
3947 end:
3948 uirow = MSI_CreateRecord( 1 );
3949 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
3950 ui_actiondata( package, szPublishProduct, uirow );
3951 msiobj_release( &uirow->hdr );
3953 RegCloseKey(hukey);
3954 RegCloseKey(hudkey);
3956 return rc;
3959 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
3961 WCHAR *filename, *ptr, *folder, *ret;
3962 const WCHAR *dirprop;
3964 filename = msi_dup_record_field( row, 2 );
3965 if (filename && (ptr = strchrW( filename, '|' )))
3966 ptr++;
3967 else
3968 ptr = filename;
3970 dirprop = MSI_RecordGetString( row, 3 );
3971 if (dirprop)
3973 folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
3974 if (!folder)
3975 folder = msi_dup_property( package->db, dirprop );
3977 else
3978 folder = msi_dup_property( package->db, szWindowsFolder );
3980 if (!folder)
3982 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
3983 msi_free( filename );
3984 return NULL;
3987 ret = build_directory_name( 2, folder, ptr );
3989 msi_free( filename );
3990 msi_free( folder );
3991 return ret;
3994 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3996 MSIPACKAGE *package = param;
3997 LPCWSTR component, section, key, value, identifier;
3998 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
3999 MSIRECORD * uirow;
4000 INT action;
4001 MSICOMPONENT *comp;
4003 component = MSI_RecordGetString(row, 8);
4004 comp = get_loaded_component(package,component);
4005 if (!comp)
4006 return ERROR_SUCCESS;
4008 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4010 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4011 comp->Action = comp->Installed;
4012 return ERROR_SUCCESS;
4014 comp->Action = INSTALLSTATE_LOCAL;
4016 identifier = MSI_RecordGetString(row,1);
4017 section = MSI_RecordGetString(row,4);
4018 key = MSI_RecordGetString(row,5);
4019 value = MSI_RecordGetString(row,6);
4020 action = MSI_RecordGetInteger(row,7);
4022 deformat_string(package,section,&deformated_section);
4023 deformat_string(package,key,&deformated_key);
4024 deformat_string(package,value,&deformated_value);
4026 fullname = get_ini_file_name(package, row);
4028 if (action == 0)
4030 TRACE("Adding value %s to section %s in %s\n",
4031 debugstr_w(deformated_key), debugstr_w(deformated_section),
4032 debugstr_w(fullname));
4033 WritePrivateProfileStringW(deformated_section, deformated_key,
4034 deformated_value, fullname);
4036 else if (action == 1)
4038 WCHAR returned[10];
4039 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4040 returned, 10, fullname);
4041 if (returned[0] == 0)
4043 TRACE("Adding value %s to section %s in %s\n",
4044 debugstr_w(deformated_key), debugstr_w(deformated_section),
4045 debugstr_w(fullname));
4047 WritePrivateProfileStringW(deformated_section, deformated_key,
4048 deformated_value, fullname);
4051 else if (action == 3)
4052 FIXME("Append to existing section not yet implemented\n");
4054 uirow = MSI_CreateRecord(4);
4055 MSI_RecordSetStringW(uirow,1,identifier);
4056 MSI_RecordSetStringW(uirow,2,deformated_section);
4057 MSI_RecordSetStringW(uirow,3,deformated_key);
4058 MSI_RecordSetStringW(uirow,4,deformated_value);
4059 ui_actiondata(package,szWriteIniValues,uirow);
4060 msiobj_release( &uirow->hdr );
4062 msi_free(fullname);
4063 msi_free(deformated_key);
4064 msi_free(deformated_value);
4065 msi_free(deformated_section);
4066 return ERROR_SUCCESS;
4069 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4071 UINT rc;
4072 MSIQUERY * view;
4073 static const WCHAR ExecSeqQuery[] =
4074 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4075 '`','I','n','i','F','i','l','e','`',0};
4077 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4078 if (rc != ERROR_SUCCESS)
4080 TRACE("no IniFile table\n");
4081 return ERROR_SUCCESS;
4084 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4085 msiobj_release(&view->hdr);
4086 return rc;
4089 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4091 MSIPACKAGE *package = param;
4092 LPCWSTR component, section, key, value, identifier;
4093 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4094 MSICOMPONENT *comp;
4095 MSIRECORD *uirow;
4096 INT action;
4098 component = MSI_RecordGetString( row, 8 );
4099 comp = get_loaded_component( package, component );
4100 if (!comp)
4101 return ERROR_SUCCESS;
4103 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4105 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4106 comp->Action = comp->Installed;
4107 return ERROR_SUCCESS;
4109 comp->Action = INSTALLSTATE_ABSENT;
4111 identifier = MSI_RecordGetString( row, 1 );
4112 section = MSI_RecordGetString( row, 4 );
4113 key = MSI_RecordGetString( row, 5 );
4114 value = MSI_RecordGetString( row, 6 );
4115 action = MSI_RecordGetInteger( row, 7 );
4117 deformat_string( package, section, &deformated_section );
4118 deformat_string( package, key, &deformated_key );
4119 deformat_string( package, value, &deformated_value );
4121 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4123 filename = get_ini_file_name( package, row );
4125 TRACE("Removing key %s from section %s in %s\n",
4126 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4128 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4130 WARN("Unable to remove key %u\n", GetLastError());
4132 msi_free( filename );
4134 else
4135 FIXME("Unsupported action %d\n", action);
4138 uirow = MSI_CreateRecord( 4 );
4139 MSI_RecordSetStringW( uirow, 1, identifier );
4140 MSI_RecordSetStringW( uirow, 2, deformated_section );
4141 MSI_RecordSetStringW( uirow, 3, deformated_key );
4142 MSI_RecordSetStringW( uirow, 4, deformated_value );
4143 ui_actiondata( package, szRemoveIniValues, uirow );
4144 msiobj_release( &uirow->hdr );
4146 msi_free( deformated_key );
4147 msi_free( deformated_value );
4148 msi_free( deformated_section );
4149 return ERROR_SUCCESS;
4152 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4154 MSIPACKAGE *package = param;
4155 LPCWSTR component, section, key, value, identifier;
4156 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4157 MSICOMPONENT *comp;
4158 MSIRECORD *uirow;
4159 INT action;
4161 component = MSI_RecordGetString( row, 8 );
4162 comp = get_loaded_component( package, component );
4163 if (!comp)
4164 return ERROR_SUCCESS;
4166 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4168 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4169 comp->Action = comp->Installed;
4170 return ERROR_SUCCESS;
4172 comp->Action = INSTALLSTATE_LOCAL;
4174 identifier = MSI_RecordGetString( row, 1 );
4175 section = MSI_RecordGetString( row, 4 );
4176 key = MSI_RecordGetString( row, 5 );
4177 value = MSI_RecordGetString( row, 6 );
4178 action = MSI_RecordGetInteger( row, 7 );
4180 deformat_string( package, section, &deformated_section );
4181 deformat_string( package, key, &deformated_key );
4182 deformat_string( package, value, &deformated_value );
4184 if (action == msidbIniFileActionRemoveLine)
4186 filename = get_ini_file_name( package, row );
4188 TRACE("Removing key %s from section %s in %s\n",
4189 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4191 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4193 WARN("Unable to remove key %u\n", GetLastError());
4195 msi_free( filename );
4197 else
4198 FIXME("Unsupported action %d\n", action);
4200 uirow = MSI_CreateRecord( 4 );
4201 MSI_RecordSetStringW( uirow, 1, identifier );
4202 MSI_RecordSetStringW( uirow, 2, deformated_section );
4203 MSI_RecordSetStringW( uirow, 3, deformated_key );
4204 MSI_RecordSetStringW( uirow, 4, deformated_value );
4205 ui_actiondata( package, szRemoveIniValues, uirow );
4206 msiobj_release( &uirow->hdr );
4208 msi_free( deformated_key );
4209 msi_free( deformated_value );
4210 msi_free( deformated_section );
4211 return ERROR_SUCCESS;
4214 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4216 UINT rc;
4217 MSIQUERY *view;
4218 static const WCHAR query[] =
4219 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4220 '`','I','n','i','F','i','l','e','`',0};
4221 static const WCHAR remove_query[] =
4222 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4223 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4225 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4226 if (rc == ERROR_SUCCESS)
4228 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4229 msiobj_release( &view->hdr );
4230 if (rc != ERROR_SUCCESS)
4231 return rc;
4234 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4235 if (rc == ERROR_SUCCESS)
4237 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4238 msiobj_release( &view->hdr );
4239 if (rc != ERROR_SUCCESS)
4240 return rc;
4243 return ERROR_SUCCESS;
4246 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4248 MSIPACKAGE *package = param;
4249 LPCWSTR filename;
4250 LPWSTR FullName;
4251 MSIFILE *file;
4252 DWORD len;
4253 static const WCHAR ExeStr[] =
4254 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
4255 static const WCHAR close[] = {'\"',0};
4256 STARTUPINFOW si;
4257 PROCESS_INFORMATION info;
4258 BOOL brc;
4259 MSIRECORD *uirow;
4260 LPWSTR uipath, p;
4262 memset(&si,0,sizeof(STARTUPINFOW));
4264 filename = MSI_RecordGetString(row,1);
4265 file = get_loaded_file( package, filename );
4267 if (!file)
4269 ERR("Unable to find file id %s\n",debugstr_w(filename));
4270 return ERROR_SUCCESS;
4273 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
4275 FullName = msi_alloc(len*sizeof(WCHAR));
4276 strcpyW(FullName,ExeStr);
4277 strcatW( FullName, file->TargetPath );
4278 strcatW(FullName,close);
4280 TRACE("Registering %s\n",debugstr_w(FullName));
4281 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
4282 &si, &info);
4284 if (brc)
4286 CloseHandle(info.hThread);
4287 msi_dialog_check_messages(info.hProcess);
4288 CloseHandle(info.hProcess);
4291 uirow = MSI_CreateRecord( 2 );
4292 MSI_RecordSetStringW( uirow, 1, filename );
4293 uipath = strdupW( file->TargetPath );
4294 if ((p = strrchrW( uipath,'\\' ))) *p = 0;
4295 MSI_RecordSetStringW( uirow, 2, uipath );
4296 ui_actiondata( package, szSelfRegModules, uirow );
4297 msiobj_release( &uirow->hdr );
4299 msi_free( FullName );
4300 msi_free( uipath );
4301 return ERROR_SUCCESS;
4304 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4306 UINT rc;
4307 MSIQUERY * view;
4308 static const WCHAR ExecSeqQuery[] =
4309 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4310 '`','S','e','l','f','R','e','g','`',0};
4312 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4313 if (rc != ERROR_SUCCESS)
4315 TRACE("no SelfReg table\n");
4316 return ERROR_SUCCESS;
4319 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4320 msiobj_release(&view->hdr);
4322 return ERROR_SUCCESS;
4325 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4327 static const WCHAR regsvr32[] =
4328 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
4329 static const WCHAR close[] = {'\"',0};
4330 MSIPACKAGE *package = param;
4331 LPCWSTR filename;
4332 LPWSTR cmdline;
4333 MSIFILE *file;
4334 DWORD len;
4335 STARTUPINFOW si;
4336 PROCESS_INFORMATION pi;
4337 BOOL ret;
4338 MSIRECORD *uirow;
4339 LPWSTR uipath, p;
4341 memset( &si, 0, sizeof(STARTUPINFOW) );
4343 filename = MSI_RecordGetString( row, 1 );
4344 file = get_loaded_file( package, filename );
4346 if (!file)
4348 ERR("Unable to find file id %s\n", debugstr_w(filename));
4349 return ERROR_SUCCESS;
4352 len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
4354 cmdline = msi_alloc( len * sizeof(WCHAR) );
4355 strcpyW( cmdline, regsvr32 );
4356 strcatW( cmdline, file->TargetPath );
4357 strcatW( cmdline, close );
4359 TRACE("Unregistering %s\n", debugstr_w(cmdline));
4361 ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
4362 if (ret)
4364 CloseHandle( pi.hThread );
4365 msi_dialog_check_messages( pi.hProcess );
4366 CloseHandle( pi.hProcess );
4369 uirow = MSI_CreateRecord( 2 );
4370 MSI_RecordSetStringW( uirow, 1, filename );
4371 uipath = strdupW( file->TargetPath );
4372 if ((p = strrchrW( uipath,'\\' ))) *p = 0;
4373 MSI_RecordSetStringW( uirow, 2, uipath );
4374 ui_actiondata( package, szSelfUnregModules, uirow );
4375 msiobj_release( &uirow->hdr );
4377 msi_free( cmdline );
4378 msi_free( uipath );
4379 return ERROR_SUCCESS;
4382 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4384 UINT rc;
4385 MSIQUERY *view;
4386 static const WCHAR query[] =
4387 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4388 '`','S','e','l','f','R','e','g','`',0};
4390 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4391 if (rc != ERROR_SUCCESS)
4393 TRACE("no SelfReg table\n");
4394 return ERROR_SUCCESS;
4397 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4398 msiobj_release( &view->hdr );
4400 return ERROR_SUCCESS;
4403 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4405 MSIFEATURE *feature;
4406 UINT rc;
4407 HKEY hkey = NULL, userdata = NULL;
4409 if (!msi_check_publish(package))
4410 return ERROR_SUCCESS;
4412 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4413 &hkey, TRUE);
4414 if (rc != ERROR_SUCCESS)
4415 goto end;
4417 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4418 &userdata, TRUE);
4419 if (rc != ERROR_SUCCESS)
4420 goto end;
4422 /* here the guids are base 85 encoded */
4423 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4425 ComponentList *cl;
4426 LPWSTR data = NULL;
4427 GUID clsid;
4428 INT size;
4429 BOOL absent = FALSE;
4430 MSIRECORD *uirow;
4432 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4433 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4434 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4436 size = 1;
4437 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4439 size += 21;
4441 if (feature->Feature_Parent)
4442 size += strlenW( feature->Feature_Parent )+2;
4444 data = msi_alloc(size * sizeof(WCHAR));
4446 data[0] = 0;
4447 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4449 MSICOMPONENT* component = cl->component;
4450 WCHAR buf[21];
4452 buf[0] = 0;
4453 if (component->ComponentId)
4455 TRACE("From %s\n",debugstr_w(component->ComponentId));
4456 CLSIDFromString(component->ComponentId, &clsid);
4457 encode_base85_guid(&clsid,buf);
4458 TRACE("to %s\n",debugstr_w(buf));
4459 strcatW(data,buf);
4463 if (feature->Feature_Parent)
4465 static const WCHAR sep[] = {'\2',0};
4466 strcatW(data,sep);
4467 strcatW(data,feature->Feature_Parent);
4470 msi_reg_set_val_str( userdata, feature->Feature, data );
4471 msi_free(data);
4473 size = 0;
4474 if (feature->Feature_Parent)
4475 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4476 if (!absent)
4478 size += sizeof(WCHAR);
4479 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4480 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4482 else
4484 size += 2*sizeof(WCHAR);
4485 data = msi_alloc(size);
4486 data[0] = 0x6;
4487 data[1] = 0;
4488 if (feature->Feature_Parent)
4489 strcpyW( &data[1], feature->Feature_Parent );
4490 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4491 (LPBYTE)data,size);
4492 msi_free(data);
4495 /* the UI chunk */
4496 uirow = MSI_CreateRecord( 1 );
4497 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4498 ui_actiondata( package, szPublishFeatures, uirow);
4499 msiobj_release( &uirow->hdr );
4500 /* FIXME: call ui_progress? */
4503 end:
4504 RegCloseKey(hkey);
4505 RegCloseKey(userdata);
4506 return rc;
4509 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4511 UINT r;
4512 HKEY hkey;
4513 MSIRECORD *uirow;
4515 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4517 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4518 &hkey, FALSE);
4519 if (r == ERROR_SUCCESS)
4521 RegDeleteValueW(hkey, feature->Feature);
4522 RegCloseKey(hkey);
4525 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4526 &hkey, FALSE);
4527 if (r == ERROR_SUCCESS)
4529 RegDeleteValueW(hkey, feature->Feature);
4530 RegCloseKey(hkey);
4533 uirow = MSI_CreateRecord( 1 );
4534 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4535 ui_actiondata( package, szUnpublishFeatures, uirow );
4536 msiobj_release( &uirow->hdr );
4538 return ERROR_SUCCESS;
4541 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4543 MSIFEATURE *feature;
4545 if (!msi_check_unpublish(package))
4546 return ERROR_SUCCESS;
4548 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4550 msi_unpublish_feature(package, feature);
4553 return ERROR_SUCCESS;
4556 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4558 SYSTEMTIME systime;
4559 DWORD size, langid;
4560 WCHAR date[9], *val, *buffer;
4561 const WCHAR *prop, *key;
4563 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4564 static const WCHAR szWindowsInstaller[] =
4565 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4566 static const WCHAR modpath_fmt[] =
4567 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4568 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4569 static const WCHAR szModifyPath[] =
4570 {'M','o','d','i','f','y','P','a','t','h',0};
4571 static const WCHAR szUninstallString[] =
4572 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4573 static const WCHAR szEstimatedSize[] =
4574 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4575 static const WCHAR szProductLanguage[] =
4576 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4577 static const WCHAR szProductVersion[] =
4578 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4579 static const WCHAR szDisplayVersion[] =
4580 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4581 static const WCHAR szInstallSource[] =
4582 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4583 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4584 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4585 static const WCHAR szAuthorizedCDFPrefix[] =
4586 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4587 static const WCHAR szARPCONTACT[] =
4588 {'A','R','P','C','O','N','T','A','C','T',0};
4589 static const WCHAR szContact[] =
4590 {'C','o','n','t','a','c','t',0};
4591 static const WCHAR szARPCOMMENTS[] =
4592 {'A','R','P','C','O','M','M','E','N','T','S',0};
4593 static const WCHAR szComments[] =
4594 {'C','o','m','m','e','n','t','s',0};
4595 static const WCHAR szProductName[] =
4596 {'P','r','o','d','u','c','t','N','a','m','e',0};
4597 static const WCHAR szDisplayName[] =
4598 {'D','i','s','p','l','a','y','N','a','m','e',0};
4599 static const WCHAR szARPHELPLINK[] =
4600 {'A','R','P','H','E','L','P','L','I','N','K',0};
4601 static const WCHAR szHelpLink[] =
4602 {'H','e','l','p','L','i','n','k',0};
4603 static const WCHAR szARPHELPTELEPHONE[] =
4604 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4605 static const WCHAR szHelpTelephone[] =
4606 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4607 static const WCHAR szARPINSTALLLOCATION[] =
4608 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4609 static const WCHAR szInstallLocation[] =
4610 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4611 static const WCHAR szManufacturer[] =
4612 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4613 static const WCHAR szPublisher[] =
4614 {'P','u','b','l','i','s','h','e','r',0};
4615 static const WCHAR szARPREADME[] =
4616 {'A','R','P','R','E','A','D','M','E',0};
4617 static const WCHAR szReadme[] =
4618 {'R','e','a','d','M','e',0};
4619 static const WCHAR szARPSIZE[] =
4620 {'A','R','P','S','I','Z','E',0};
4621 static const WCHAR szSize[] =
4622 {'S','i','z','e',0};
4623 static const WCHAR szARPURLINFOABOUT[] =
4624 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4625 static const WCHAR szURLInfoAbout[] =
4626 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4627 static const WCHAR szARPURLUPDATEINFO[] =
4628 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4629 static const WCHAR szURLUpdateInfo[] =
4630 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4632 static const WCHAR *propval[] = {
4633 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4634 szARPCONTACT, szContact,
4635 szARPCOMMENTS, szComments,
4636 szProductName, szDisplayName,
4637 szARPHELPLINK, szHelpLink,
4638 szARPHELPTELEPHONE, szHelpTelephone,
4639 szARPINSTALLLOCATION, szInstallLocation,
4640 cszSourceDir, szInstallSource,
4641 szManufacturer, szPublisher,
4642 szARPREADME, szReadme,
4643 szARPSIZE, szSize,
4644 szARPURLINFOABOUT, szURLInfoAbout,
4645 szARPURLUPDATEINFO, szURLUpdateInfo,
4646 NULL
4648 const WCHAR **p = propval;
4650 while (*p)
4652 prop = *p++;
4653 key = *p++;
4654 val = msi_dup_property(package->db, prop);
4655 msi_reg_set_val_str(hkey, key, val);
4656 msi_free(val);
4659 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4661 size = deformat_string(package, modpath_fmt, &buffer);
4662 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4663 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4664 msi_free(buffer);
4666 /* FIXME: Write real Estimated Size when we have it */
4667 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4669 GetLocalTime(&systime);
4670 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4671 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4673 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4674 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4676 buffer = msi_dup_property(package->db, szProductVersion);
4677 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4678 if (buffer)
4680 DWORD verdword = msi_version_str_to_dword(buffer);
4682 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4683 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4684 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4685 msi_free(buffer);
4688 return ERROR_SUCCESS;
4691 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4693 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4694 MSIRECORD *uirow;
4695 LPWSTR upgrade_code;
4696 HKEY hkey, props;
4697 HKEY upgrade;
4698 UINT rc;
4700 static const WCHAR szUpgradeCode[] = {
4701 'U','p','g','r','a','d','e','C','o','d','e',0};
4703 /* FIXME: also need to publish if the product is in advertise mode */
4704 if (!msi_check_publish(package))
4705 return ERROR_SUCCESS;
4707 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4708 if (rc != ERROR_SUCCESS)
4709 return rc;
4711 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4712 NULL, &props, TRUE);
4713 if (rc != ERROR_SUCCESS)
4714 goto done;
4716 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4717 msi_free( package->db->localfile );
4718 package->db->localfile = NULL;
4720 rc = msi_publish_install_properties(package, hkey);
4721 if (rc != ERROR_SUCCESS)
4722 goto done;
4724 rc = msi_publish_install_properties(package, props);
4725 if (rc != ERROR_SUCCESS)
4726 goto done;
4728 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4729 if (upgrade_code)
4731 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4732 squash_guid(package->ProductCode, squashed_pc);
4733 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4734 RegCloseKey(upgrade);
4735 msi_free(upgrade_code);
4738 done:
4739 uirow = MSI_CreateRecord( 1 );
4740 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4741 ui_actiondata( package, szRegisterProduct, uirow );
4742 msiobj_release( &uirow->hdr );
4744 RegCloseKey(hkey);
4745 return ERROR_SUCCESS;
4748 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4750 return execute_script(package,INSTALL_SCRIPT);
4753 static UINT msi_unpublish_product(MSIPACKAGE *package)
4755 LPWSTR upgrade;
4756 LPWSTR remove = NULL;
4757 LPWSTR *features = NULL;
4758 BOOL full_uninstall = TRUE;
4759 MSIFEATURE *feature;
4760 MSIPATCHINFO *patch;
4762 static const WCHAR szUpgradeCode[] =
4763 {'U','p','g','r','a','d','e','C','o','d','e',0};
4765 remove = msi_dup_property(package->db, szRemove);
4766 if (!remove)
4767 return ERROR_SUCCESS;
4769 features = msi_split_string(remove, ',');
4770 if (!features)
4772 msi_free(remove);
4773 ERR("REMOVE feature list is empty!\n");
4774 return ERROR_FUNCTION_FAILED;
4777 if (!lstrcmpW(features[0], szAll))
4778 full_uninstall = TRUE;
4779 else
4781 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4783 if (feature->Action != INSTALLSTATE_ABSENT)
4784 full_uninstall = FALSE;
4788 if (!full_uninstall)
4789 goto done;
4791 MSIREG_DeleteProductKey(package->ProductCode);
4792 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4793 MSIREG_DeleteUninstallKey(package->ProductCode);
4795 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4797 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4798 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4800 else
4802 MSIREG_DeleteUserProductKey(package->ProductCode);
4803 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4806 upgrade = msi_dup_property(package->db, szUpgradeCode);
4807 if (upgrade)
4809 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4810 msi_free(upgrade);
4813 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4815 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4818 done:
4819 msi_free(remove);
4820 msi_free(features);
4821 return ERROR_SUCCESS;
4824 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4826 UINT rc;
4828 rc = msi_unpublish_product(package);
4829 if (rc != ERROR_SUCCESS)
4830 return rc;
4832 /* turn off scheduling */
4833 package->script->CurrentlyScripting= FALSE;
4835 /* first do the same as an InstallExecute */
4836 rc = ACTION_InstallExecute(package);
4837 if (rc != ERROR_SUCCESS)
4838 return rc;
4840 /* then handle Commit Actions */
4841 rc = execute_script(package,COMMIT_SCRIPT);
4843 return rc;
4846 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4848 static const WCHAR RunOnce[] = {
4849 'S','o','f','t','w','a','r','e','\\',
4850 'M','i','c','r','o','s','o','f','t','\\',
4851 'W','i','n','d','o','w','s','\\',
4852 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4853 'R','u','n','O','n','c','e',0};
4854 static const WCHAR InstallRunOnce[] = {
4855 'S','o','f','t','w','a','r','e','\\',
4856 'M','i','c','r','o','s','o','f','t','\\',
4857 'W','i','n','d','o','w','s','\\',
4858 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4859 'I','n','s','t','a','l','l','e','r','\\',
4860 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4862 static const WCHAR msiexec_fmt[] = {
4863 '%','s',
4864 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4865 '\"','%','s','\"',0};
4866 static const WCHAR install_fmt[] = {
4867 '/','I',' ','\"','%','s','\"',' ',
4868 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4869 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4870 WCHAR buffer[256], sysdir[MAX_PATH];
4871 HKEY hkey;
4872 WCHAR squished_pc[100];
4874 squash_guid(package->ProductCode,squished_pc);
4876 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4877 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4878 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4879 squished_pc);
4881 msi_reg_set_val_str( hkey, squished_pc, buffer );
4882 RegCloseKey(hkey);
4884 TRACE("Reboot command %s\n",debugstr_w(buffer));
4886 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4887 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4889 msi_reg_set_val_str( hkey, squished_pc, buffer );
4890 RegCloseKey(hkey);
4892 return ERROR_INSTALL_SUSPEND;
4895 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4897 DWORD attrib;
4898 UINT rc;
4901 * We are currently doing what should be done here in the top level Install
4902 * however for Administrative and uninstalls this step will be needed
4904 if (!package->PackagePath)
4905 return ERROR_SUCCESS;
4907 msi_set_sourcedir_props(package, TRUE);
4909 attrib = GetFileAttributesW(package->db->path);
4910 if (attrib == INVALID_FILE_ATTRIBUTES)
4912 LPWSTR prompt;
4913 LPWSTR msg;
4914 DWORD size = 0;
4916 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4917 package->Context, MSICODE_PRODUCT,
4918 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4919 if (rc == ERROR_MORE_DATA)
4921 prompt = msi_alloc(size * sizeof(WCHAR));
4922 MsiSourceListGetInfoW(package->ProductCode, NULL,
4923 package->Context, MSICODE_PRODUCT,
4924 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4926 else
4927 prompt = strdupW(package->db->path);
4929 msg = generate_error_string(package,1302,1,prompt);
4930 while(attrib == INVALID_FILE_ATTRIBUTES)
4932 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4933 if (rc == IDCANCEL)
4935 rc = ERROR_INSTALL_USEREXIT;
4936 break;
4938 attrib = GetFileAttributesW(package->db->path);
4940 msi_free(prompt);
4941 rc = ERROR_SUCCESS;
4943 else
4944 return ERROR_SUCCESS;
4946 return rc;
4949 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4951 HKEY hkey = 0;
4952 LPWSTR buffer, productid = NULL;
4953 UINT i, rc = ERROR_SUCCESS;
4954 MSIRECORD *uirow;
4956 static const WCHAR szPropKeys[][80] =
4958 {'P','r','o','d','u','c','t','I','D',0},
4959 {'U','S','E','R','N','A','M','E',0},
4960 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4961 {0},
4964 static const WCHAR szRegKeys[][80] =
4966 {'P','r','o','d','u','c','t','I','D',0},
4967 {'R','e','g','O','w','n','e','r',0},
4968 {'R','e','g','C','o','m','p','a','n','y',0},
4969 {0},
4972 if (msi_check_unpublish(package))
4974 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4975 goto end;
4978 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
4979 if (!productid)
4980 goto end;
4982 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4983 NULL, &hkey, TRUE);
4984 if (rc != ERROR_SUCCESS)
4985 goto end;
4987 for( i = 0; szPropKeys[i][0]; i++ )
4989 buffer = msi_dup_property( package->db, szPropKeys[i] );
4990 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4991 msi_free( buffer );
4994 end:
4995 uirow = MSI_CreateRecord( 1 );
4996 MSI_RecordSetStringW( uirow, 1, productid );
4997 ui_actiondata( package, szRegisterUser, uirow );
4998 msiobj_release( &uirow->hdr );
5000 msi_free(productid);
5001 RegCloseKey(hkey);
5002 return rc;
5006 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5008 UINT rc;
5010 package->script->InWhatSequence |= SEQUENCE_EXEC;
5011 rc = ACTION_ProcessExecSequence(package,FALSE);
5012 return rc;
5016 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5018 MSIPACKAGE *package = param;
5019 LPCWSTR compgroupid, component, feature, qualifier, text;
5020 LPWSTR advertise = NULL, output = NULL;
5021 HKEY hkey = NULL;
5022 UINT rc;
5023 MSICOMPONENT *comp;
5024 MSIFEATURE *feat;
5025 DWORD sz;
5026 MSIRECORD *uirow;
5028 feature = MSI_RecordGetString(rec, 5);
5029 feat = get_loaded_feature(package, feature);
5030 if (!feat)
5031 return ERROR_SUCCESS;
5033 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
5034 feat->ActionRequest != INSTALLSTATE_SOURCE &&
5035 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
5037 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
5038 feat->Action = feat->Installed;
5039 return ERROR_SUCCESS;
5042 component = MSI_RecordGetString(rec, 3);
5043 comp = get_loaded_component(package, component);
5044 if (!comp)
5045 return ERROR_SUCCESS;
5047 compgroupid = MSI_RecordGetString(rec,1);
5048 qualifier = MSI_RecordGetString(rec,2);
5050 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5051 if (rc != ERROR_SUCCESS)
5052 goto end;
5054 text = MSI_RecordGetString(rec,4);
5055 advertise = create_component_advertise_string(package, comp, feature);
5057 sz = strlenW(advertise);
5059 if (text)
5060 sz += lstrlenW(text);
5062 sz+=3;
5063 sz *= sizeof(WCHAR);
5065 output = msi_alloc_zero(sz);
5066 strcpyW(output,advertise);
5067 msi_free(advertise);
5069 if (text)
5070 strcatW(output,text);
5072 msi_reg_set_val_multi_str( hkey, qualifier, output );
5074 end:
5075 RegCloseKey(hkey);
5076 msi_free(output);
5078 /* the UI chunk */
5079 uirow = MSI_CreateRecord( 2 );
5080 MSI_RecordSetStringW( uirow, 1, compgroupid );
5081 MSI_RecordSetStringW( uirow, 2, qualifier);
5082 ui_actiondata( package, szPublishComponents, uirow);
5083 msiobj_release( &uirow->hdr );
5084 /* FIXME: call ui_progress? */
5086 return rc;
5090 * At present I am ignorning the advertised components part of this and only
5091 * focusing on the qualified component sets
5093 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5095 UINT rc;
5096 MSIQUERY * view;
5097 static const WCHAR ExecSeqQuery[] =
5098 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5099 '`','P','u','b','l','i','s','h',
5100 'C','o','m','p','o','n','e','n','t','`',0};
5102 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5103 if (rc != ERROR_SUCCESS)
5104 return ERROR_SUCCESS;
5106 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5107 msiobj_release(&view->hdr);
5109 return rc;
5112 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5114 static const WCHAR szInstallerComponents[] = {
5115 'S','o','f','t','w','a','r','e','\\',
5116 'M','i','c','r','o','s','o','f','t','\\',
5117 'I','n','s','t','a','l','l','e','r','\\',
5118 'C','o','m','p','o','n','e','n','t','s','\\',0};
5120 MSIPACKAGE *package = param;
5121 LPCWSTR compgroupid, component, feature, qualifier;
5122 MSICOMPONENT *comp;
5123 MSIFEATURE *feat;
5124 MSIRECORD *uirow;
5125 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5126 LONG res;
5128 feature = MSI_RecordGetString( rec, 5 );
5129 feat = get_loaded_feature( package, feature );
5130 if (!feat)
5131 return ERROR_SUCCESS;
5133 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5135 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5136 feat->Action = feat->Installed;
5137 return ERROR_SUCCESS;
5140 component = MSI_RecordGetString( rec, 3 );
5141 comp = get_loaded_component( package, component );
5142 if (!comp)
5143 return ERROR_SUCCESS;
5145 compgroupid = MSI_RecordGetString( rec, 1 );
5146 qualifier = MSI_RecordGetString( rec, 2 );
5148 squash_guid( compgroupid, squashed );
5149 strcpyW( keypath, szInstallerComponents );
5150 strcatW( keypath, squashed );
5152 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5153 if (res != ERROR_SUCCESS)
5155 WARN("Unable to delete component key %d\n", res);
5158 uirow = MSI_CreateRecord( 2 );
5159 MSI_RecordSetStringW( uirow, 1, compgroupid );
5160 MSI_RecordSetStringW( uirow, 2, qualifier );
5161 ui_actiondata( package, szUnpublishComponents, uirow );
5162 msiobj_release( &uirow->hdr );
5164 return ERROR_SUCCESS;
5167 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5169 UINT rc;
5170 MSIQUERY *view;
5171 static const WCHAR query[] =
5172 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5173 '`','P','u','b','l','i','s','h',
5174 'C','o','m','p','o','n','e','n','t','`',0};
5176 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5177 if (rc != ERROR_SUCCESS)
5178 return ERROR_SUCCESS;
5180 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5181 msiobj_release( &view->hdr );
5183 return rc;
5186 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5188 MSIPACKAGE *package = param;
5189 MSIRECORD *row;
5190 MSIFILE *file;
5191 SC_HANDLE hscm, service = NULL;
5192 LPCWSTR comp, depends, pass;
5193 LPWSTR name = NULL, disp = NULL;
5194 LPCWSTR load_order, serv_name, key;
5195 DWORD serv_type, start_type;
5196 DWORD err_control;
5198 static const WCHAR query[] =
5199 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5200 '`','C','o','m','p','o','n','e','n','t','`',' ',
5201 'W','H','E','R','E',' ',
5202 '`','C','o','m','p','o','n','e','n','t','`',' ',
5203 '=','\'','%','s','\'',0};
5205 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5206 if (!hscm)
5208 ERR("Failed to open the SC Manager!\n");
5209 goto done;
5212 start_type = MSI_RecordGetInteger(rec, 5);
5213 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5214 goto done;
5216 depends = MSI_RecordGetString(rec, 8);
5217 if (depends && *depends)
5218 FIXME("Dependency list unhandled!\n");
5220 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5221 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5222 serv_type = MSI_RecordGetInteger(rec, 4);
5223 err_control = MSI_RecordGetInteger(rec, 6);
5224 load_order = MSI_RecordGetString(rec, 7);
5225 serv_name = MSI_RecordGetString(rec, 9);
5226 pass = MSI_RecordGetString(rec, 10);
5227 comp = MSI_RecordGetString(rec, 12);
5229 /* fetch the service path */
5230 row = MSI_QueryGetRecord(package->db, query, comp);
5231 if (!row)
5233 ERR("Control query failed!\n");
5234 goto done;
5237 key = MSI_RecordGetString(row, 6);
5239 file = get_loaded_file(package, key);
5240 msiobj_release(&row->hdr);
5241 if (!file)
5243 ERR("Failed to load the service file\n");
5244 goto done;
5247 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5248 start_type, err_control, file->TargetPath,
5249 load_order, NULL, NULL, serv_name, pass);
5250 if (!service)
5252 if (GetLastError() != ERROR_SERVICE_EXISTS)
5253 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5256 done:
5257 CloseServiceHandle(service);
5258 CloseServiceHandle(hscm);
5259 msi_free(name);
5260 msi_free(disp);
5262 return ERROR_SUCCESS;
5265 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5267 UINT rc;
5268 MSIQUERY * view;
5269 static const WCHAR ExecSeqQuery[] =
5270 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5271 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5273 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5274 if (rc != ERROR_SUCCESS)
5275 return ERROR_SUCCESS;
5277 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5278 msiobj_release(&view->hdr);
5280 return rc;
5283 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5284 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5286 LPCWSTR *vector, *temp_vector;
5287 LPWSTR p, q;
5288 DWORD sep_len;
5290 static const WCHAR separator[] = {'[','~',']',0};
5292 *numargs = 0;
5293 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5295 if (!args)
5296 return NULL;
5298 vector = msi_alloc(sizeof(LPWSTR));
5299 if (!vector)
5300 return NULL;
5302 p = args;
5305 (*numargs)++;
5306 vector[*numargs - 1] = p;
5308 if ((q = strstrW(p, separator)))
5310 *q = '\0';
5312 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5313 if (!temp_vector)
5315 msi_free(vector);
5316 return NULL;
5318 vector = temp_vector;
5320 p = q + sep_len;
5322 } while (q);
5324 return vector;
5327 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5329 MSIPACKAGE *package = param;
5330 MSICOMPONENT *comp;
5331 MSIRECORD *uirow;
5332 SC_HANDLE scm = NULL, service = NULL;
5333 LPCWSTR component, *vector = NULL;
5334 LPWSTR name, args, display_name = NULL;
5335 DWORD event, numargs, len;
5336 UINT r = ERROR_FUNCTION_FAILED;
5338 component = MSI_RecordGetString(rec, 6);
5339 comp = get_loaded_component(package, component);
5340 if (!comp)
5341 return ERROR_SUCCESS;
5343 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5345 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5346 comp->Action = comp->Installed;
5347 return ERROR_SUCCESS;
5349 comp->Action = INSTALLSTATE_LOCAL;
5351 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5352 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5353 event = MSI_RecordGetInteger(rec, 3);
5355 if (!(event & msidbServiceControlEventStart))
5357 r = ERROR_SUCCESS;
5358 goto done;
5361 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5362 if (!scm)
5364 ERR("Failed to open the service control manager\n");
5365 goto done;
5368 len = 0;
5369 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5370 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5372 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5373 GetServiceDisplayNameW( scm, name, display_name, &len );
5376 service = OpenServiceW(scm, name, SERVICE_START);
5377 if (!service)
5379 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5380 goto done;
5383 vector = msi_service_args_to_vector(args, &numargs);
5385 if (!StartServiceW(service, numargs, vector) &&
5386 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5388 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5389 goto done;
5392 r = ERROR_SUCCESS;
5394 done:
5395 uirow = MSI_CreateRecord( 2 );
5396 MSI_RecordSetStringW( uirow, 1, display_name );
5397 MSI_RecordSetStringW( uirow, 2, name );
5398 ui_actiondata( package, szStartServices, uirow );
5399 msiobj_release( &uirow->hdr );
5401 CloseServiceHandle(service);
5402 CloseServiceHandle(scm);
5404 msi_free(name);
5405 msi_free(args);
5406 msi_free(vector);
5407 msi_free(display_name);
5408 return r;
5411 static UINT ACTION_StartServices( MSIPACKAGE *package )
5413 UINT rc;
5414 MSIQUERY *view;
5416 static const WCHAR query[] = {
5417 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5418 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5420 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5421 if (rc != ERROR_SUCCESS)
5422 return ERROR_SUCCESS;
5424 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5425 msiobj_release(&view->hdr);
5427 return rc;
5430 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5432 DWORD i, needed, count;
5433 ENUM_SERVICE_STATUSW *dependencies;
5434 SERVICE_STATUS ss;
5435 SC_HANDLE depserv;
5437 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5438 0, &needed, &count))
5439 return TRUE;
5441 if (GetLastError() != ERROR_MORE_DATA)
5442 return FALSE;
5444 dependencies = msi_alloc(needed);
5445 if (!dependencies)
5446 return FALSE;
5448 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5449 needed, &needed, &count))
5450 goto error;
5452 for (i = 0; i < count; i++)
5454 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5455 SERVICE_STOP | SERVICE_QUERY_STATUS);
5456 if (!depserv)
5457 goto error;
5459 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5460 goto error;
5463 return TRUE;
5465 error:
5466 msi_free(dependencies);
5467 return FALSE;
5470 static UINT stop_service( LPCWSTR name )
5472 SC_HANDLE scm = NULL, service = NULL;
5473 SERVICE_STATUS status;
5474 SERVICE_STATUS_PROCESS ssp;
5475 DWORD needed;
5477 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5478 if (!scm)
5480 WARN("Failed to open the SCM: %d\n", GetLastError());
5481 goto done;
5484 service = OpenServiceW(scm, name,
5485 SERVICE_STOP |
5486 SERVICE_QUERY_STATUS |
5487 SERVICE_ENUMERATE_DEPENDENTS);
5488 if (!service)
5490 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5491 goto done;
5494 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5495 sizeof(SERVICE_STATUS_PROCESS), &needed))
5497 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5498 goto done;
5501 if (ssp.dwCurrentState == SERVICE_STOPPED)
5502 goto done;
5504 stop_service_dependents(scm, service);
5506 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5507 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5509 done:
5510 CloseServiceHandle(service);
5511 CloseServiceHandle(scm);
5513 return ERROR_SUCCESS;
5516 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5518 MSIPACKAGE *package = param;
5519 MSICOMPONENT *comp;
5520 MSIRECORD *uirow;
5521 LPCWSTR component;
5522 LPWSTR name = NULL, display_name = NULL;
5523 DWORD event, len;
5524 SC_HANDLE scm;
5526 event = MSI_RecordGetInteger( rec, 3 );
5527 if (!(event & msidbServiceControlEventStop))
5528 return ERROR_SUCCESS;
5530 component = MSI_RecordGetString( rec, 6 );
5531 comp = get_loaded_component( package, component );
5532 if (!comp)
5533 return ERROR_SUCCESS;
5535 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5537 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5538 comp->Action = comp->Installed;
5539 return ERROR_SUCCESS;
5541 comp->Action = INSTALLSTATE_ABSENT;
5543 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5544 if (!scm)
5546 ERR("Failed to open the service control manager\n");
5547 goto done;
5550 len = 0;
5551 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5552 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5554 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5555 GetServiceDisplayNameW( scm, name, display_name, &len );
5557 CloseServiceHandle( scm );
5559 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5560 stop_service( name );
5562 done:
5563 uirow = MSI_CreateRecord( 2 );
5564 MSI_RecordSetStringW( uirow, 1, display_name );
5565 MSI_RecordSetStringW( uirow, 2, name );
5566 ui_actiondata( package, szStopServices, uirow );
5567 msiobj_release( &uirow->hdr );
5569 msi_free( name );
5570 msi_free( display_name );
5571 return ERROR_SUCCESS;
5574 static UINT ACTION_StopServices( MSIPACKAGE *package )
5576 UINT rc;
5577 MSIQUERY *view;
5579 static const WCHAR query[] = {
5580 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5581 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5583 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5584 if (rc != ERROR_SUCCESS)
5585 return ERROR_SUCCESS;
5587 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5588 msiobj_release(&view->hdr);
5590 return rc;
5593 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5595 MSIPACKAGE *package = param;
5596 MSICOMPONENT *comp;
5597 MSIRECORD *uirow;
5598 LPCWSTR component;
5599 LPWSTR name = NULL, display_name = NULL;
5600 DWORD event, len;
5601 SC_HANDLE scm = NULL, service = NULL;
5603 event = MSI_RecordGetInteger( rec, 3 );
5604 if (!(event & msidbServiceControlEventDelete))
5605 return ERROR_SUCCESS;
5607 component = MSI_RecordGetString(rec, 6);
5608 comp = get_loaded_component(package, component);
5609 if (!comp)
5610 return ERROR_SUCCESS;
5612 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5614 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5615 comp->Action = comp->Installed;
5616 return ERROR_SUCCESS;
5618 comp->Action = INSTALLSTATE_ABSENT;
5620 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5621 stop_service( name );
5623 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5624 if (!scm)
5626 WARN("Failed to open the SCM: %d\n", GetLastError());
5627 goto done;
5630 len = 0;
5631 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5632 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5634 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5635 GetServiceDisplayNameW( scm, name, display_name, &len );
5638 service = OpenServiceW( scm, name, DELETE );
5639 if (!service)
5641 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5642 goto done;
5645 if (!DeleteService( service ))
5646 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5648 done:
5649 uirow = MSI_CreateRecord( 2 );
5650 MSI_RecordSetStringW( uirow, 1, display_name );
5651 MSI_RecordSetStringW( uirow, 2, name );
5652 ui_actiondata( package, szDeleteServices, uirow );
5653 msiobj_release( &uirow->hdr );
5655 CloseServiceHandle( service );
5656 CloseServiceHandle( scm );
5657 msi_free( name );
5658 msi_free( display_name );
5660 return ERROR_SUCCESS;
5663 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5665 UINT rc;
5666 MSIQUERY *view;
5668 static const WCHAR query[] = {
5669 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5670 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5672 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5673 if (rc != ERROR_SUCCESS)
5674 return ERROR_SUCCESS;
5676 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5677 msiobj_release( &view->hdr );
5679 return rc;
5682 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5684 MSIFILE *file;
5686 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5688 if (!lstrcmpW(file->File, filename))
5689 return file;
5692 return NULL;
5695 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5697 MSIPACKAGE *package = param;
5698 LPWSTR driver, driver_path, ptr;
5699 WCHAR outpath[MAX_PATH];
5700 MSIFILE *driver_file, *setup_file;
5701 MSIRECORD *uirow;
5702 LPCWSTR desc;
5703 DWORD len, usage;
5704 UINT r = ERROR_SUCCESS;
5706 static const WCHAR driver_fmt[] = {
5707 'D','r','i','v','e','r','=','%','s',0};
5708 static const WCHAR setup_fmt[] = {
5709 'S','e','t','u','p','=','%','s',0};
5710 static const WCHAR usage_fmt[] = {
5711 'F','i','l','e','U','s','a','g','e','=','1',0};
5713 desc = MSI_RecordGetString(rec, 3);
5715 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5716 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5718 if (!driver_file)
5720 ERR("ODBC Driver entry not found!\n");
5721 return ERROR_FUNCTION_FAILED;
5724 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5725 if (setup_file)
5726 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5727 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5729 driver = msi_alloc(len * sizeof(WCHAR));
5730 if (!driver)
5731 return ERROR_OUTOFMEMORY;
5733 ptr = driver;
5734 lstrcpyW(ptr, desc);
5735 ptr += lstrlenW(ptr) + 1;
5737 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5738 ptr += len + 1;
5740 if (setup_file)
5742 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5743 ptr += len + 1;
5746 lstrcpyW(ptr, usage_fmt);
5747 ptr += lstrlenW(ptr) + 1;
5748 *ptr = '\0';
5750 driver_path = strdupW(driver_file->TargetPath);
5751 ptr = strrchrW(driver_path, '\\');
5752 if (ptr) *ptr = '\0';
5754 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5755 NULL, ODBC_INSTALL_COMPLETE, &usage))
5757 ERR("Failed to install SQL driver!\n");
5758 r = ERROR_FUNCTION_FAILED;
5761 uirow = MSI_CreateRecord( 5 );
5762 MSI_RecordSetStringW( uirow, 1, desc );
5763 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5764 MSI_RecordSetStringW( uirow, 3, driver_path );
5765 ui_actiondata( package, szInstallODBC, uirow );
5766 msiobj_release( &uirow->hdr );
5768 msi_free(driver);
5769 msi_free(driver_path);
5771 return r;
5774 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5776 MSIPACKAGE *package = param;
5777 LPWSTR translator, translator_path, ptr;
5778 WCHAR outpath[MAX_PATH];
5779 MSIFILE *translator_file, *setup_file;
5780 MSIRECORD *uirow;
5781 LPCWSTR desc;
5782 DWORD len, usage;
5783 UINT r = ERROR_SUCCESS;
5785 static const WCHAR translator_fmt[] = {
5786 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5787 static const WCHAR setup_fmt[] = {
5788 'S','e','t','u','p','=','%','s',0};
5790 desc = MSI_RecordGetString(rec, 3);
5792 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5793 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5795 if (!translator_file)
5797 ERR("ODBC Translator entry not found!\n");
5798 return ERROR_FUNCTION_FAILED;
5801 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5802 if (setup_file)
5803 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5805 translator = msi_alloc(len * sizeof(WCHAR));
5806 if (!translator)
5807 return ERROR_OUTOFMEMORY;
5809 ptr = translator;
5810 lstrcpyW(ptr, desc);
5811 ptr += lstrlenW(ptr) + 1;
5813 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5814 ptr += len + 1;
5816 if (setup_file)
5818 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5819 ptr += len + 1;
5821 *ptr = '\0';
5823 translator_path = strdupW(translator_file->TargetPath);
5824 ptr = strrchrW(translator_path, '\\');
5825 if (ptr) *ptr = '\0';
5827 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5828 NULL, ODBC_INSTALL_COMPLETE, &usage))
5830 ERR("Failed to install SQL translator!\n");
5831 r = ERROR_FUNCTION_FAILED;
5834 uirow = MSI_CreateRecord( 5 );
5835 MSI_RecordSetStringW( uirow, 1, desc );
5836 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5837 MSI_RecordSetStringW( uirow, 3, translator_path );
5838 ui_actiondata( package, szInstallODBC, uirow );
5839 msiobj_release( &uirow->hdr );
5841 msi_free(translator);
5842 msi_free(translator_path);
5844 return r;
5847 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5849 MSIPACKAGE *package = param;
5850 LPWSTR attrs;
5851 LPCWSTR desc, driver;
5852 WORD request = ODBC_ADD_SYS_DSN;
5853 INT registration;
5854 DWORD len;
5855 UINT r = ERROR_SUCCESS;
5856 MSIRECORD *uirow;
5858 static const WCHAR attrs_fmt[] = {
5859 'D','S','N','=','%','s',0 };
5861 desc = MSI_RecordGetString(rec, 3);
5862 driver = MSI_RecordGetString(rec, 4);
5863 registration = MSI_RecordGetInteger(rec, 5);
5865 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5866 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5868 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5869 attrs = msi_alloc(len * sizeof(WCHAR));
5870 if (!attrs)
5871 return ERROR_OUTOFMEMORY;
5873 len = sprintfW(attrs, attrs_fmt, desc);
5874 attrs[len + 1] = 0;
5876 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5878 ERR("Failed to install SQL data source!\n");
5879 r = ERROR_FUNCTION_FAILED;
5882 uirow = MSI_CreateRecord( 5 );
5883 MSI_RecordSetStringW( uirow, 1, desc );
5884 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5885 MSI_RecordSetInteger( uirow, 3, request );
5886 ui_actiondata( package, szInstallODBC, uirow );
5887 msiobj_release( &uirow->hdr );
5889 msi_free(attrs);
5891 return r;
5894 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5896 UINT rc;
5897 MSIQUERY *view;
5899 static const WCHAR driver_query[] = {
5900 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5901 'O','D','B','C','D','r','i','v','e','r',0 };
5903 static const WCHAR translator_query[] = {
5904 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5905 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5907 static const WCHAR source_query[] = {
5908 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5909 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5911 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5912 if (rc != ERROR_SUCCESS)
5913 return ERROR_SUCCESS;
5915 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5916 msiobj_release(&view->hdr);
5918 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5919 if (rc != ERROR_SUCCESS)
5920 return ERROR_SUCCESS;
5922 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5923 msiobj_release(&view->hdr);
5925 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5926 if (rc != ERROR_SUCCESS)
5927 return ERROR_SUCCESS;
5929 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5930 msiobj_release(&view->hdr);
5932 return rc;
5935 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5937 MSIPACKAGE *package = param;
5938 MSIRECORD *uirow;
5939 DWORD usage;
5940 LPCWSTR desc;
5942 desc = MSI_RecordGetString( rec, 3 );
5943 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5945 WARN("Failed to remove ODBC driver\n");
5947 else if (!usage)
5949 FIXME("Usage count reached 0\n");
5952 uirow = MSI_CreateRecord( 2 );
5953 MSI_RecordSetStringW( uirow, 1, desc );
5954 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5955 ui_actiondata( package, szRemoveODBC, uirow );
5956 msiobj_release( &uirow->hdr );
5958 return ERROR_SUCCESS;
5961 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5963 MSIPACKAGE *package = param;
5964 MSIRECORD *uirow;
5965 DWORD usage;
5966 LPCWSTR desc;
5968 desc = MSI_RecordGetString( rec, 3 );
5969 if (!SQLRemoveTranslatorW( desc, &usage ))
5971 WARN("Failed to remove ODBC translator\n");
5973 else if (!usage)
5975 FIXME("Usage count reached 0\n");
5978 uirow = MSI_CreateRecord( 2 );
5979 MSI_RecordSetStringW( uirow, 1, desc );
5980 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5981 ui_actiondata( package, szRemoveODBC, uirow );
5982 msiobj_release( &uirow->hdr );
5984 return ERROR_SUCCESS;
5987 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5989 MSIPACKAGE *package = param;
5990 MSIRECORD *uirow;
5991 LPWSTR attrs;
5992 LPCWSTR desc, driver;
5993 WORD request = ODBC_REMOVE_SYS_DSN;
5994 INT registration;
5995 DWORD len;
5997 static const WCHAR attrs_fmt[] = {
5998 'D','S','N','=','%','s',0 };
6000 desc = MSI_RecordGetString( rec, 3 );
6001 driver = MSI_RecordGetString( rec, 4 );
6002 registration = MSI_RecordGetInteger( rec, 5 );
6004 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6005 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6007 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6008 attrs = msi_alloc( len * sizeof(WCHAR) );
6009 if (!attrs)
6010 return ERROR_OUTOFMEMORY;
6012 FIXME("Use ODBCSourceAttribute table\n");
6014 len = sprintfW( attrs, attrs_fmt, desc );
6015 attrs[len + 1] = 0;
6017 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6019 WARN("Failed to remove ODBC data source\n");
6021 msi_free( attrs );
6023 uirow = MSI_CreateRecord( 3 );
6024 MSI_RecordSetStringW( uirow, 1, desc );
6025 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6026 MSI_RecordSetInteger( uirow, 3, request );
6027 ui_actiondata( package, szRemoveODBC, uirow );
6028 msiobj_release( &uirow->hdr );
6030 return ERROR_SUCCESS;
6033 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6035 UINT rc;
6036 MSIQUERY *view;
6038 static const WCHAR driver_query[] = {
6039 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6040 'O','D','B','C','D','r','i','v','e','r',0 };
6042 static const WCHAR translator_query[] = {
6043 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6044 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6046 static const WCHAR source_query[] = {
6047 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6048 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6050 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6051 if (rc != ERROR_SUCCESS)
6052 return ERROR_SUCCESS;
6054 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6055 msiobj_release( &view->hdr );
6057 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6058 if (rc != ERROR_SUCCESS)
6059 return ERROR_SUCCESS;
6061 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6062 msiobj_release( &view->hdr );
6064 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6065 if (rc != ERROR_SUCCESS)
6066 return ERROR_SUCCESS;
6068 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6069 msiobj_release( &view->hdr );
6071 return rc;
6074 #define ENV_ACT_SETALWAYS 0x1
6075 #define ENV_ACT_SETABSENT 0x2
6076 #define ENV_ACT_REMOVE 0x4
6077 #define ENV_ACT_REMOVEMATCH 0x8
6079 #define ENV_MOD_MACHINE 0x20000000
6080 #define ENV_MOD_APPEND 0x40000000
6081 #define ENV_MOD_PREFIX 0x80000000
6082 #define ENV_MOD_MASK 0xC0000000
6084 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6086 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6088 LPCWSTR cptr = *name;
6090 static const WCHAR prefix[] = {'[','~',']',0};
6091 static const int prefix_len = 3;
6093 *flags = 0;
6094 while (*cptr)
6096 if (*cptr == '=')
6097 *flags |= ENV_ACT_SETALWAYS;
6098 else if (*cptr == '+')
6099 *flags |= ENV_ACT_SETABSENT;
6100 else if (*cptr == '-')
6101 *flags |= ENV_ACT_REMOVE;
6102 else if (*cptr == '!')
6103 *flags |= ENV_ACT_REMOVEMATCH;
6104 else if (*cptr == '*')
6105 *flags |= ENV_MOD_MACHINE;
6106 else
6107 break;
6109 cptr++;
6110 (*name)++;
6113 if (!*cptr)
6115 ERR("Missing environment variable\n");
6116 return ERROR_FUNCTION_FAILED;
6119 if (*value)
6121 LPCWSTR ptr = *value;
6122 if (!strncmpW(ptr, prefix, prefix_len))
6124 if (ptr[prefix_len] == szSemiColon[0])
6126 *flags |= ENV_MOD_APPEND;
6127 *value += lstrlenW(prefix);
6129 else
6131 *value = NULL;
6134 else if (lstrlenW(*value) >= prefix_len)
6136 ptr += lstrlenW(ptr) - prefix_len;
6137 if (!lstrcmpW(ptr, prefix))
6139 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6141 *flags |= ENV_MOD_PREFIX;
6142 /* the "[~]" will be removed by deformat_string */;
6144 else
6146 *value = NULL;
6152 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6153 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6154 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6155 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6157 ERR("Invalid flags: %08x\n", *flags);
6158 return ERROR_FUNCTION_FAILED;
6161 if (!*flags)
6162 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6164 return ERROR_SUCCESS;
6167 static UINT open_env_key( DWORD flags, HKEY *key )
6169 static const WCHAR user_env[] =
6170 {'E','n','v','i','r','o','n','m','e','n','t',0};
6171 static const WCHAR machine_env[] =
6172 {'S','y','s','t','e','m','\\',
6173 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6174 'C','o','n','t','r','o','l','\\',
6175 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6176 'E','n','v','i','r','o','n','m','e','n','t',0};
6177 const WCHAR *env;
6178 HKEY root;
6179 LONG res;
6181 if (flags & ENV_MOD_MACHINE)
6183 env = machine_env;
6184 root = HKEY_LOCAL_MACHINE;
6186 else
6188 env = user_env;
6189 root = HKEY_CURRENT_USER;
6192 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6193 if (res != ERROR_SUCCESS)
6195 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6196 return ERROR_FUNCTION_FAILED;
6199 return ERROR_SUCCESS;
6202 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6204 MSIPACKAGE *package = param;
6205 LPCWSTR name, value, component;
6206 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6207 DWORD flags, type, size;
6208 UINT res;
6209 HKEY env = NULL;
6210 MSICOMPONENT *comp;
6211 MSIRECORD *uirow;
6212 int action = 0;
6214 component = MSI_RecordGetString(rec, 4);
6215 comp = get_loaded_component(package, component);
6216 if (!comp)
6217 return ERROR_SUCCESS;
6219 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6221 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6222 comp->Action = comp->Installed;
6223 return ERROR_SUCCESS;
6225 comp->Action = INSTALLSTATE_LOCAL;
6227 name = MSI_RecordGetString(rec, 2);
6228 value = MSI_RecordGetString(rec, 3);
6230 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6232 res = env_parse_flags(&name, &value, &flags);
6233 if (res != ERROR_SUCCESS || !value)
6234 goto done;
6236 if (value && !deformat_string(package, value, &deformatted))
6238 res = ERROR_OUTOFMEMORY;
6239 goto done;
6242 value = deformatted;
6244 res = open_env_key( flags, &env );
6245 if (res != ERROR_SUCCESS)
6246 goto done;
6248 if (flags & ENV_MOD_MACHINE)
6249 action |= 0x20000000;
6251 size = 0;
6252 type = REG_SZ;
6253 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6254 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6255 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6256 goto done;
6258 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6260 action = 0x2;
6262 /* Nothing to do. */
6263 if (!value)
6265 res = ERROR_SUCCESS;
6266 goto done;
6269 /* If we are appending but the string was empty, strip ; */
6270 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6272 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6273 newval = strdupW(value);
6274 if (!newval)
6276 res = ERROR_OUTOFMEMORY;
6277 goto done;
6280 else
6282 action = 0x1;
6284 /* Contrary to MSDN, +-variable to [~];path works */
6285 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6287 res = ERROR_SUCCESS;
6288 goto done;
6291 data = msi_alloc(size);
6292 if (!data)
6294 RegCloseKey(env);
6295 return ERROR_OUTOFMEMORY;
6298 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6299 if (res != ERROR_SUCCESS)
6300 goto done;
6302 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
6304 action = 0x4;
6305 res = RegDeleteValueW(env, name);
6306 if (res != ERROR_SUCCESS)
6307 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6308 goto done;
6311 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6312 if (flags & ENV_MOD_MASK)
6314 DWORD mod_size;
6315 int multiplier = 0;
6316 if (flags & ENV_MOD_APPEND) multiplier++;
6317 if (flags & ENV_MOD_PREFIX) multiplier++;
6318 mod_size = lstrlenW(value) * multiplier;
6319 size += mod_size * sizeof(WCHAR);
6322 newval = msi_alloc(size);
6323 ptr = newval;
6324 if (!newval)
6326 res = ERROR_OUTOFMEMORY;
6327 goto done;
6330 if (flags & ENV_MOD_PREFIX)
6332 lstrcpyW(newval, value);
6333 ptr = newval + lstrlenW(value);
6334 action |= 0x80000000;
6337 lstrcpyW(ptr, data);
6339 if (flags & ENV_MOD_APPEND)
6341 lstrcatW(newval, value);
6342 action |= 0x40000000;
6345 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6346 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6347 if (res)
6349 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6352 done:
6353 uirow = MSI_CreateRecord( 3 );
6354 MSI_RecordSetStringW( uirow, 1, name );
6355 MSI_RecordSetStringW( uirow, 2, newval );
6356 MSI_RecordSetInteger( uirow, 3, action );
6357 ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6358 msiobj_release( &uirow->hdr );
6360 if (env) RegCloseKey(env);
6361 msi_free(deformatted);
6362 msi_free(data);
6363 msi_free(newval);
6364 return res;
6367 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6369 UINT rc;
6370 MSIQUERY * view;
6371 static const WCHAR ExecSeqQuery[] =
6372 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6373 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6374 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6375 if (rc != ERROR_SUCCESS)
6376 return ERROR_SUCCESS;
6378 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6379 msiobj_release(&view->hdr);
6381 return rc;
6384 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6386 MSIPACKAGE *package = param;
6387 LPCWSTR name, value, component;
6388 LPWSTR deformatted = NULL;
6389 DWORD flags;
6390 HKEY env;
6391 MSICOMPONENT *comp;
6392 MSIRECORD *uirow;
6393 int action = 0;
6394 LONG res;
6395 UINT r;
6397 component = MSI_RecordGetString( rec, 4 );
6398 comp = get_loaded_component( package, component );
6399 if (!comp)
6400 return ERROR_SUCCESS;
6402 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6404 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6405 comp->Action = comp->Installed;
6406 return ERROR_SUCCESS;
6408 comp->Action = INSTALLSTATE_ABSENT;
6410 name = MSI_RecordGetString( rec, 2 );
6411 value = MSI_RecordGetString( rec, 3 );
6413 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6415 r = env_parse_flags( &name, &value, &flags );
6416 if (r != ERROR_SUCCESS)
6417 return r;
6419 if (!(flags & ENV_ACT_REMOVE))
6421 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6422 return ERROR_SUCCESS;
6425 if (value && !deformat_string( package, value, &deformatted ))
6426 return ERROR_OUTOFMEMORY;
6428 value = deformatted;
6430 r = open_env_key( flags, &env );
6431 if (r != ERROR_SUCCESS)
6433 r = ERROR_SUCCESS;
6434 goto done;
6437 if (flags & ENV_MOD_MACHINE)
6438 action |= 0x20000000;
6440 TRACE("Removing %s\n", debugstr_w(name));
6442 res = RegDeleteValueW( env, name );
6443 if (res != ERROR_SUCCESS)
6445 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6446 r = ERROR_SUCCESS;
6449 done:
6450 uirow = MSI_CreateRecord( 3 );
6451 MSI_RecordSetStringW( uirow, 1, name );
6452 MSI_RecordSetStringW( uirow, 2, value );
6453 MSI_RecordSetInteger( uirow, 3, action );
6454 ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6455 msiobj_release( &uirow->hdr );
6457 if (env) RegCloseKey( env );
6458 msi_free( deformatted );
6459 return r;
6462 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6464 UINT rc;
6465 MSIQUERY *view;
6466 static const WCHAR query[] =
6467 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6468 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6470 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6471 if (rc != ERROR_SUCCESS)
6472 return ERROR_SUCCESS;
6474 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6475 msiobj_release( &view->hdr );
6477 return rc;
6480 typedef struct tagMSIASSEMBLY
6482 struct list entry;
6483 MSICOMPONENT *component;
6484 MSIFEATURE *feature;
6485 MSIFILE *file;
6486 LPWSTR manifest;
6487 LPWSTR application;
6488 LPWSTR display_name;
6489 DWORD attributes;
6490 BOOL installed;
6491 } MSIASSEMBLY;
6493 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6494 DWORD dwReserved);
6495 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6496 LPVOID pvReserved, HMODULE *phModDll);
6498 static BOOL init_functionpointers(void)
6500 HRESULT hr;
6501 HMODULE hfusion;
6502 HMODULE hmscoree;
6504 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6506 hmscoree = LoadLibraryA("mscoree.dll");
6507 if (!hmscoree)
6509 WARN("mscoree.dll not available\n");
6510 return FALSE;
6513 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6514 if (!pLoadLibraryShim)
6516 WARN("LoadLibraryShim not available\n");
6517 FreeLibrary(hmscoree);
6518 return FALSE;
6521 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6522 if (FAILED(hr))
6524 WARN("fusion.dll not available\n");
6525 FreeLibrary(hmscoree);
6526 return FALSE;
6529 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6531 FreeLibrary(hmscoree);
6532 return TRUE;
6535 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6536 LPWSTR path)
6538 IAssemblyCache *cache;
6539 MSIRECORD *uirow;
6540 HRESULT hr;
6541 UINT r = ERROR_FUNCTION_FAILED;
6543 TRACE("installing assembly: %s\n", debugstr_w(path));
6545 uirow = MSI_CreateRecord( 2 );
6546 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
6547 ui_actiondata( package, szMsiPublishAssemblies, uirow );
6548 msiobj_release( &uirow->hdr );
6550 if (assembly->feature)
6551 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6553 if (assembly->manifest)
6554 FIXME("Manifest unhandled\n");
6556 if (assembly->application)
6558 FIXME("Assembly should be privately installed\n");
6559 return ERROR_SUCCESS;
6562 if (assembly->attributes == msidbAssemblyAttributesWin32)
6564 FIXME("Win32 assemblies not handled\n");
6565 return ERROR_SUCCESS;
6568 hr = pCreateAssemblyCache(&cache, 0);
6569 if (FAILED(hr))
6570 goto done;
6572 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6573 if (FAILED(hr))
6574 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6576 r = ERROR_SUCCESS;
6578 done:
6579 IAssemblyCache_Release(cache);
6580 return r;
6583 typedef struct tagASSEMBLY_LIST
6585 MSIPACKAGE *package;
6586 IAssemblyCache *cache;
6587 struct list *assemblies;
6588 } ASSEMBLY_LIST;
6590 typedef struct tagASSEMBLY_NAME
6592 LPWSTR name;
6593 LPWSTR version;
6594 LPWSTR culture;
6595 LPWSTR pubkeytoken;
6596 } ASSEMBLY_NAME;
6598 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6600 ASSEMBLY_NAME *asmname = param;
6601 LPCWSTR name = MSI_RecordGetString(rec, 2);
6602 LPWSTR val = msi_dup_record_field(rec, 3);
6604 static const WCHAR Name[] = {'N','a','m','e',0};
6605 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6606 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6607 static const WCHAR PublicKeyToken[] = {
6608 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6610 if (!strcmpiW(name, Name))
6611 asmname->name = val;
6612 else if (!strcmpiW(name, Version))
6613 asmname->version = val;
6614 else if (!strcmpiW(name, Culture))
6615 asmname->culture = val;
6616 else if (!strcmpiW(name, PublicKeyToken))
6617 asmname->pubkeytoken = val;
6618 else
6619 msi_free(val);
6621 return ERROR_SUCCESS;
6624 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6626 if (!*str)
6628 *size = lstrlenW(append) + 1;
6629 *str = msi_alloc((*size) * sizeof(WCHAR));
6630 lstrcpyW(*str, append);
6631 return;
6634 (*size) += lstrlenW(append);
6635 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6636 lstrcatW(*str, append);
6639 static WCHAR *get_assembly_display_name( MSIDATABASE *db, MSICOMPONENT *comp )
6641 static const WCHAR separator[] = {',',' ',0};
6642 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6643 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6644 static const WCHAR PublicKeyToken[] = {'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6645 static const WCHAR query[] = {
6646 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6647 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6648 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6649 '=','\'','%','s','\'',0};
6650 ASSEMBLY_NAME name;
6651 MSIQUERY *view;
6652 LPWSTR display_name;
6653 DWORD size;
6654 UINT r;
6656 display_name = NULL;
6657 memset( &name, 0, sizeof(ASSEMBLY_NAME) );
6659 r = MSI_OpenQuery( db, &view, query, comp->Component );
6660 if (r != ERROR_SUCCESS)
6661 return NULL;
6663 MSI_IterateRecords( view, NULL, parse_assembly_name, &name );
6664 msiobj_release( &view->hdr );
6666 if (!name.name)
6668 ERR("No assembly name specified!\n");
6669 return NULL;
6672 append_str( &display_name, &size, name.name );
6674 if (name.version)
6676 append_str( &display_name, &size, separator );
6677 append_str( &display_name, &size, Version );
6678 append_str( &display_name, &size, name.version );
6680 if (name.culture)
6682 append_str( &display_name, &size, separator );
6683 append_str( &display_name, &size, Culture );
6684 append_str( &display_name, &size, name.culture );
6686 if (name.pubkeytoken)
6688 append_str( &display_name, &size, separator );
6689 append_str( &display_name, &size, PublicKeyToken );
6690 append_str( &display_name, &size, name.pubkeytoken );
6693 msi_free( name.name );
6694 msi_free( name.version );
6695 msi_free( name.culture );
6696 msi_free( name.pubkeytoken );
6698 return display_name;
6701 static BOOL check_assembly_installed( MSIDATABASE *db, IAssemblyCache *cache, MSICOMPONENT *comp )
6703 ASSEMBLY_INFO asminfo;
6704 LPWSTR disp;
6705 BOOL found = FALSE;
6706 HRESULT hr;
6708 disp = get_assembly_display_name( db, comp );
6709 if (!disp)
6710 return FALSE;
6712 memset( &asminfo, 0, sizeof(ASSEMBLY_INFO) );
6713 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6715 hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_VALIDATE, disp, &asminfo );
6716 if (SUCCEEDED(hr))
6717 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6719 msi_free( disp );
6720 return found;
6723 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6725 ASSEMBLY_LIST *list = param;
6726 MSIASSEMBLY *assembly;
6727 LPCWSTR component;
6729 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6730 if (!assembly)
6731 return ERROR_OUTOFMEMORY;
6733 component = MSI_RecordGetString(rec, 1);
6734 assembly->component = get_loaded_component(list->package, component);
6735 if (!assembly->component)
6736 return ERROR_SUCCESS;
6738 if (assembly->component->ActionRequest != INSTALLSTATE_LOCAL &&
6739 assembly->component->ActionRequest != INSTALLSTATE_SOURCE)
6741 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6742 assembly->component->Action = assembly->component->Installed;
6743 return ERROR_SUCCESS;
6745 assembly->component->Action = assembly->component->ActionRequest;
6747 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6748 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6750 if (!assembly->file)
6752 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6753 return ERROR_FUNCTION_FAILED;
6756 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6757 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6758 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6760 if (assembly->application)
6762 WCHAR version[24];
6763 DWORD size = sizeof(version)/sizeof(WCHAR);
6765 /* FIXME: we should probably check the manifest file here */
6767 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6768 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6770 assembly->installed = TRUE;
6773 else
6774 assembly->installed = check_assembly_installed(list->package->db,
6775 list->cache,
6776 assembly->component);
6778 list_add_head(list->assemblies, &assembly->entry);
6779 return ERROR_SUCCESS;
6782 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6784 IAssemblyCache *cache = NULL;
6785 ASSEMBLY_LIST list;
6786 MSIQUERY *view;
6787 HRESULT hr;
6788 UINT r;
6790 static const WCHAR query[] =
6791 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6792 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6794 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6795 if (r != ERROR_SUCCESS)
6796 return ERROR_SUCCESS;
6798 hr = pCreateAssemblyCache(&cache, 0);
6799 if (FAILED(hr))
6800 return ERROR_FUNCTION_FAILED;
6802 list.package = package;
6803 list.cache = cache;
6804 list.assemblies = assemblies;
6806 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6807 msiobj_release(&view->hdr);
6809 IAssemblyCache_Release(cache);
6811 return r;
6814 static void free_assemblies(struct list *assemblies)
6816 struct list *item, *cursor;
6818 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6820 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6822 list_remove(&assembly->entry);
6823 msi_free(assembly->application);
6824 msi_free(assembly->manifest);
6825 msi_free(assembly->display_name);
6826 msi_free(assembly);
6830 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6832 MSIASSEMBLY *assembly;
6834 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6836 if (!lstrcmpW(assembly->file->File, file))
6838 *out = assembly;
6839 return TRUE;
6843 return FALSE;
6846 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6847 LPWSTR *path, DWORD *attrs, PVOID user)
6849 MSIASSEMBLY *assembly;
6850 WCHAR temppath[MAX_PATH];
6851 struct list *assemblies = user;
6852 UINT r;
6854 if (!find_assembly(assemblies, file, &assembly))
6855 return FALSE;
6857 GetTempPathW(MAX_PATH, temppath);
6858 PathAddBackslashW(temppath);
6859 lstrcatW(temppath, assembly->file->FileName);
6861 if (action == MSICABEXTRACT_BEGINEXTRACT)
6863 if (assembly->installed)
6864 return FALSE;
6866 *path = strdupW(temppath);
6867 *attrs = assembly->file->Attributes;
6869 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6871 assembly->installed = TRUE;
6873 r = install_assembly(package, assembly, temppath);
6874 if (r != ERROR_SUCCESS)
6875 ERR("Failed to install assembly\n");
6878 return TRUE;
6881 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6883 UINT r;
6884 struct list assemblies = LIST_INIT(assemblies);
6885 MSIASSEMBLY *assembly;
6886 MSIMEDIAINFO *mi;
6888 if (!init_functionpointers() || !pCreateAssemblyCache)
6889 return ERROR_FUNCTION_FAILED;
6891 r = load_assemblies(package, &assemblies);
6892 if (r != ERROR_SUCCESS)
6893 goto done;
6895 if (list_empty(&assemblies))
6896 goto done;
6898 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6899 if (!mi)
6901 r = ERROR_OUTOFMEMORY;
6902 goto done;
6905 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6907 if (assembly->installed && !mi->is_continuous)
6908 continue;
6910 if (assembly->file->IsCompressed)
6912 if (assembly->file->disk_id != mi->disk_id || mi->is_continuous)
6914 MSICABDATA data;
6916 r = ready_media(package, assembly->file, mi);
6917 if (r != ERROR_SUCCESS)
6919 ERR("Failed to ready media\n");
6920 break;
6923 data.mi = mi;
6924 data.package = package;
6925 data.cb = installassembly_cb;
6926 data.user = &assemblies;
6928 if (!msi_cabextract(package, mi, &data))
6930 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6931 r = ERROR_FUNCTION_FAILED;
6932 break;
6936 else
6938 LPWSTR source = resolve_file_source(package, assembly->file);
6940 r = install_assembly(package, assembly, source);
6941 if (r != ERROR_SUCCESS)
6942 ERR("Failed to install assembly\n");
6944 msi_free(source);
6947 /* FIXME: write Installer assembly reg values */
6950 done:
6951 free_assemblies(&assemblies);
6952 return r;
6955 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6957 LPWSTR key, template, id;
6958 UINT r = ERROR_SUCCESS;
6960 id = msi_dup_property( package->db, szProductID );
6961 if (id)
6963 msi_free( id );
6964 return ERROR_SUCCESS;
6966 template = msi_dup_property( package->db, szPIDTemplate );
6967 key = msi_dup_property( package->db, szPIDKEY );
6969 if (key && template)
6971 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6972 r = msi_set_property( package->db, szProductID, key );
6974 msi_free( template );
6975 msi_free( key );
6976 return r;
6979 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6981 TRACE("\n");
6982 package->need_reboot = 1;
6983 return ERROR_SUCCESS;
6986 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6988 static const WCHAR szAvailableFreeReg[] =
6989 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6990 MSIRECORD *uirow;
6991 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6993 TRACE("%p %d kilobytes\n", package, space);
6995 uirow = MSI_CreateRecord( 1 );
6996 MSI_RecordSetInteger( uirow, 1, space );
6997 ui_actiondata( package, szAllocateRegistrySpace, uirow );
6998 msiobj_release( &uirow->hdr );
7000 return ERROR_SUCCESS;
7003 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7005 FIXME("%p\n", package);
7006 return ERROR_SUCCESS;
7009 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7011 FIXME("%p\n", package);
7012 return ERROR_SUCCESS;
7015 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7017 UINT r, count;
7018 MSIQUERY *view;
7020 static const WCHAR driver_query[] = {
7021 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7022 'O','D','B','C','D','r','i','v','e','r',0 };
7024 static const WCHAR translator_query[] = {
7025 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7026 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7028 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7029 if (r == ERROR_SUCCESS)
7031 count = 0;
7032 r = MSI_IterateRecords( view, &count, NULL, package );
7033 msiobj_release( &view->hdr );
7034 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7037 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7038 if (r == ERROR_SUCCESS)
7040 count = 0;
7041 r = MSI_IterateRecords( view, &count, NULL, package );
7042 msiobj_release( &view->hdr );
7043 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7046 return ERROR_SUCCESS;
7049 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7050 LPCSTR action, LPCWSTR table )
7052 static const WCHAR query[] = {
7053 'S','E','L','E','C','T',' ','*',' ',
7054 'F','R','O','M',' ','`','%','s','`',0 };
7055 MSIQUERY *view = NULL;
7056 DWORD count = 0;
7057 UINT r;
7059 r = MSI_OpenQuery( package->db, &view, query, table );
7060 if (r == ERROR_SUCCESS)
7062 r = MSI_IterateRecords(view, &count, NULL, package);
7063 msiobj_release(&view->hdr);
7066 if (count)
7067 FIXME("%s -> %u ignored %s table values\n",
7068 action, count, debugstr_w(table));
7070 return ERROR_SUCCESS;
7073 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7075 static const WCHAR table[] = { 'P','a','t','c','h',0 };
7076 return msi_unimplemented_action_stub( package, "PatchFiles", table );
7079 static UINT ACTION_BindImage( MSIPACKAGE *package )
7081 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7082 return msi_unimplemented_action_stub( package, "BindImage", table );
7085 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7087 static const WCHAR table[] = {
7088 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7089 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7092 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7094 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7095 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
7098 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
7100 static const WCHAR table[] = {
7101 'M','s','i','A','s','s','e','m','b','l','y',0 };
7102 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
7105 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7107 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7108 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7111 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7113 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7114 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7117 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7119 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7120 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7123 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7125 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7126 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7129 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7131 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7132 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
7135 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7137 static const struct
7139 const WCHAR *action;
7140 UINT (*handler)(MSIPACKAGE *);
7142 StandardActions[] =
7144 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7145 { szAppSearch, ACTION_AppSearch },
7146 { szBindImage, ACTION_BindImage },
7147 { szCCPSearch, ACTION_CCPSearch },
7148 { szCostFinalize, ACTION_CostFinalize },
7149 { szCostInitialize, ACTION_CostInitialize },
7150 { szCreateFolders, ACTION_CreateFolders },
7151 { szCreateShortcuts, ACTION_CreateShortcuts },
7152 { szDeleteServices, ACTION_DeleteServices },
7153 { szDisableRollback, ACTION_DisableRollback },
7154 { szDuplicateFiles, ACTION_DuplicateFiles },
7155 { szExecuteAction, ACTION_ExecuteAction },
7156 { szFileCost, ACTION_FileCost },
7157 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7158 { szForceReboot, ACTION_ForceReboot },
7159 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7160 { szInstallExecute, ACTION_InstallExecute },
7161 { szInstallExecuteAgain, ACTION_InstallExecute },
7162 { szInstallFiles, ACTION_InstallFiles},
7163 { szInstallFinalize, ACTION_InstallFinalize },
7164 { szInstallInitialize, ACTION_InstallInitialize },
7165 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7166 { szInstallValidate, ACTION_InstallValidate },
7167 { szIsolateComponents, ACTION_IsolateComponents },
7168 { szLaunchConditions, ACTION_LaunchConditions },
7169 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7170 { szMoveFiles, ACTION_MoveFiles },
7171 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7172 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7173 { szInstallODBC, ACTION_InstallODBC },
7174 { szInstallServices, ACTION_InstallServices },
7175 { szPatchFiles, ACTION_PatchFiles },
7176 { szProcessComponents, ACTION_ProcessComponents },
7177 { szPublishComponents, ACTION_PublishComponents },
7178 { szPublishFeatures, ACTION_PublishFeatures },
7179 { szPublishProduct, ACTION_PublishProduct },
7180 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7181 { szRegisterComPlus, ACTION_RegisterComPlus},
7182 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7183 { szRegisterFonts, ACTION_RegisterFonts },
7184 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7185 { szRegisterProduct, ACTION_RegisterProduct },
7186 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7187 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7188 { szRegisterUser, ACTION_RegisterUser },
7189 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7190 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7191 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7192 { szRemoveFiles, ACTION_RemoveFiles },
7193 { szRemoveFolders, ACTION_RemoveFolders },
7194 { szRemoveIniValues, ACTION_RemoveIniValues },
7195 { szRemoveODBC, ACTION_RemoveODBC },
7196 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7197 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7198 { szResolveSource, ACTION_ResolveSource },
7199 { szRMCCPSearch, ACTION_RMCCPSearch },
7200 { szScheduleReboot, ACTION_ScheduleReboot },
7201 { szSelfRegModules, ACTION_SelfRegModules },
7202 { szSelfUnregModules, ACTION_SelfUnregModules },
7203 { szSetODBCFolders, ACTION_SetODBCFolders },
7204 { szStartServices, ACTION_StartServices },
7205 { szStopServices, ACTION_StopServices },
7206 { szUnpublishComponents, ACTION_UnpublishComponents },
7207 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7208 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7209 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7210 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7211 { szUnregisterFonts, ACTION_UnregisterFonts },
7212 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7213 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7214 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7215 { szValidateProductID, ACTION_ValidateProductID },
7216 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7217 { szWriteIniValues, ACTION_WriteIniValues },
7218 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7219 { NULL, NULL },
7222 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
7223 UINT* rc, BOOL force )
7225 BOOL ret = FALSE;
7226 BOOL run = force;
7227 int i;
7229 if (!run && !package->script->CurrentlyScripting)
7230 run = TRUE;
7232 if (!run)
7234 if (strcmpW(action,szInstallFinalize) == 0 ||
7235 strcmpW(action,szInstallExecute) == 0 ||
7236 strcmpW(action,szInstallExecuteAgain) == 0)
7237 run = TRUE;
7240 i = 0;
7241 while (StandardActions[i].action != NULL)
7243 if (strcmpW(StandardActions[i].action, action)==0)
7245 if (!run)
7247 ui_actioninfo(package, action, TRUE, 0);
7248 *rc = schedule_action(package,INSTALL_SCRIPT,action);
7249 ui_actioninfo(package, action, FALSE, *rc);
7251 else
7253 ui_actionstart(package, action);
7254 if (StandardActions[i].handler)
7256 *rc = StandardActions[i].handler(package);
7258 else
7260 FIXME("unhandled standard action %s\n",debugstr_w(action));
7261 *rc = ERROR_SUCCESS;
7264 ret = TRUE;
7265 break;
7267 i++;
7269 return ret;
7272 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
7274 UINT rc = ERROR_SUCCESS;
7275 BOOL handled;
7277 TRACE("Performing action (%s)\n", debugstr_w(action));
7279 handled = ACTION_HandleStandardAction(package, action, &rc, force);
7281 if (!handled)
7282 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
7284 if (!handled)
7286 WARN("unhandled msi action %s\n", debugstr_w(action));
7287 rc = ERROR_FUNCTION_NOT_CALLED;
7290 return rc;
7293 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7295 UINT rc = ERROR_SUCCESS;
7296 BOOL handled = FALSE;
7298 TRACE("Performing action (%s)\n", debugstr_w(action));
7300 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
7302 if (!handled)
7303 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7305 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7306 handled = TRUE;
7308 if (!handled)
7310 WARN("unhandled msi action %s\n", debugstr_w(action));
7311 rc = ERROR_FUNCTION_NOT_CALLED;
7314 return rc;
7317 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7319 UINT rc = ERROR_SUCCESS;
7320 MSIRECORD *row;
7322 static const WCHAR ExecSeqQuery[] =
7323 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7324 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7325 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7326 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7327 static const WCHAR UISeqQuery[] =
7328 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7329 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7330 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7331 ' ', '=',' ','%','i',0};
7333 if (needs_ui_sequence(package))
7334 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7335 else
7336 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7338 if (row)
7340 LPCWSTR action, cond;
7342 TRACE("Running the actions\n");
7344 /* check conditions */
7345 cond = MSI_RecordGetString(row, 2);
7347 /* this is a hack to skip errors in the condition code */
7348 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7350 msiobj_release(&row->hdr);
7351 return ERROR_SUCCESS;
7354 action = MSI_RecordGetString(row, 1);
7355 if (!action)
7357 ERR("failed to fetch action\n");
7358 msiobj_release(&row->hdr);
7359 return ERROR_FUNCTION_FAILED;
7362 if (needs_ui_sequence(package))
7363 rc = ACTION_PerformUIAction(package, action, -1);
7364 else
7365 rc = ACTION_PerformAction(package, action, -1, FALSE);
7367 msiobj_release(&row->hdr);
7370 return rc;
7373 /****************************************************
7374 * TOP level entry points
7375 *****************************************************/
7377 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7378 LPCWSTR szCommandLine )
7380 UINT rc;
7381 BOOL ui_exists;
7383 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7384 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7386 msi_set_property( package->db, szAction, szInstall );
7388 package->script->InWhatSequence = SEQUENCE_INSTALL;
7390 if (szPackagePath)
7392 LPWSTR p, dir;
7393 LPCWSTR file;
7395 dir = strdupW(szPackagePath);
7396 p = strrchrW(dir, '\\');
7397 if (p)
7399 *(++p) = 0;
7400 file = szPackagePath + (p - dir);
7402 else
7404 msi_free(dir);
7405 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7406 GetCurrentDirectoryW(MAX_PATH, dir);
7407 lstrcatW(dir, szBackSlash);
7408 file = szPackagePath;
7411 msi_free( package->PackagePath );
7412 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7413 if (!package->PackagePath)
7415 msi_free(dir);
7416 return ERROR_OUTOFMEMORY;
7419 lstrcpyW(package->PackagePath, dir);
7420 lstrcatW(package->PackagePath, file);
7421 msi_free(dir);
7423 msi_set_sourcedir_props(package, FALSE);
7426 msi_parse_command_line( package, szCommandLine, FALSE );
7428 msi_apply_transforms( package );
7429 msi_apply_patches( package );
7431 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7433 TRACE("setting reinstall property\n");
7434 msi_set_property( package->db, szReinstall, szAll );
7437 /* properties may have been added by a transform */
7438 msi_clone_properties( package );
7439 msi_set_context( package );
7441 if (needs_ui_sequence( package))
7443 package->script->InWhatSequence |= SEQUENCE_UI;
7444 rc = ACTION_ProcessUISequence(package);
7445 ui_exists = ui_sequence_exists(package);
7446 if (rc == ERROR_SUCCESS || !ui_exists)
7448 package->script->InWhatSequence |= SEQUENCE_EXEC;
7449 rc = ACTION_ProcessExecSequence(package, ui_exists);
7452 else
7453 rc = ACTION_ProcessExecSequence(package, FALSE);
7455 package->script->CurrentlyScripting = FALSE;
7457 /* process the ending type action */
7458 if (rc == ERROR_SUCCESS)
7459 ACTION_PerformActionSequence(package, -1);
7460 else if (rc == ERROR_INSTALL_USEREXIT)
7461 ACTION_PerformActionSequence(package, -2);
7462 else if (rc == ERROR_INSTALL_SUSPEND)
7463 ACTION_PerformActionSequence(package, -4);
7464 else /* failed */
7465 ACTION_PerformActionSequence(package, -3);
7467 /* finish up running custom actions */
7468 ACTION_FinishCustomActions(package);
7470 if (rc == ERROR_SUCCESS && package->need_reboot)
7471 return ERROR_SUCCESS_REBOOT_REQUIRED;
7473 return rc;