msvcr100: The msvcr100 debug channel is unused so remove it.
[wine/testsucceed.git] / dlls / msi / action.c
blob7ae8286c9787badca38654d3c97250331d7a654a
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, dir );
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 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1969 static const WCHAR name[] = {'\\',0};
1970 VS_FIXEDFILEINFO *ret;
1971 LPVOID version;
1972 DWORD versize, handle;
1973 UINT sz;
1975 TRACE("%s\n", debugstr_w(filename));
1977 versize = GetFileVersionInfoSizeW( filename, &handle );
1978 if (!versize)
1979 return NULL;
1981 version = msi_alloc( versize );
1982 if (!version)
1983 return NULL;
1985 GetFileVersionInfoW( filename, 0, versize, version );
1987 if (!VerQueryValueW( version, name, (LPVOID *)&ret, &sz ))
1989 msi_free( version );
1990 return NULL;
1993 msi_free( version );
1994 return ret;
1997 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
1999 DWORD ms, ls;
2001 msi_parse_version_string( version, &ms, &ls );
2003 if (fi->dwFileVersionMS > ms) return 1;
2004 else if (fi->dwFileVersionMS < ms) return -1;
2005 else if (fi->dwFileVersionLS > ls) return 1;
2006 else if (fi->dwFileVersionLS < ls) return -1;
2007 return 0;
2010 static DWORD get_disk_file_size( LPCWSTR filename )
2012 HANDLE file;
2013 DWORD size;
2015 TRACE("%s\n", debugstr_w(filename));
2017 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2018 if (file == INVALID_HANDLE_VALUE)
2019 return INVALID_FILE_SIZE;
2021 size = GetFileSize( file, NULL );
2022 CloseHandle( file );
2023 return size;
2026 static BOOL hash_matches( MSIFILE *file )
2028 UINT r;
2029 MSIFILEHASHINFO hash;
2031 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2032 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2033 if (r != ERROR_SUCCESS)
2034 return FALSE;
2036 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2039 static UINT set_file_install_states( MSIPACKAGE *package )
2041 VS_FIXEDFILEINFO *file_version;
2042 MSIFILE *file;
2044 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2046 MSICOMPONENT* comp = file->Component;
2047 DWORD file_size;
2048 LPWSTR p;
2050 if (!comp)
2051 continue;
2053 if (file->IsCompressed)
2054 comp->ForceLocalState = TRUE;
2056 /* calculate target */
2057 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2058 msi_free(file->TargetPath);
2060 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2062 file->TargetPath = build_directory_name(2, p, file->FileName);
2063 msi_free(p);
2065 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2067 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2069 file->state = msifs_missing;
2070 comp->Cost += file->FileSize;
2071 continue;
2073 if (file->Version && (file_version = msi_get_disk_file_version( file->TargetPath )))
2075 TRACE("new %s old %u.%u.%u.%u\n", debugstr_w(file->Version),
2076 HIWORD(file_version->dwFileVersionMS),
2077 LOWORD(file_version->dwFileVersionMS),
2078 HIWORD(file_version->dwFileVersionLS),
2079 LOWORD(file_version->dwFileVersionLS));
2081 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2083 file->state = msifs_overwrite;
2084 comp->Cost += file->FileSize;
2086 else
2088 TRACE("Destination file version equal or greater, not overwriting\n");
2089 file->state = msifs_present;
2091 msi_free( file_version );
2092 continue;
2094 if ((file_size = get_disk_file_size( file->TargetPath )) != file->FileSize)
2096 file->state = msifs_overwrite;
2097 comp->Cost += file->FileSize - file_size;
2098 continue;
2100 if (file->hash.dwFileHashInfoSize && hash_matches( file ))
2102 TRACE("File hashes match, not overwriting\n");
2103 file->state = msifs_present;
2104 continue;
2106 file->state = msifs_overwrite;
2107 comp->Cost += file->FileSize - file_size;
2110 return ERROR_SUCCESS;
2114 * A lot is done in this function aside from just the costing.
2115 * The costing needs to be implemented at some point but for now I am going
2116 * to focus on the directory building
2119 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2121 static const WCHAR ExecSeqQuery[] =
2122 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2123 '`','D','i','r','e','c','t','o','r','y','`',0};
2124 static const WCHAR ConditionQuery[] =
2125 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2126 '`','C','o','n','d','i','t','i','o','n','`',0};
2127 static const WCHAR szCosting[] =
2128 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2129 static const WCHAR szlevel[] =
2130 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2131 static const WCHAR szOutOfDiskSpace[] =
2132 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2133 MSICOMPONENT *comp;
2134 UINT rc = ERROR_SUCCESS;
2135 MSIQUERY * view;
2136 LPWSTR level;
2138 TRACE("Building Directory properties\n");
2140 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2141 if (rc == ERROR_SUCCESS)
2143 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2144 package);
2145 msiobj_release(&view->hdr);
2148 /* read components states from the registry */
2149 ACTION_GetComponentInstallStates(package);
2150 ACTION_GetFeatureInstallStates(package);
2152 TRACE("Calculating file install states\n");
2153 set_file_install_states( package );
2155 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2157 TRACE("Evaluating feature conditions\n");
2159 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2160 if (rc == ERROR_SUCCESS)
2162 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2163 msiobj_release( &view->hdr );
2166 TRACE("Evaluating component conditions\n");
2168 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2170 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2172 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2173 comp->Enabled = FALSE;
2175 else
2176 comp->Enabled = TRUE;
2179 msi_set_property( package->db, szCosting, szOne );
2180 /* set default run level if not set */
2181 level = msi_dup_property( package->db, szlevel );
2182 if (!level)
2183 msi_set_property( package->db, szlevel, szOne );
2184 msi_free(level);
2186 /* FIXME: check volume disk space */
2187 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2189 return MSI_SetFeatureStates(package);
2192 /* OK this value is "interpreted" and then formatted based on the
2193 first few characters */
2194 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2195 DWORD *size)
2197 LPSTR data = NULL;
2199 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2201 if (value[1]=='x')
2203 LPWSTR ptr;
2204 CHAR byte[5];
2205 LPWSTR deformated = NULL;
2206 int count;
2208 deformat_string(package, &value[2], &deformated);
2210 /* binary value type */
2211 ptr = deformated;
2212 *type = REG_BINARY;
2213 if (strlenW(ptr)%2)
2214 *size = (strlenW(ptr)/2)+1;
2215 else
2216 *size = strlenW(ptr)/2;
2218 data = msi_alloc(*size);
2220 byte[0] = '0';
2221 byte[1] = 'x';
2222 byte[4] = 0;
2223 count = 0;
2224 /* if uneven pad with a zero in front */
2225 if (strlenW(ptr)%2)
2227 byte[2]= '0';
2228 byte[3]= *ptr;
2229 ptr++;
2230 data[count] = (BYTE)strtol(byte,NULL,0);
2231 count ++;
2232 TRACE("Uneven byte count\n");
2234 while (*ptr)
2236 byte[2]= *ptr;
2237 ptr++;
2238 byte[3]= *ptr;
2239 ptr++;
2240 data[count] = (BYTE)strtol(byte,NULL,0);
2241 count ++;
2243 msi_free(deformated);
2245 TRACE("Data %i bytes(%i)\n",*size,count);
2247 else
2249 LPWSTR deformated;
2250 LPWSTR p;
2251 DWORD d = 0;
2252 deformat_string(package, &value[1], &deformated);
2254 *type=REG_DWORD;
2255 *size = sizeof(DWORD);
2256 data = msi_alloc(*size);
2257 p = deformated;
2258 if (*p == '-')
2259 p++;
2260 while (*p)
2262 if ( (*p < '0') || (*p > '9') )
2263 break;
2264 d *= 10;
2265 d += (*p - '0');
2266 p++;
2268 if (deformated[0] == '-')
2269 d = -d;
2270 *(LPDWORD)data = d;
2271 TRACE("DWORD %i\n",*(LPDWORD)data);
2273 msi_free(deformated);
2276 else
2278 static const WCHAR szMulti[] = {'[','~',']',0};
2279 LPCWSTR ptr;
2280 *type=REG_SZ;
2282 if (value[0]=='#')
2284 if (value[1]=='%')
2286 ptr = &value[2];
2287 *type=REG_EXPAND_SZ;
2289 else
2290 ptr = &value[1];
2292 else
2293 ptr=value;
2295 if (strstrW(value,szMulti))
2296 *type = REG_MULTI_SZ;
2298 /* remove initial delimiter */
2299 if (!strncmpW(value, szMulti, 3))
2300 ptr = value + 3;
2302 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2304 /* add double NULL terminator */
2305 if (*type == REG_MULTI_SZ)
2307 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2308 data = msi_realloc_zero(data, *size);
2311 return data;
2314 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2316 const WCHAR *ret;
2318 switch (root)
2320 case -1:
2321 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2323 *root_key = HKEY_LOCAL_MACHINE;
2324 ret = szHLM;
2326 else
2328 *root_key = HKEY_CURRENT_USER;
2329 ret = szHCU;
2331 break;
2332 case 0:
2333 *root_key = HKEY_CLASSES_ROOT;
2334 ret = szHCR;
2335 break;
2336 case 1:
2337 *root_key = HKEY_CURRENT_USER;
2338 ret = szHCU;
2339 break;
2340 case 2:
2341 *root_key = HKEY_LOCAL_MACHINE;
2342 ret = szHLM;
2343 break;
2344 case 3:
2345 *root_key = HKEY_USERS;
2346 ret = szHU;
2347 break;
2348 default:
2349 ERR("Unknown root %i\n", root);
2350 return NULL;
2353 return ret;
2356 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2358 MSIPACKAGE *package = param;
2359 LPSTR value_data = NULL;
2360 HKEY root_key, hkey;
2361 DWORD type,size;
2362 LPWSTR deformated;
2363 LPCWSTR szRoot, component, name, key, value;
2364 MSICOMPONENT *comp;
2365 MSIRECORD * uirow;
2366 LPWSTR uikey;
2367 INT root;
2368 BOOL check_first = FALSE;
2369 UINT rc;
2371 ui_progress(package,2,0,0,0);
2373 value = NULL;
2374 key = NULL;
2375 uikey = NULL;
2376 name = NULL;
2378 component = MSI_RecordGetString(row, 6);
2379 comp = get_loaded_component(package,component);
2380 if (!comp)
2381 return ERROR_SUCCESS;
2383 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2385 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2386 comp->Action = comp->Installed;
2387 return ERROR_SUCCESS;
2389 comp->Action = INSTALLSTATE_LOCAL;
2391 name = MSI_RecordGetString(row, 4);
2392 if( MSI_RecordIsNull(row,5) && name )
2394 /* null values can have special meanings */
2395 if (name[0]=='-' && name[1] == 0)
2396 return ERROR_SUCCESS;
2397 else if ((name[0]=='+' && name[1] == 0) ||
2398 (name[0] == '*' && name[1] == 0))
2399 name = NULL;
2400 check_first = TRUE;
2403 root = MSI_RecordGetInteger(row,2);
2404 key = MSI_RecordGetString(row, 3);
2406 szRoot = get_root_key( package, root, &root_key );
2407 if (!szRoot)
2408 return ERROR_SUCCESS;
2410 deformat_string(package, key , &deformated);
2411 size = strlenW(deformated) + strlenW(szRoot) + 1;
2412 uikey = msi_alloc(size*sizeof(WCHAR));
2413 strcpyW(uikey,szRoot);
2414 strcatW(uikey,deformated);
2416 if (RegCreateKeyW( root_key, deformated, &hkey))
2418 ERR("Could not create key %s\n",debugstr_w(deformated));
2419 msi_free(deformated);
2420 msi_free(uikey);
2421 return ERROR_SUCCESS;
2423 msi_free(deformated);
2425 value = MSI_RecordGetString(row,5);
2426 if (value)
2427 value_data = parse_value(package, value, &type, &size);
2428 else
2430 value_data = (LPSTR)strdupW(szEmpty);
2431 size = sizeof(szEmpty);
2432 type = REG_SZ;
2435 deformat_string(package, name, &deformated);
2437 if (!check_first)
2439 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2440 debugstr_w(uikey));
2441 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2443 else
2445 DWORD sz = 0;
2446 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2447 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2449 TRACE("value %s of %s checked already exists\n",
2450 debugstr_w(deformated), debugstr_w(uikey));
2452 else
2454 TRACE("Checked and setting value %s of %s\n",
2455 debugstr_w(deformated), debugstr_w(uikey));
2456 if (deformated || size)
2457 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2460 RegCloseKey(hkey);
2462 uirow = MSI_CreateRecord(3);
2463 MSI_RecordSetStringW(uirow,2,deformated);
2464 MSI_RecordSetStringW(uirow,1,uikey);
2465 if (type == REG_SZ || type == REG_EXPAND_SZ)
2466 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2467 ui_actiondata(package,szWriteRegistryValues,uirow);
2468 msiobj_release( &uirow->hdr );
2470 msi_free(value_data);
2471 msi_free(deformated);
2472 msi_free(uikey);
2474 return ERROR_SUCCESS;
2477 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2479 UINT rc;
2480 MSIQUERY * view;
2481 static const WCHAR ExecSeqQuery[] =
2482 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2483 '`','R','e','g','i','s','t','r','y','`',0 };
2485 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2486 if (rc != ERROR_SUCCESS)
2487 return ERROR_SUCCESS;
2489 /* increment progress bar each time action data is sent */
2490 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2492 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2494 msiobj_release(&view->hdr);
2495 return rc;
2498 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2500 LONG res;
2501 HKEY hkey;
2502 DWORD num_subkeys, num_values;
2504 if (delete_key)
2506 if ((res = RegDeleteTreeW( hkey_root, key )))
2508 WARN("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2510 return;
2513 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2515 if ((res = RegDeleteValueW( hkey, value )))
2517 WARN("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2519 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2520 NULL, NULL, NULL, NULL );
2521 RegCloseKey( hkey );
2523 if (!res && !num_subkeys && !num_values)
2525 TRACE("Removing empty key %s\n", debugstr_w(key));
2526 RegDeleteKeyW( hkey_root, key );
2528 return;
2530 WARN("Failed to open key %s (%d)\n", debugstr_w(key), res);
2534 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2536 MSIPACKAGE *package = param;
2537 LPCWSTR component, name, key_str, root_key_str;
2538 LPWSTR deformated_key, deformated_name, ui_key_str;
2539 MSICOMPONENT *comp;
2540 MSIRECORD *uirow;
2541 BOOL delete_key = FALSE;
2542 HKEY hkey_root;
2543 UINT size;
2544 INT root;
2546 ui_progress( package, 2, 0, 0, 0 );
2548 component = MSI_RecordGetString( row, 6 );
2549 comp = get_loaded_component( package, component );
2550 if (!comp)
2551 return ERROR_SUCCESS;
2553 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2555 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2556 comp->Action = comp->Installed;
2557 return ERROR_SUCCESS;
2559 comp->Action = INSTALLSTATE_ABSENT;
2561 name = MSI_RecordGetString( row, 4 );
2562 if (MSI_RecordIsNull( row, 5 ) && name )
2564 if (name[0] == '+' && !name[1])
2565 return ERROR_SUCCESS;
2566 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2568 delete_key = TRUE;
2569 name = NULL;
2573 root = MSI_RecordGetInteger( row, 2 );
2574 key_str = MSI_RecordGetString( row, 3 );
2576 root_key_str = get_root_key( package, root, &hkey_root );
2577 if (!root_key_str)
2578 return ERROR_SUCCESS;
2580 deformat_string( package, key_str, &deformated_key );
2581 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2582 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2583 strcpyW( ui_key_str, root_key_str );
2584 strcatW( ui_key_str, deformated_key );
2586 deformat_string( package, name, &deformated_name );
2588 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2589 msi_free( deformated_key );
2591 uirow = MSI_CreateRecord( 2 );
2592 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2593 MSI_RecordSetStringW( uirow, 2, deformated_name );
2595 ui_actiondata( package, szRemoveRegistryValues, uirow );
2596 msiobj_release( &uirow->hdr );
2598 msi_free( ui_key_str );
2599 msi_free( deformated_name );
2600 return ERROR_SUCCESS;
2603 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2605 MSIPACKAGE *package = param;
2606 LPCWSTR component, name, key_str, root_key_str;
2607 LPWSTR deformated_key, deformated_name, ui_key_str;
2608 MSICOMPONENT *comp;
2609 MSIRECORD *uirow;
2610 BOOL delete_key = FALSE;
2611 HKEY hkey_root;
2612 UINT size;
2613 INT root;
2615 ui_progress( package, 2, 0, 0, 0 );
2617 component = MSI_RecordGetString( row, 5 );
2618 comp = get_loaded_component( package, component );
2619 if (!comp)
2620 return ERROR_SUCCESS;
2622 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2624 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2625 comp->Action = comp->Installed;
2626 return ERROR_SUCCESS;
2628 comp->Action = INSTALLSTATE_LOCAL;
2630 if ((name = MSI_RecordGetString( row, 4 )))
2632 if (name[0] == '-' && !name[1])
2634 delete_key = TRUE;
2635 name = NULL;
2639 root = MSI_RecordGetInteger( row, 2 );
2640 key_str = MSI_RecordGetString( row, 3 );
2642 root_key_str = get_root_key( package, root, &hkey_root );
2643 if (!root_key_str)
2644 return ERROR_SUCCESS;
2646 deformat_string( package, key_str, &deformated_key );
2647 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2648 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2649 strcpyW( ui_key_str, root_key_str );
2650 strcatW( ui_key_str, deformated_key );
2652 deformat_string( package, name, &deformated_name );
2654 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2655 msi_free( deformated_key );
2657 uirow = MSI_CreateRecord( 2 );
2658 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2659 MSI_RecordSetStringW( uirow, 2, deformated_name );
2661 ui_actiondata( package, szRemoveRegistryValues, uirow );
2662 msiobj_release( &uirow->hdr );
2664 msi_free( ui_key_str );
2665 msi_free( deformated_name );
2666 return ERROR_SUCCESS;
2669 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2671 UINT rc;
2672 MSIQUERY *view;
2673 static const WCHAR registry_query[] =
2674 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2675 '`','R','e','g','i','s','t','r','y','`',0 };
2676 static const WCHAR remove_registry_query[] =
2677 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2678 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2680 /* increment progress bar each time action data is sent */
2681 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2683 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2684 if (rc == ERROR_SUCCESS)
2686 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2687 msiobj_release( &view->hdr );
2688 if (rc != ERROR_SUCCESS)
2689 return rc;
2692 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2693 if (rc == ERROR_SUCCESS)
2695 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2696 msiobj_release( &view->hdr );
2697 if (rc != ERROR_SUCCESS)
2698 return rc;
2701 return ERROR_SUCCESS;
2704 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2706 package->script->CurrentlyScripting = TRUE;
2708 return ERROR_SUCCESS;
2712 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2714 MSICOMPONENT *comp;
2715 DWORD progress = 0;
2716 DWORD total = 0;
2717 static const WCHAR q1[]=
2718 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2719 '`','R','e','g','i','s','t','r','y','`',0};
2720 UINT rc;
2721 MSIQUERY * view;
2722 MSIFEATURE *feature;
2723 MSIFILE *file;
2725 TRACE("InstallValidate\n");
2727 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2728 if (rc == ERROR_SUCCESS)
2730 MSI_IterateRecords( view, &progress, NULL, package );
2731 msiobj_release( &view->hdr );
2732 total += progress * REG_PROGRESS_VALUE;
2735 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2736 total += COMPONENT_PROGRESS_VALUE;
2738 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2739 total += file->FileSize;
2741 ui_progress(package,0,total,0,0);
2743 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2745 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2746 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2747 feature->ActionRequest);
2750 return ERROR_SUCCESS;
2753 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2755 MSIPACKAGE* package = param;
2756 LPCWSTR cond = NULL;
2757 LPCWSTR message = NULL;
2758 UINT r;
2760 static const WCHAR title[]=
2761 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2763 cond = MSI_RecordGetString(row,1);
2765 r = MSI_EvaluateConditionW(package,cond);
2766 if (r == MSICONDITION_FALSE)
2768 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2770 LPWSTR deformated;
2771 message = MSI_RecordGetString(row,2);
2772 deformat_string(package,message,&deformated);
2773 MessageBoxW(NULL,deformated,title,MB_OK);
2774 msi_free(deformated);
2777 return ERROR_INSTALL_FAILURE;
2780 return ERROR_SUCCESS;
2783 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2785 UINT rc;
2786 MSIQUERY * view = NULL;
2787 static const WCHAR ExecSeqQuery[] =
2788 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2789 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2791 TRACE("Checking launch conditions\n");
2793 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2794 if (rc != ERROR_SUCCESS)
2795 return ERROR_SUCCESS;
2797 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2798 msiobj_release(&view->hdr);
2800 return rc;
2803 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2806 if (!cmp->KeyPath)
2807 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2809 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2811 MSIRECORD * row = 0;
2812 UINT root,len;
2813 LPWSTR deformated,buffer,deformated_name;
2814 LPCWSTR key,name;
2815 static const WCHAR ExecSeqQuery[] =
2816 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2817 '`','R','e','g','i','s','t','r','y','`',' ',
2818 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2819 ' ','=',' ' ,'\'','%','s','\'',0 };
2820 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2821 static const WCHAR fmt2[]=
2822 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2824 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2825 if (!row)
2826 return NULL;
2828 root = MSI_RecordGetInteger(row,2);
2829 key = MSI_RecordGetString(row, 3);
2830 name = MSI_RecordGetString(row, 4);
2831 deformat_string(package, key , &deformated);
2832 deformat_string(package, name, &deformated_name);
2834 len = strlenW(deformated) + 6;
2835 if (deformated_name)
2836 len+=strlenW(deformated_name);
2838 buffer = msi_alloc( len *sizeof(WCHAR));
2840 if (deformated_name)
2841 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2842 else
2843 sprintfW(buffer,fmt,root,deformated);
2845 msi_free(deformated);
2846 msi_free(deformated_name);
2847 msiobj_release(&row->hdr);
2849 return buffer;
2851 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2853 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2854 return NULL;
2856 else
2858 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2860 if (file)
2861 return strdupW( file->TargetPath );
2863 return NULL;
2866 static HKEY openSharedDLLsKey(void)
2868 HKEY hkey=0;
2869 static const WCHAR path[] =
2870 {'S','o','f','t','w','a','r','e','\\',
2871 'M','i','c','r','o','s','o','f','t','\\',
2872 'W','i','n','d','o','w','s','\\',
2873 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2874 'S','h','a','r','e','d','D','L','L','s',0};
2876 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2877 return hkey;
2880 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2882 HKEY hkey;
2883 DWORD count=0;
2884 DWORD type;
2885 DWORD sz = sizeof(count);
2886 DWORD rc;
2888 hkey = openSharedDLLsKey();
2889 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2890 if (rc != ERROR_SUCCESS)
2891 count = 0;
2892 RegCloseKey(hkey);
2893 return count;
2896 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2898 HKEY hkey;
2900 hkey = openSharedDLLsKey();
2901 if (count > 0)
2902 msi_reg_set_val_dword( hkey, path, count );
2903 else
2904 RegDeleteValueW(hkey,path);
2905 RegCloseKey(hkey);
2906 return count;
2910 * Return TRUE if the count should be written out and FALSE if not
2912 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2914 MSIFEATURE *feature;
2915 INT count = 0;
2916 BOOL write = FALSE;
2918 /* only refcount DLLs */
2919 if (comp->KeyPath == NULL ||
2920 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2921 comp->Attributes & msidbComponentAttributesODBCDataSource)
2922 write = FALSE;
2923 else
2925 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2926 write = (count > 0);
2928 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2929 write = TRUE;
2932 /* increment counts */
2933 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2935 ComponentList *cl;
2937 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2938 continue;
2940 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2942 if ( cl->component == comp )
2943 count++;
2947 /* decrement counts */
2948 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2950 ComponentList *cl;
2952 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2953 continue;
2955 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2957 if ( cl->component == comp )
2958 count--;
2962 /* ref count all the files in the component */
2963 if (write)
2965 MSIFILE *file;
2967 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2969 if (file->Component == comp)
2970 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2974 /* add a count for permanent */
2975 if (comp->Attributes & msidbComponentAttributesPermanent)
2976 count ++;
2978 comp->RefCount = count;
2980 if (write)
2981 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2984 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2986 WCHAR squished_pc[GUID_SIZE];
2987 WCHAR squished_cc[GUID_SIZE];
2988 UINT rc;
2989 MSICOMPONENT *comp;
2990 HKEY hkey;
2992 TRACE("\n");
2994 squash_guid(package->ProductCode,squished_pc);
2995 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2997 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2999 MSIRECORD * uirow;
3001 ui_progress(package,2,0,0,0);
3002 if (!comp->ComponentId)
3003 continue;
3005 squash_guid(comp->ComponentId,squished_cc);
3007 msi_free(comp->FullKeypath);
3008 comp->FullKeypath = resolve_keypath( package, comp );
3010 ACTION_RefCountComponent( package, comp );
3012 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
3013 debugstr_w(comp->Component),
3014 debugstr_w(squished_cc),
3015 debugstr_w(comp->FullKeypath),
3016 comp->RefCount);
3018 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3019 comp->ActionRequest == INSTALLSTATE_SOURCE)
3021 if (!comp->FullKeypath)
3022 continue;
3024 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3025 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
3026 &hkey, TRUE);
3027 else
3028 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
3029 &hkey, TRUE);
3031 if (rc != ERROR_SUCCESS)
3032 continue;
3034 if (comp->Attributes & msidbComponentAttributesPermanent)
3036 static const WCHAR szPermKey[] =
3037 { '0','0','0','0','0','0','0','0','0','0','0','0',
3038 '0','0','0','0','0','0','0','0','0','0','0','0',
3039 '0','0','0','0','0','0','0','0',0 };
3041 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3044 if (comp->Action == INSTALLSTATE_LOCAL)
3045 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3046 else
3048 MSIFILE *file;
3049 MSIRECORD *row;
3050 LPWSTR ptr, ptr2;
3051 WCHAR source[MAX_PATH];
3052 WCHAR base[MAX_PATH];
3053 LPWSTR sourcepath;
3055 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3056 static const WCHAR query[] = {
3057 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3058 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3059 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3060 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3061 '`','D','i','s','k','I','d','`',0};
3063 file = get_loaded_file(package, comp->KeyPath);
3064 if (!file)
3065 continue;
3067 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3068 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3069 ptr2 = strrchrW(source, '\\') + 1;
3070 msiobj_release(&row->hdr);
3072 lstrcpyW(base, package->PackagePath);
3073 ptr = strrchrW(base, '\\');
3074 *(ptr + 1) = '\0';
3076 sourcepath = resolve_file_source(package, file);
3077 ptr = sourcepath + lstrlenW(base);
3078 lstrcpyW(ptr2, ptr);
3079 msi_free(sourcepath);
3081 msi_reg_set_val_str(hkey, squished_pc, source);
3083 RegCloseKey(hkey);
3085 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3087 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3088 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3089 else
3090 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3092 comp->Action = comp->ActionRequest;
3094 /* UI stuff */
3095 uirow = MSI_CreateRecord(3);
3096 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3097 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3098 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3099 ui_actiondata(package,szProcessComponents,uirow);
3100 msiobj_release( &uirow->hdr );
3103 return ERROR_SUCCESS;
3106 typedef struct {
3107 CLSID clsid;
3108 LPWSTR source;
3110 LPWSTR path;
3111 ITypeLib *ptLib;
3112 } typelib_struct;
3114 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3115 LPWSTR lpszName, LONG_PTR lParam)
3117 TLIBATTR *attr;
3118 typelib_struct *tl_struct = (typelib_struct*) lParam;
3119 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3120 int sz;
3121 HRESULT res;
3123 if (!IS_INTRESOURCE(lpszName))
3125 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3126 return TRUE;
3129 sz = strlenW(tl_struct->source)+4;
3130 sz *= sizeof(WCHAR);
3132 if ((INT_PTR)lpszName == 1)
3133 tl_struct->path = strdupW(tl_struct->source);
3134 else
3136 tl_struct->path = msi_alloc(sz);
3137 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3140 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3141 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3142 if (FAILED(res))
3144 msi_free(tl_struct->path);
3145 tl_struct->path = NULL;
3147 return TRUE;
3150 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3151 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3153 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3154 return FALSE;
3157 msi_free(tl_struct->path);
3158 tl_struct->path = NULL;
3160 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3161 ITypeLib_Release(tl_struct->ptLib);
3163 return TRUE;
3166 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3168 MSIPACKAGE* package = param;
3169 LPCWSTR component;
3170 MSICOMPONENT *comp;
3171 MSIFILE *file;
3172 typelib_struct tl_struct;
3173 ITypeLib *tlib;
3174 HMODULE module;
3175 HRESULT hr;
3177 component = MSI_RecordGetString(row,3);
3178 comp = get_loaded_component(package,component);
3179 if (!comp)
3180 return ERROR_SUCCESS;
3182 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3184 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3185 comp->Action = comp->Installed;
3186 return ERROR_SUCCESS;
3188 comp->Action = INSTALLSTATE_LOCAL;
3190 file = get_loaded_file( package, comp->KeyPath );
3191 if (!file)
3192 return ERROR_SUCCESS;
3194 ui_actiondata( package, szRegisterTypeLibraries, row );
3196 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3197 if (module)
3199 LPCWSTR guid;
3200 guid = MSI_RecordGetString(row,1);
3201 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3202 tl_struct.source = strdupW( file->TargetPath );
3203 tl_struct.path = NULL;
3205 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3206 (LONG_PTR)&tl_struct);
3208 if (tl_struct.path)
3210 LPWSTR help = NULL;
3211 LPCWSTR helpid;
3212 HRESULT res;
3214 helpid = MSI_RecordGetString(row,6);
3216 if (helpid)
3217 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3218 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3219 msi_free(help);
3221 if (FAILED(res))
3222 ERR("Failed to register type library %s\n",
3223 debugstr_w(tl_struct.path));
3224 else
3225 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3227 ITypeLib_Release(tl_struct.ptLib);
3228 msi_free(tl_struct.path);
3230 else
3231 ERR("Failed to load type library %s\n",
3232 debugstr_w(tl_struct.source));
3234 FreeLibrary(module);
3235 msi_free(tl_struct.source);
3237 else
3239 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3240 if (FAILED(hr))
3242 ERR("Failed to load type library: %08x\n", hr);
3243 return ERROR_INSTALL_FAILURE;
3246 ITypeLib_Release(tlib);
3249 return ERROR_SUCCESS;
3252 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3255 * OK this is a bit confusing.. I am given a _Component key and I believe
3256 * that the file that is being registered as a type library is the "key file
3257 * of that component" which I interpret to mean "The file in the KeyPath of
3258 * that component".
3260 UINT rc;
3261 MSIQUERY * view;
3262 static const WCHAR Query[] =
3263 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3264 '`','T','y','p','e','L','i','b','`',0};
3266 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3267 if (rc != ERROR_SUCCESS)
3268 return ERROR_SUCCESS;
3270 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3271 msiobj_release(&view->hdr);
3272 return rc;
3275 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3277 MSIPACKAGE *package = param;
3278 LPCWSTR component, guid;
3279 MSICOMPONENT *comp;
3280 GUID libid;
3281 UINT version;
3282 LCID language;
3283 SYSKIND syskind;
3284 HRESULT hr;
3286 component = MSI_RecordGetString( row, 3 );
3287 comp = get_loaded_component( package, component );
3288 if (!comp)
3289 return ERROR_SUCCESS;
3291 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3293 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3294 comp->Action = comp->Installed;
3295 return ERROR_SUCCESS;
3297 comp->Action = INSTALLSTATE_ABSENT;
3299 ui_actiondata( package, szUnregisterTypeLibraries, row );
3301 guid = MSI_RecordGetString( row, 1 );
3302 CLSIDFromString( (LPCWSTR)guid, &libid );
3303 version = MSI_RecordGetInteger( row, 4 );
3304 language = MSI_RecordGetInteger( row, 2 );
3306 #ifdef _WIN64
3307 syskind = SYS_WIN64;
3308 #else
3309 syskind = SYS_WIN32;
3310 #endif
3312 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3313 if (FAILED(hr))
3315 WARN("Failed to unregister typelib: %08x\n", hr);
3318 return ERROR_SUCCESS;
3321 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3323 UINT rc;
3324 MSIQUERY *view;
3325 static const WCHAR query[] =
3326 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3327 '`','T','y','p','e','L','i','b','`',0};
3329 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3330 if (rc != ERROR_SUCCESS)
3331 return ERROR_SUCCESS;
3333 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3334 msiobj_release( &view->hdr );
3335 return rc;
3338 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3340 static const WCHAR szlnk[] = {'.','l','n','k',0};
3341 LPCWSTR directory, extension;
3342 LPWSTR link_folder, link_file, filename;
3344 directory = MSI_RecordGetString( row, 2 );
3345 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3347 /* may be needed because of a bug somewhere else */
3348 create_full_pathW( link_folder );
3350 filename = msi_dup_record_field( row, 3 );
3351 reduce_to_longfilename( filename );
3353 extension = strchrW( filename, '.' );
3354 if (!extension || strcmpiW( extension, szlnk ))
3356 int len = strlenW( filename );
3357 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3358 memcpy( filename + len, szlnk, sizeof(szlnk) );
3360 link_file = build_directory_name( 2, link_folder, filename );
3361 msi_free( link_folder );
3362 msi_free( filename );
3364 return link_file;
3367 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3369 MSIPACKAGE *package = param;
3370 LPWSTR link_file, deformated, path;
3371 LPCWSTR component, target;
3372 MSICOMPONENT *comp;
3373 IShellLinkW *sl = NULL;
3374 IPersistFile *pf = NULL;
3375 HRESULT res;
3377 component = MSI_RecordGetString(row, 4);
3378 comp = get_loaded_component(package, component);
3379 if (!comp)
3380 return ERROR_SUCCESS;
3382 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3384 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3385 comp->Action = comp->Installed;
3386 return ERROR_SUCCESS;
3388 comp->Action = INSTALLSTATE_LOCAL;
3390 ui_actiondata(package,szCreateShortcuts,row);
3392 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3393 &IID_IShellLinkW, (LPVOID *) &sl );
3395 if (FAILED( res ))
3397 ERR("CLSID_ShellLink not available\n");
3398 goto err;
3401 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3402 if (FAILED( res ))
3404 ERR("QueryInterface(IID_IPersistFile) failed\n");
3405 goto err;
3408 target = MSI_RecordGetString(row, 5);
3409 if (strchrW(target, '['))
3411 deformat_string(package, target, &deformated);
3412 IShellLinkW_SetPath(sl,deformated);
3413 msi_free(deformated);
3415 else
3417 FIXME("poorly handled shortcut format, advertised shortcut\n");
3418 IShellLinkW_SetPath(sl,comp->FullKeypath);
3421 if (!MSI_RecordIsNull(row,6))
3423 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3424 deformat_string(package, arguments, &deformated);
3425 IShellLinkW_SetArguments(sl,deformated);
3426 msi_free(deformated);
3429 if (!MSI_RecordIsNull(row,7))
3431 LPCWSTR description = MSI_RecordGetString(row, 7);
3432 IShellLinkW_SetDescription(sl, description);
3435 if (!MSI_RecordIsNull(row,8))
3436 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3438 if (!MSI_RecordIsNull(row,9))
3440 INT index;
3441 LPCWSTR icon = MSI_RecordGetString(row, 9);
3443 path = build_icon_path(package, icon);
3444 index = MSI_RecordGetInteger(row,10);
3446 /* no value means 0 */
3447 if (index == MSI_NULL_INTEGER)
3448 index = 0;
3450 IShellLinkW_SetIconLocation(sl, path, index);
3451 msi_free(path);
3454 if (!MSI_RecordIsNull(row,11))
3455 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3457 if (!MSI_RecordIsNull(row,12))
3459 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3460 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3461 if (path)
3462 IShellLinkW_SetWorkingDirectory(sl, path);
3463 msi_free(path);
3466 link_file = get_link_file(package, row);
3468 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3469 IPersistFile_Save(pf, link_file, FALSE);
3471 msi_free(link_file);
3473 err:
3474 if (pf)
3475 IPersistFile_Release( pf );
3476 if (sl)
3477 IShellLinkW_Release( sl );
3479 return ERROR_SUCCESS;
3482 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3484 UINT rc;
3485 HRESULT res;
3486 MSIQUERY * view;
3487 static const WCHAR Query[] =
3488 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3489 '`','S','h','o','r','t','c','u','t','`',0};
3491 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3492 if (rc != ERROR_SUCCESS)
3493 return ERROR_SUCCESS;
3495 res = CoInitialize( NULL );
3497 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3498 msiobj_release(&view->hdr);
3500 if (SUCCEEDED(res))
3501 CoUninitialize();
3503 return rc;
3506 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3508 MSIPACKAGE *package = param;
3509 LPWSTR link_file;
3510 LPCWSTR component;
3511 MSICOMPONENT *comp;
3513 component = MSI_RecordGetString( row, 4 );
3514 comp = get_loaded_component( package, component );
3515 if (!comp)
3516 return ERROR_SUCCESS;
3518 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3520 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3521 comp->Action = comp->Installed;
3522 return ERROR_SUCCESS;
3524 comp->Action = INSTALLSTATE_ABSENT;
3526 ui_actiondata( package, szRemoveShortcuts, row );
3528 link_file = get_link_file( package, row );
3530 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3531 if (!DeleteFileW( link_file ))
3533 WARN("Failed to remove shortcut file %u\n", GetLastError());
3535 msi_free( link_file );
3537 return ERROR_SUCCESS;
3540 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3542 UINT rc;
3543 MSIQUERY *view;
3544 static const WCHAR query[] =
3545 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3546 '`','S','h','o','r','t','c','u','t','`',0};
3548 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3549 if (rc != ERROR_SUCCESS)
3550 return ERROR_SUCCESS;
3552 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3553 msiobj_release( &view->hdr );
3555 return rc;
3558 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3560 MSIPACKAGE* package = param;
3561 HANDLE the_file;
3562 LPWSTR FilePath;
3563 LPCWSTR FileName;
3564 CHAR buffer[1024];
3565 DWORD sz;
3566 UINT rc;
3568 FileName = MSI_RecordGetString(row,1);
3569 if (!FileName)
3571 ERR("Unable to get FileName\n");
3572 return ERROR_SUCCESS;
3575 FilePath = build_icon_path(package,FileName);
3577 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3579 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3580 FILE_ATTRIBUTE_NORMAL, NULL);
3582 if (the_file == INVALID_HANDLE_VALUE)
3584 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3585 msi_free(FilePath);
3586 return ERROR_SUCCESS;
3591 DWORD write;
3592 sz = 1024;
3593 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3594 if (rc != ERROR_SUCCESS)
3596 ERR("Failed to get stream\n");
3597 CloseHandle(the_file);
3598 DeleteFileW(FilePath);
3599 break;
3601 WriteFile(the_file,buffer,sz,&write,NULL);
3602 } while (sz == 1024);
3604 msi_free(FilePath);
3605 CloseHandle(the_file);
3607 return ERROR_SUCCESS;
3610 static UINT msi_publish_icons(MSIPACKAGE *package)
3612 UINT r;
3613 MSIQUERY *view;
3615 static const WCHAR query[]= {
3616 'S','E','L','E','C','T',' ','*',' ',
3617 'F','R','O','M',' ','`','I','c','o','n','`',0};
3619 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3620 if (r == ERROR_SUCCESS)
3622 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3623 msiobj_release(&view->hdr);
3626 return ERROR_SUCCESS;
3629 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3631 UINT r;
3632 HKEY source;
3633 LPWSTR buffer;
3634 MSIMEDIADISK *disk;
3635 MSISOURCELISTINFO *info;
3637 r = RegCreateKeyW(hkey, szSourceList, &source);
3638 if (r != ERROR_SUCCESS)
3639 return r;
3641 RegCloseKey(source);
3643 buffer = strrchrW(package->PackagePath, '\\') + 1;
3644 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3645 package->Context, MSICODE_PRODUCT,
3646 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3647 if (r != ERROR_SUCCESS)
3648 return r;
3650 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3651 package->Context, MSICODE_PRODUCT,
3652 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3653 if (r != ERROR_SUCCESS)
3654 return r;
3656 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3657 package->Context, MSICODE_PRODUCT,
3658 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3659 if (r != ERROR_SUCCESS)
3660 return r;
3662 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3664 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3665 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3666 info->options, info->value);
3667 else
3668 MsiSourceListSetInfoW(package->ProductCode, NULL,
3669 info->context, info->options,
3670 info->property, info->value);
3673 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3675 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3676 disk->context, disk->options,
3677 disk->disk_id, disk->volume_label, disk->disk_prompt);
3680 return ERROR_SUCCESS;
3683 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3685 MSIHANDLE hdb, suminfo;
3686 WCHAR guids[MAX_PATH];
3687 WCHAR packcode[SQUISH_GUID_SIZE];
3688 LPWSTR buffer;
3689 LPWSTR ptr;
3690 DWORD langid;
3691 DWORD size;
3692 UINT r;
3694 static const WCHAR szProductLanguage[] =
3695 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3696 static const WCHAR szARPProductIcon[] =
3697 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3698 static const WCHAR szProductVersion[] =
3699 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3700 static const WCHAR szAssignment[] =
3701 {'A','s','s','i','g','n','m','e','n','t',0};
3702 static const WCHAR szAdvertiseFlags[] =
3703 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3704 static const WCHAR szClients[] =
3705 {'C','l','i','e','n','t','s',0};
3706 static const WCHAR szColon[] = {':',0};
3708 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3709 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3710 msi_free(buffer);
3712 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3713 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3715 /* FIXME */
3716 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3718 buffer = msi_dup_property(package->db, szARPProductIcon);
3719 if (buffer)
3721 LPWSTR path = build_icon_path(package,buffer);
3722 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3723 msi_free(path);
3724 msi_free(buffer);
3727 buffer = msi_dup_property(package->db, szProductVersion);
3728 if (buffer)
3730 DWORD verdword = msi_version_str_to_dword(buffer);
3731 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3732 msi_free(buffer);
3735 msi_reg_set_val_dword(hkey, szAssignment, 0);
3736 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3737 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3738 msi_reg_set_val_str(hkey, szClients, szColon);
3740 hdb = alloc_msihandle(&package->db->hdr);
3741 if (!hdb)
3742 return ERROR_NOT_ENOUGH_MEMORY;
3744 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3745 MsiCloseHandle(hdb);
3746 if (r != ERROR_SUCCESS)
3747 goto done;
3749 size = MAX_PATH;
3750 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3751 NULL, guids, &size);
3752 if (r != ERROR_SUCCESS)
3753 goto done;
3755 ptr = strchrW(guids, ';');
3756 if (ptr) *ptr = 0;
3757 squash_guid(guids, packcode);
3758 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3760 done:
3761 MsiCloseHandle(suminfo);
3762 return ERROR_SUCCESS;
3765 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3767 UINT r;
3768 HKEY hkey;
3769 LPWSTR upgrade;
3770 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3772 static const WCHAR szUpgradeCode[] =
3773 {'U','p','g','r','a','d','e','C','o','d','e',0};
3775 upgrade = msi_dup_property(package->db, szUpgradeCode);
3776 if (!upgrade)
3777 return ERROR_SUCCESS;
3779 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3781 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3782 if (r != ERROR_SUCCESS)
3783 goto done;
3785 else
3787 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3788 if (r != ERROR_SUCCESS)
3789 goto done;
3792 squash_guid(package->ProductCode, squashed_pc);
3793 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3795 RegCloseKey(hkey);
3797 done:
3798 msi_free(upgrade);
3799 return r;
3802 static BOOL msi_check_publish(MSIPACKAGE *package)
3804 MSIFEATURE *feature;
3806 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3808 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3809 return TRUE;
3812 return FALSE;
3815 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3817 MSIFEATURE *feature;
3819 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3821 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3822 return FALSE;
3825 return TRUE;
3828 static UINT msi_publish_patches( MSIPACKAGE *package, HKEY prodkey )
3830 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
3831 WCHAR patch_squashed[GUID_SIZE];
3832 HKEY patches_key = NULL, product_patches_key;
3833 LONG res;
3834 MSIPATCHINFO *patch;
3835 UINT r;
3836 WCHAR *p, *all_patches = NULL;
3837 DWORD len = 0;
3839 res = RegCreateKeyExW( prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
3840 if (res != ERROR_SUCCESS)
3841 return ERROR_FUNCTION_FAILED;
3843 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
3844 if (r != ERROR_SUCCESS)
3845 goto done;
3847 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3849 squash_guid( patch->patchcode, patch_squashed );
3850 len += strlenW( patch_squashed ) + 1;
3853 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
3854 if (!all_patches)
3855 goto done;
3857 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3859 HKEY patch_key;
3861 squash_guid( patch->patchcode, p );
3862 p += strlenW( p ) + 1;
3864 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
3865 (const BYTE *)patch->transforms,
3866 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
3867 if (res != ERROR_SUCCESS)
3868 goto done;
3870 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
3871 if (r != ERROR_SUCCESS)
3872 goto done;
3874 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
3875 (const BYTE *)patch->localfile,
3876 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
3877 RegCloseKey( patch_key );
3878 if (res != ERROR_SUCCESS)
3879 goto done;
3881 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
3882 RegCloseKey( patch_key );
3883 if (res != ERROR_SUCCESS)
3884 goto done;
3887 all_patches[len] = 0;
3888 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
3889 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3890 if (res != ERROR_SUCCESS)
3891 goto done;
3893 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
3894 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3895 if (res != ERROR_SUCCESS)
3896 r = ERROR_FUNCTION_FAILED;
3898 done:
3899 RegCloseKey( product_patches_key );
3900 RegCloseKey( patches_key );
3901 msi_free( all_patches );
3902 return r;
3906 * 99% of the work done here is only done for
3907 * advertised installs. However this is where the
3908 * Icon table is processed and written out
3909 * so that is what I am going to do here.
3911 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3913 UINT rc;
3914 HKEY hukey = NULL, hudkey = NULL;
3915 MSIRECORD *uirow;
3917 /* FIXME: also need to publish if the product is in advertise mode */
3918 if (!msi_check_publish(package))
3919 return ERROR_SUCCESS;
3921 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3922 &hukey, TRUE);
3923 if (rc != ERROR_SUCCESS)
3924 goto end;
3926 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3927 NULL, &hudkey, TRUE);
3928 if (rc != ERROR_SUCCESS)
3929 goto end;
3931 rc = msi_publish_upgrade_code(package);
3932 if (rc != ERROR_SUCCESS)
3933 goto end;
3935 if (!list_empty(&package->patches))
3937 rc = msi_publish_patches(package, hukey);
3938 if (rc != ERROR_SUCCESS)
3939 goto end;
3942 rc = msi_publish_product_properties(package, hukey);
3943 if (rc != ERROR_SUCCESS)
3944 goto end;
3946 rc = msi_publish_sourcelist(package, hukey);
3947 if (rc != ERROR_SUCCESS)
3948 goto end;
3950 rc = msi_publish_icons(package);
3952 end:
3953 uirow = MSI_CreateRecord( 1 );
3954 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
3955 ui_actiondata( package, szPublishProduct, uirow );
3956 msiobj_release( &uirow->hdr );
3958 RegCloseKey(hukey);
3959 RegCloseKey(hudkey);
3961 return rc;
3964 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
3966 WCHAR *filename, *ptr, *folder, *ret;
3967 const WCHAR *dirprop;
3969 filename = msi_dup_record_field( row, 2 );
3970 if (filename && (ptr = strchrW( filename, '|' )))
3971 ptr++;
3972 else
3973 ptr = filename;
3975 dirprop = MSI_RecordGetString( row, 3 );
3976 if (dirprop)
3978 folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
3979 if (!folder)
3980 folder = msi_dup_property( package->db, dirprop );
3982 else
3983 folder = msi_dup_property( package->db, szWindowsFolder );
3985 if (!folder)
3987 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
3988 msi_free( filename );
3989 return NULL;
3992 ret = build_directory_name( 2, folder, ptr );
3994 msi_free( filename );
3995 msi_free( folder );
3996 return ret;
3999 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4001 MSIPACKAGE *package = param;
4002 LPCWSTR component, section, key, value, identifier;
4003 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4004 MSIRECORD * uirow;
4005 INT action;
4006 MSICOMPONENT *comp;
4008 component = MSI_RecordGetString(row, 8);
4009 comp = get_loaded_component(package,component);
4010 if (!comp)
4011 return ERROR_SUCCESS;
4013 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4015 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4016 comp->Action = comp->Installed;
4017 return ERROR_SUCCESS;
4019 comp->Action = INSTALLSTATE_LOCAL;
4021 identifier = MSI_RecordGetString(row,1);
4022 section = MSI_RecordGetString(row,4);
4023 key = MSI_RecordGetString(row,5);
4024 value = MSI_RecordGetString(row,6);
4025 action = MSI_RecordGetInteger(row,7);
4027 deformat_string(package,section,&deformated_section);
4028 deformat_string(package,key,&deformated_key);
4029 deformat_string(package,value,&deformated_value);
4031 fullname = get_ini_file_name(package, row);
4033 if (action == 0)
4035 TRACE("Adding value %s to section %s in %s\n",
4036 debugstr_w(deformated_key), debugstr_w(deformated_section),
4037 debugstr_w(fullname));
4038 WritePrivateProfileStringW(deformated_section, deformated_key,
4039 deformated_value, fullname);
4041 else if (action == 1)
4043 WCHAR returned[10];
4044 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4045 returned, 10, fullname);
4046 if (returned[0] == 0)
4048 TRACE("Adding value %s to section %s in %s\n",
4049 debugstr_w(deformated_key), debugstr_w(deformated_section),
4050 debugstr_w(fullname));
4052 WritePrivateProfileStringW(deformated_section, deformated_key,
4053 deformated_value, fullname);
4056 else if (action == 3)
4057 FIXME("Append to existing section not yet implemented\n");
4059 uirow = MSI_CreateRecord(4);
4060 MSI_RecordSetStringW(uirow,1,identifier);
4061 MSI_RecordSetStringW(uirow,2,deformated_section);
4062 MSI_RecordSetStringW(uirow,3,deformated_key);
4063 MSI_RecordSetStringW(uirow,4,deformated_value);
4064 ui_actiondata(package,szWriteIniValues,uirow);
4065 msiobj_release( &uirow->hdr );
4067 msi_free(fullname);
4068 msi_free(deformated_key);
4069 msi_free(deformated_value);
4070 msi_free(deformated_section);
4071 return ERROR_SUCCESS;
4074 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4076 UINT rc;
4077 MSIQUERY * view;
4078 static const WCHAR ExecSeqQuery[] =
4079 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4080 '`','I','n','i','F','i','l','e','`',0};
4082 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4083 if (rc != ERROR_SUCCESS)
4085 TRACE("no IniFile table\n");
4086 return ERROR_SUCCESS;
4089 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4090 msiobj_release(&view->hdr);
4091 return rc;
4094 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4096 MSIPACKAGE *package = param;
4097 LPCWSTR component, section, key, value, identifier;
4098 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4099 MSICOMPONENT *comp;
4100 MSIRECORD *uirow;
4101 INT action;
4103 component = MSI_RecordGetString( row, 8 );
4104 comp = get_loaded_component( package, component );
4105 if (!comp)
4106 return ERROR_SUCCESS;
4108 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4110 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4111 comp->Action = comp->Installed;
4112 return ERROR_SUCCESS;
4114 comp->Action = INSTALLSTATE_ABSENT;
4116 identifier = MSI_RecordGetString( row, 1 );
4117 section = MSI_RecordGetString( row, 4 );
4118 key = MSI_RecordGetString( row, 5 );
4119 value = MSI_RecordGetString( row, 6 );
4120 action = MSI_RecordGetInteger( row, 7 );
4122 deformat_string( package, section, &deformated_section );
4123 deformat_string( package, key, &deformated_key );
4124 deformat_string( package, value, &deformated_value );
4126 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4128 filename = get_ini_file_name( package, row );
4130 TRACE("Removing key %s from section %s in %s\n",
4131 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4133 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4135 WARN("Unable to remove key %u\n", GetLastError());
4137 msi_free( filename );
4139 else
4140 FIXME("Unsupported action %d\n", action);
4143 uirow = MSI_CreateRecord( 4 );
4144 MSI_RecordSetStringW( uirow, 1, identifier );
4145 MSI_RecordSetStringW( uirow, 2, deformated_section );
4146 MSI_RecordSetStringW( uirow, 3, deformated_key );
4147 MSI_RecordSetStringW( uirow, 4, deformated_value );
4148 ui_actiondata( package, szRemoveIniValues, uirow );
4149 msiobj_release( &uirow->hdr );
4151 msi_free( deformated_key );
4152 msi_free( deformated_value );
4153 msi_free( deformated_section );
4154 return ERROR_SUCCESS;
4157 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4159 MSIPACKAGE *package = param;
4160 LPCWSTR component, section, key, value, identifier;
4161 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4162 MSICOMPONENT *comp;
4163 MSIRECORD *uirow;
4164 INT action;
4166 component = MSI_RecordGetString( row, 8 );
4167 comp = get_loaded_component( package, component );
4168 if (!comp)
4169 return ERROR_SUCCESS;
4171 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4173 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4174 comp->Action = comp->Installed;
4175 return ERROR_SUCCESS;
4177 comp->Action = INSTALLSTATE_LOCAL;
4179 identifier = MSI_RecordGetString( row, 1 );
4180 section = MSI_RecordGetString( row, 4 );
4181 key = MSI_RecordGetString( row, 5 );
4182 value = MSI_RecordGetString( row, 6 );
4183 action = MSI_RecordGetInteger( row, 7 );
4185 deformat_string( package, section, &deformated_section );
4186 deformat_string( package, key, &deformated_key );
4187 deformat_string( package, value, &deformated_value );
4189 if (action == msidbIniFileActionRemoveLine)
4191 filename = get_ini_file_name( package, row );
4193 TRACE("Removing key %s from section %s in %s\n",
4194 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4196 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4198 WARN("Unable to remove key %u\n", GetLastError());
4200 msi_free( filename );
4202 else
4203 FIXME("Unsupported action %d\n", action);
4205 uirow = MSI_CreateRecord( 4 );
4206 MSI_RecordSetStringW( uirow, 1, identifier );
4207 MSI_RecordSetStringW( uirow, 2, deformated_section );
4208 MSI_RecordSetStringW( uirow, 3, deformated_key );
4209 MSI_RecordSetStringW( uirow, 4, deformated_value );
4210 ui_actiondata( package, szRemoveIniValues, uirow );
4211 msiobj_release( &uirow->hdr );
4213 msi_free( deformated_key );
4214 msi_free( deformated_value );
4215 msi_free( deformated_section );
4216 return ERROR_SUCCESS;
4219 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4221 UINT rc;
4222 MSIQUERY *view;
4223 static const WCHAR query[] =
4224 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4225 '`','I','n','i','F','i','l','e','`',0};
4226 static const WCHAR remove_query[] =
4227 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4228 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4230 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4231 if (rc == ERROR_SUCCESS)
4233 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4234 msiobj_release( &view->hdr );
4235 if (rc != ERROR_SUCCESS)
4236 return rc;
4239 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4240 if (rc == ERROR_SUCCESS)
4242 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4243 msiobj_release( &view->hdr );
4244 if (rc != ERROR_SUCCESS)
4245 return rc;
4248 return ERROR_SUCCESS;
4251 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4253 MSIPACKAGE *package = param;
4254 LPCWSTR filename;
4255 LPWSTR FullName;
4256 MSIFILE *file;
4257 DWORD len;
4258 static const WCHAR ExeStr[] =
4259 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
4260 static const WCHAR close[] = {'\"',0};
4261 STARTUPINFOW si;
4262 PROCESS_INFORMATION info;
4263 BOOL brc;
4264 MSIRECORD *uirow;
4266 memset(&si,0,sizeof(STARTUPINFOW));
4268 filename = MSI_RecordGetString(row,1);
4269 file = get_loaded_file( package, filename );
4271 if (!file)
4273 ERR("Unable to find file id %s\n",debugstr_w(filename));
4274 return ERROR_SUCCESS;
4277 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
4279 FullName = msi_alloc(len*sizeof(WCHAR));
4280 strcpyW(FullName,ExeStr);
4281 strcatW( FullName, file->TargetPath );
4282 strcatW(FullName,close);
4284 TRACE("Registering %s\n",debugstr_w(FullName));
4285 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
4286 &si, &info);
4288 if (brc)
4290 CloseHandle(info.hThread);
4291 msi_dialog_check_messages(info.hProcess);
4292 CloseHandle(info.hProcess);
4295 uirow = MSI_CreateRecord( 2 );
4296 MSI_RecordSetStringW( uirow, 1, filename );
4297 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4298 ui_actiondata( package, szSelfRegModules, uirow );
4299 msiobj_release( &uirow->hdr );
4301 msi_free( FullName );
4302 return ERROR_SUCCESS;
4305 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4307 UINT rc;
4308 MSIQUERY * view;
4309 static const WCHAR ExecSeqQuery[] =
4310 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4311 '`','S','e','l','f','R','e','g','`',0};
4313 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4314 if (rc != ERROR_SUCCESS)
4316 TRACE("no SelfReg table\n");
4317 return ERROR_SUCCESS;
4320 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4321 msiobj_release(&view->hdr);
4323 return ERROR_SUCCESS;
4326 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4328 static const WCHAR regsvr32[] =
4329 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
4330 static const WCHAR close[] = {'\"',0};
4331 MSIPACKAGE *package = param;
4332 LPCWSTR filename;
4333 LPWSTR cmdline;
4334 MSIFILE *file;
4335 DWORD len;
4336 STARTUPINFOW si;
4337 PROCESS_INFORMATION pi;
4338 BOOL ret;
4339 MSIRECORD *uirow;
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 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4372 ui_actiondata( package, szSelfUnregModules, uirow );
4373 msiobj_release( &uirow->hdr );
4375 msi_free( cmdline );
4376 return ERROR_SUCCESS;
4379 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4381 UINT rc;
4382 MSIQUERY *view;
4383 static const WCHAR query[] =
4384 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4385 '`','S','e','l','f','R','e','g','`',0};
4387 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4388 if (rc != ERROR_SUCCESS)
4390 TRACE("no SelfReg table\n");
4391 return ERROR_SUCCESS;
4394 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4395 msiobj_release( &view->hdr );
4397 return ERROR_SUCCESS;
4400 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4402 MSIFEATURE *feature;
4403 UINT rc;
4404 HKEY hkey = NULL, userdata = NULL;
4406 if (!msi_check_publish(package))
4407 return ERROR_SUCCESS;
4409 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4410 &hkey, TRUE);
4411 if (rc != ERROR_SUCCESS)
4412 goto end;
4414 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4415 &userdata, TRUE);
4416 if (rc != ERROR_SUCCESS)
4417 goto end;
4419 /* here the guids are base 85 encoded */
4420 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4422 ComponentList *cl;
4423 LPWSTR data = NULL;
4424 GUID clsid;
4425 INT size;
4426 BOOL absent = FALSE;
4427 MSIRECORD *uirow;
4429 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4430 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4431 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4433 size = 1;
4434 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4436 size += 21;
4438 if (feature->Feature_Parent)
4439 size += strlenW( feature->Feature_Parent )+2;
4441 data = msi_alloc(size * sizeof(WCHAR));
4443 data[0] = 0;
4444 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4446 MSICOMPONENT* component = cl->component;
4447 WCHAR buf[21];
4449 buf[0] = 0;
4450 if (component->ComponentId)
4452 TRACE("From %s\n",debugstr_w(component->ComponentId));
4453 CLSIDFromString(component->ComponentId, &clsid);
4454 encode_base85_guid(&clsid,buf);
4455 TRACE("to %s\n",debugstr_w(buf));
4456 strcatW(data,buf);
4460 if (feature->Feature_Parent)
4462 static const WCHAR sep[] = {'\2',0};
4463 strcatW(data,sep);
4464 strcatW(data,feature->Feature_Parent);
4467 msi_reg_set_val_str( userdata, feature->Feature, data );
4468 msi_free(data);
4470 size = 0;
4471 if (feature->Feature_Parent)
4472 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4473 if (!absent)
4475 size += sizeof(WCHAR);
4476 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4477 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4479 else
4481 size += 2*sizeof(WCHAR);
4482 data = msi_alloc(size);
4483 data[0] = 0x6;
4484 data[1] = 0;
4485 if (feature->Feature_Parent)
4486 strcpyW( &data[1], feature->Feature_Parent );
4487 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4488 (LPBYTE)data,size);
4489 msi_free(data);
4492 /* the UI chunk */
4493 uirow = MSI_CreateRecord( 1 );
4494 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4495 ui_actiondata( package, szPublishFeatures, uirow);
4496 msiobj_release( &uirow->hdr );
4497 /* FIXME: call ui_progress? */
4500 end:
4501 RegCloseKey(hkey);
4502 RegCloseKey(userdata);
4503 return rc;
4506 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4508 UINT r;
4509 HKEY hkey;
4510 MSIRECORD *uirow;
4512 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4514 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4515 &hkey, FALSE);
4516 if (r == ERROR_SUCCESS)
4518 RegDeleteValueW(hkey, feature->Feature);
4519 RegCloseKey(hkey);
4522 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4523 &hkey, FALSE);
4524 if (r == ERROR_SUCCESS)
4526 RegDeleteValueW(hkey, feature->Feature);
4527 RegCloseKey(hkey);
4530 uirow = MSI_CreateRecord( 1 );
4531 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4532 ui_actiondata( package, szUnpublishFeatures, uirow );
4533 msiobj_release( &uirow->hdr );
4535 return ERROR_SUCCESS;
4538 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4540 MSIFEATURE *feature;
4542 if (!msi_check_unpublish(package))
4543 return ERROR_SUCCESS;
4545 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4547 msi_unpublish_feature(package, feature);
4550 return ERROR_SUCCESS;
4553 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4555 SYSTEMTIME systime;
4556 DWORD size, langid;
4557 WCHAR date[9], *val, *buffer;
4558 const WCHAR *prop, *key;
4560 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4561 static const WCHAR szWindowsInstaller[] =
4562 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4563 static const WCHAR modpath_fmt[] =
4564 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4565 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4566 static const WCHAR szModifyPath[] =
4567 {'M','o','d','i','f','y','P','a','t','h',0};
4568 static const WCHAR szUninstallString[] =
4569 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4570 static const WCHAR szEstimatedSize[] =
4571 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4572 static const WCHAR szProductLanguage[] =
4573 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4574 static const WCHAR szProductVersion[] =
4575 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4576 static const WCHAR szDisplayVersion[] =
4577 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4578 static const WCHAR szInstallSource[] =
4579 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4580 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4581 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4582 static const WCHAR szAuthorizedCDFPrefix[] =
4583 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4584 static const WCHAR szARPCONTACT[] =
4585 {'A','R','P','C','O','N','T','A','C','T',0};
4586 static const WCHAR szContact[] =
4587 {'C','o','n','t','a','c','t',0};
4588 static const WCHAR szARPCOMMENTS[] =
4589 {'A','R','P','C','O','M','M','E','N','T','S',0};
4590 static const WCHAR szComments[] =
4591 {'C','o','m','m','e','n','t','s',0};
4592 static const WCHAR szProductName[] =
4593 {'P','r','o','d','u','c','t','N','a','m','e',0};
4594 static const WCHAR szDisplayName[] =
4595 {'D','i','s','p','l','a','y','N','a','m','e',0};
4596 static const WCHAR szARPHELPLINK[] =
4597 {'A','R','P','H','E','L','P','L','I','N','K',0};
4598 static const WCHAR szHelpLink[] =
4599 {'H','e','l','p','L','i','n','k',0};
4600 static const WCHAR szARPHELPTELEPHONE[] =
4601 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4602 static const WCHAR szHelpTelephone[] =
4603 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4604 static const WCHAR szARPINSTALLLOCATION[] =
4605 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4606 static const WCHAR szInstallLocation[] =
4607 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4608 static const WCHAR szManufacturer[] =
4609 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4610 static const WCHAR szPublisher[] =
4611 {'P','u','b','l','i','s','h','e','r',0};
4612 static const WCHAR szARPREADME[] =
4613 {'A','R','P','R','E','A','D','M','E',0};
4614 static const WCHAR szReadme[] =
4615 {'R','e','a','d','M','e',0};
4616 static const WCHAR szARPSIZE[] =
4617 {'A','R','P','S','I','Z','E',0};
4618 static const WCHAR szSize[] =
4619 {'S','i','z','e',0};
4620 static const WCHAR szARPURLINFOABOUT[] =
4621 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4622 static const WCHAR szURLInfoAbout[] =
4623 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4624 static const WCHAR szARPURLUPDATEINFO[] =
4625 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4626 static const WCHAR szURLUpdateInfo[] =
4627 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4629 static const WCHAR *propval[] = {
4630 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4631 szARPCONTACT, szContact,
4632 szARPCOMMENTS, szComments,
4633 szProductName, szDisplayName,
4634 szARPHELPLINK, szHelpLink,
4635 szARPHELPTELEPHONE, szHelpTelephone,
4636 szARPINSTALLLOCATION, szInstallLocation,
4637 cszSourceDir, szInstallSource,
4638 szManufacturer, szPublisher,
4639 szARPREADME, szReadme,
4640 szARPSIZE, szSize,
4641 szARPURLINFOABOUT, szURLInfoAbout,
4642 szARPURLUPDATEINFO, szURLUpdateInfo,
4643 NULL
4645 const WCHAR **p = propval;
4647 while (*p)
4649 prop = *p++;
4650 key = *p++;
4651 val = msi_dup_property(package->db, prop);
4652 msi_reg_set_val_str(hkey, key, val);
4653 msi_free(val);
4656 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4658 size = deformat_string(package, modpath_fmt, &buffer);
4659 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4660 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4661 msi_free(buffer);
4663 /* FIXME: Write real Estimated Size when we have it */
4664 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4666 GetLocalTime(&systime);
4667 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4668 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4670 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4671 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4673 buffer = msi_dup_property(package->db, szProductVersion);
4674 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4675 if (buffer)
4677 DWORD verdword = msi_version_str_to_dword(buffer);
4679 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4680 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4681 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4682 msi_free(buffer);
4685 return ERROR_SUCCESS;
4688 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4690 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4691 MSIRECORD *uirow;
4692 LPWSTR upgrade_code;
4693 HKEY hkey, props;
4694 HKEY upgrade;
4695 UINT rc;
4697 static const WCHAR szUpgradeCode[] = {
4698 'U','p','g','r','a','d','e','C','o','d','e',0};
4700 /* FIXME: also need to publish if the product is in advertise mode */
4701 if (!msi_check_publish(package))
4702 return ERROR_SUCCESS;
4704 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4705 if (rc != ERROR_SUCCESS)
4706 return rc;
4708 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4709 NULL, &props, TRUE);
4710 if (rc != ERROR_SUCCESS)
4711 goto done;
4713 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4714 msi_free( package->db->localfile );
4715 package->db->localfile = NULL;
4717 rc = msi_publish_install_properties(package, hkey);
4718 if (rc != ERROR_SUCCESS)
4719 goto done;
4721 rc = msi_publish_install_properties(package, props);
4722 if (rc != ERROR_SUCCESS)
4723 goto done;
4725 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4726 if (upgrade_code)
4728 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4729 squash_guid(package->ProductCode, squashed_pc);
4730 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4731 RegCloseKey(upgrade);
4732 msi_free(upgrade_code);
4735 done:
4736 uirow = MSI_CreateRecord( 1 );
4737 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4738 ui_actiondata( package, szRegisterProduct, uirow );
4739 msiobj_release( &uirow->hdr );
4741 RegCloseKey(hkey);
4742 return ERROR_SUCCESS;
4745 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4747 return execute_script(package,INSTALL_SCRIPT);
4750 static UINT msi_unpublish_product(MSIPACKAGE *package)
4752 LPWSTR upgrade;
4753 LPWSTR remove = NULL;
4754 LPWSTR *features = NULL;
4755 BOOL full_uninstall = TRUE;
4756 MSIFEATURE *feature;
4757 MSIPATCHINFO *patch;
4759 static const WCHAR szUpgradeCode[] =
4760 {'U','p','g','r','a','d','e','C','o','d','e',0};
4762 remove = msi_dup_property(package->db, szRemove);
4763 if (!remove)
4764 return ERROR_SUCCESS;
4766 features = msi_split_string(remove, ',');
4767 if (!features)
4769 msi_free(remove);
4770 ERR("REMOVE feature list is empty!\n");
4771 return ERROR_FUNCTION_FAILED;
4774 if (!lstrcmpW(features[0], szAll))
4775 full_uninstall = TRUE;
4776 else
4778 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4780 if (feature->Action != INSTALLSTATE_ABSENT)
4781 full_uninstall = FALSE;
4785 if (!full_uninstall)
4786 goto done;
4788 MSIREG_DeleteProductKey(package->ProductCode);
4789 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4790 MSIREG_DeleteUninstallKey(package->ProductCode);
4792 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4794 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4795 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4797 else
4799 MSIREG_DeleteUserProductKey(package->ProductCode);
4800 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4803 upgrade = msi_dup_property(package->db, szUpgradeCode);
4804 if (upgrade)
4806 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4807 msi_free(upgrade);
4810 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4812 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4815 done:
4816 msi_free(remove);
4817 msi_free(features);
4818 return ERROR_SUCCESS;
4821 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4823 UINT rc;
4825 rc = msi_unpublish_product(package);
4826 if (rc != ERROR_SUCCESS)
4827 return rc;
4829 /* turn off scheduling */
4830 package->script->CurrentlyScripting= FALSE;
4832 /* first do the same as an InstallExecute */
4833 rc = ACTION_InstallExecute(package);
4834 if (rc != ERROR_SUCCESS)
4835 return rc;
4837 /* then handle Commit Actions */
4838 rc = execute_script(package,COMMIT_SCRIPT);
4840 return rc;
4843 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4845 static const WCHAR RunOnce[] = {
4846 'S','o','f','t','w','a','r','e','\\',
4847 'M','i','c','r','o','s','o','f','t','\\',
4848 'W','i','n','d','o','w','s','\\',
4849 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4850 'R','u','n','O','n','c','e',0};
4851 static const WCHAR InstallRunOnce[] = {
4852 'S','o','f','t','w','a','r','e','\\',
4853 'M','i','c','r','o','s','o','f','t','\\',
4854 'W','i','n','d','o','w','s','\\',
4855 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4856 'I','n','s','t','a','l','l','e','r','\\',
4857 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4859 static const WCHAR msiexec_fmt[] = {
4860 '%','s',
4861 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4862 '\"','%','s','\"',0};
4863 static const WCHAR install_fmt[] = {
4864 '/','I',' ','\"','%','s','\"',' ',
4865 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4866 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4867 WCHAR buffer[256], sysdir[MAX_PATH];
4868 HKEY hkey;
4869 WCHAR squished_pc[100];
4871 squash_guid(package->ProductCode,squished_pc);
4873 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4874 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4875 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4876 squished_pc);
4878 msi_reg_set_val_str( hkey, squished_pc, buffer );
4879 RegCloseKey(hkey);
4881 TRACE("Reboot command %s\n",debugstr_w(buffer));
4883 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4884 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4886 msi_reg_set_val_str( hkey, squished_pc, buffer );
4887 RegCloseKey(hkey);
4889 return ERROR_INSTALL_SUSPEND;
4892 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4894 DWORD attrib;
4895 UINT rc;
4898 * We are currently doing what should be done here in the top level Install
4899 * however for Administrative and uninstalls this step will be needed
4901 if (!package->PackagePath)
4902 return ERROR_SUCCESS;
4904 msi_set_sourcedir_props(package, TRUE);
4906 attrib = GetFileAttributesW(package->db->path);
4907 if (attrib == INVALID_FILE_ATTRIBUTES)
4909 LPWSTR prompt;
4910 LPWSTR msg;
4911 DWORD size = 0;
4913 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4914 package->Context, MSICODE_PRODUCT,
4915 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4916 if (rc == ERROR_MORE_DATA)
4918 prompt = msi_alloc(size * sizeof(WCHAR));
4919 MsiSourceListGetInfoW(package->ProductCode, NULL,
4920 package->Context, MSICODE_PRODUCT,
4921 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4923 else
4924 prompt = strdupW(package->db->path);
4926 msg = generate_error_string(package,1302,1,prompt);
4927 while(attrib == INVALID_FILE_ATTRIBUTES)
4929 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4930 if (rc == IDCANCEL)
4932 rc = ERROR_INSTALL_USEREXIT;
4933 break;
4935 attrib = GetFileAttributesW(package->db->path);
4937 msi_free(prompt);
4938 rc = ERROR_SUCCESS;
4940 else
4941 return ERROR_SUCCESS;
4943 return rc;
4946 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4948 HKEY hkey = 0;
4949 LPWSTR buffer, productid = NULL;
4950 UINT i, rc = ERROR_SUCCESS;
4951 MSIRECORD *uirow;
4953 static const WCHAR szPropKeys[][80] =
4955 {'P','r','o','d','u','c','t','I','D',0},
4956 {'U','S','E','R','N','A','M','E',0},
4957 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4958 {0},
4961 static const WCHAR szRegKeys[][80] =
4963 {'P','r','o','d','u','c','t','I','D',0},
4964 {'R','e','g','O','w','n','e','r',0},
4965 {'R','e','g','C','o','m','p','a','n','y',0},
4966 {0},
4969 if (msi_check_unpublish(package))
4971 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4972 goto end;
4975 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
4976 if (!productid)
4977 goto end;
4979 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4980 NULL, &hkey, TRUE);
4981 if (rc != ERROR_SUCCESS)
4982 goto end;
4984 for( i = 0; szPropKeys[i][0]; i++ )
4986 buffer = msi_dup_property( package->db, szPropKeys[i] );
4987 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4988 msi_free( buffer );
4991 end:
4992 uirow = MSI_CreateRecord( 1 );
4993 MSI_RecordSetStringW( uirow, 1, productid );
4994 ui_actiondata( package, szRegisterUser, uirow );
4995 msiobj_release( &uirow->hdr );
4997 msi_free(productid);
4998 RegCloseKey(hkey);
4999 return rc;
5003 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5005 UINT rc;
5007 package->script->InWhatSequence |= SEQUENCE_EXEC;
5008 rc = ACTION_ProcessExecSequence(package,FALSE);
5009 return rc;
5013 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5015 MSIPACKAGE *package = param;
5016 LPCWSTR compgroupid, component, feature, qualifier, text;
5017 LPWSTR advertise = NULL, output = NULL;
5018 HKEY hkey = NULL;
5019 UINT rc;
5020 MSICOMPONENT *comp;
5021 MSIFEATURE *feat;
5022 DWORD sz;
5023 MSIRECORD *uirow;
5025 feature = MSI_RecordGetString(rec, 5);
5026 feat = get_loaded_feature(package, feature);
5027 if (!feat)
5028 return ERROR_SUCCESS;
5030 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
5031 feat->ActionRequest != INSTALLSTATE_SOURCE &&
5032 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
5034 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
5035 feat->Action = feat->Installed;
5036 return ERROR_SUCCESS;
5039 component = MSI_RecordGetString(rec, 3);
5040 comp = get_loaded_component(package, component);
5041 if (!comp)
5042 return ERROR_SUCCESS;
5044 compgroupid = MSI_RecordGetString(rec,1);
5045 qualifier = MSI_RecordGetString(rec,2);
5047 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5048 if (rc != ERROR_SUCCESS)
5049 goto end;
5051 text = MSI_RecordGetString(rec,4);
5052 advertise = create_component_advertise_string(package, comp, feature);
5054 sz = strlenW(advertise);
5056 if (text)
5057 sz += lstrlenW(text);
5059 sz+=3;
5060 sz *= sizeof(WCHAR);
5062 output = msi_alloc_zero(sz);
5063 strcpyW(output,advertise);
5064 msi_free(advertise);
5066 if (text)
5067 strcatW(output,text);
5069 msi_reg_set_val_multi_str( hkey, qualifier, output );
5071 end:
5072 RegCloseKey(hkey);
5073 msi_free(output);
5075 /* the UI chunk */
5076 uirow = MSI_CreateRecord( 2 );
5077 MSI_RecordSetStringW( uirow, 1, compgroupid );
5078 MSI_RecordSetStringW( uirow, 2, qualifier);
5079 ui_actiondata( package, szPublishComponents, uirow);
5080 msiobj_release( &uirow->hdr );
5081 /* FIXME: call ui_progress? */
5083 return rc;
5087 * At present I am ignorning the advertised components part of this and only
5088 * focusing on the qualified component sets
5090 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5092 UINT rc;
5093 MSIQUERY * view;
5094 static const WCHAR ExecSeqQuery[] =
5095 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5096 '`','P','u','b','l','i','s','h',
5097 'C','o','m','p','o','n','e','n','t','`',0};
5099 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5100 if (rc != ERROR_SUCCESS)
5101 return ERROR_SUCCESS;
5103 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5104 msiobj_release(&view->hdr);
5106 return rc;
5109 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5111 static const WCHAR szInstallerComponents[] = {
5112 'S','o','f','t','w','a','r','e','\\',
5113 'M','i','c','r','o','s','o','f','t','\\',
5114 'I','n','s','t','a','l','l','e','r','\\',
5115 'C','o','m','p','o','n','e','n','t','s','\\',0};
5117 MSIPACKAGE *package = param;
5118 LPCWSTR compgroupid, component, feature, qualifier;
5119 MSICOMPONENT *comp;
5120 MSIFEATURE *feat;
5121 MSIRECORD *uirow;
5122 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5123 LONG res;
5125 feature = MSI_RecordGetString( rec, 5 );
5126 feat = get_loaded_feature( package, feature );
5127 if (!feat)
5128 return ERROR_SUCCESS;
5130 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5132 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5133 feat->Action = feat->Installed;
5134 return ERROR_SUCCESS;
5137 component = MSI_RecordGetString( rec, 3 );
5138 comp = get_loaded_component( package, component );
5139 if (!comp)
5140 return ERROR_SUCCESS;
5142 compgroupid = MSI_RecordGetString( rec, 1 );
5143 qualifier = MSI_RecordGetString( rec, 2 );
5145 squash_guid( compgroupid, squashed );
5146 strcpyW( keypath, szInstallerComponents );
5147 strcatW( keypath, squashed );
5149 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5150 if (res != ERROR_SUCCESS)
5152 WARN("Unable to delete component key %d\n", res);
5155 uirow = MSI_CreateRecord( 2 );
5156 MSI_RecordSetStringW( uirow, 1, compgroupid );
5157 MSI_RecordSetStringW( uirow, 2, qualifier );
5158 ui_actiondata( package, szUnpublishComponents, uirow );
5159 msiobj_release( &uirow->hdr );
5161 return ERROR_SUCCESS;
5164 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5166 UINT rc;
5167 MSIQUERY *view;
5168 static const WCHAR query[] =
5169 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5170 '`','P','u','b','l','i','s','h',
5171 'C','o','m','p','o','n','e','n','t','`',0};
5173 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5174 if (rc != ERROR_SUCCESS)
5175 return ERROR_SUCCESS;
5177 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5178 msiobj_release( &view->hdr );
5180 return rc;
5183 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5185 MSIPACKAGE *package = param;
5186 MSIRECORD *row;
5187 MSIFILE *file;
5188 SC_HANDLE hscm, service = NULL;
5189 LPCWSTR comp, depends, pass;
5190 LPWSTR name = NULL, disp = NULL;
5191 LPCWSTR load_order, serv_name, key;
5192 DWORD serv_type, start_type;
5193 DWORD err_control;
5195 static const WCHAR query[] =
5196 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5197 '`','C','o','m','p','o','n','e','n','t','`',' ',
5198 'W','H','E','R','E',' ',
5199 '`','C','o','m','p','o','n','e','n','t','`',' ',
5200 '=','\'','%','s','\'',0};
5202 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5203 if (!hscm)
5205 ERR("Failed to open the SC Manager!\n");
5206 goto done;
5209 start_type = MSI_RecordGetInteger(rec, 5);
5210 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5211 goto done;
5213 depends = MSI_RecordGetString(rec, 8);
5214 if (depends && *depends)
5215 FIXME("Dependency list unhandled!\n");
5217 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5218 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5219 serv_type = MSI_RecordGetInteger(rec, 4);
5220 err_control = MSI_RecordGetInteger(rec, 6);
5221 load_order = MSI_RecordGetString(rec, 7);
5222 serv_name = MSI_RecordGetString(rec, 9);
5223 pass = MSI_RecordGetString(rec, 10);
5224 comp = MSI_RecordGetString(rec, 12);
5226 /* fetch the service path */
5227 row = MSI_QueryGetRecord(package->db, query, comp);
5228 if (!row)
5230 ERR("Control query failed!\n");
5231 goto done;
5234 key = MSI_RecordGetString(row, 6);
5236 file = get_loaded_file(package, key);
5237 msiobj_release(&row->hdr);
5238 if (!file)
5240 ERR("Failed to load the service file\n");
5241 goto done;
5244 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5245 start_type, err_control, file->TargetPath,
5246 load_order, NULL, NULL, serv_name, pass);
5247 if (!service)
5249 if (GetLastError() != ERROR_SERVICE_EXISTS)
5250 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5253 done:
5254 CloseServiceHandle(service);
5255 CloseServiceHandle(hscm);
5256 msi_free(name);
5257 msi_free(disp);
5259 return ERROR_SUCCESS;
5262 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5264 UINT rc;
5265 MSIQUERY * view;
5266 static const WCHAR ExecSeqQuery[] =
5267 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5268 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5270 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5271 if (rc != ERROR_SUCCESS)
5272 return ERROR_SUCCESS;
5274 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5275 msiobj_release(&view->hdr);
5277 return rc;
5280 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5281 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5283 LPCWSTR *vector, *temp_vector;
5284 LPWSTR p, q;
5285 DWORD sep_len;
5287 static const WCHAR separator[] = {'[','~',']',0};
5289 *numargs = 0;
5290 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5292 if (!args)
5293 return NULL;
5295 vector = msi_alloc(sizeof(LPWSTR));
5296 if (!vector)
5297 return NULL;
5299 p = args;
5302 (*numargs)++;
5303 vector[*numargs - 1] = p;
5305 if ((q = strstrW(p, separator)))
5307 *q = '\0';
5309 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5310 if (!temp_vector)
5312 msi_free(vector);
5313 return NULL;
5315 vector = temp_vector;
5317 p = q + sep_len;
5319 } while (q);
5321 return vector;
5324 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5326 MSIPACKAGE *package = param;
5327 MSICOMPONENT *comp;
5328 MSIRECORD *uirow;
5329 SC_HANDLE scm = NULL, service = NULL;
5330 LPCWSTR component, *vector = NULL;
5331 LPWSTR name, args, display_name = NULL;
5332 DWORD event, numargs, len;
5333 UINT r = ERROR_FUNCTION_FAILED;
5335 component = MSI_RecordGetString(rec, 6);
5336 comp = get_loaded_component(package, component);
5337 if (!comp)
5338 return ERROR_SUCCESS;
5340 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5342 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5343 comp->Action = comp->Installed;
5344 return ERROR_SUCCESS;
5346 comp->Action = INSTALLSTATE_LOCAL;
5348 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5349 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5350 event = MSI_RecordGetInteger(rec, 3);
5352 if (!(event & msidbServiceControlEventStart))
5354 r = ERROR_SUCCESS;
5355 goto done;
5358 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5359 if (!scm)
5361 ERR("Failed to open the service control manager\n");
5362 goto done;
5365 len = 0;
5366 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5367 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5369 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5370 GetServiceDisplayNameW( scm, name, display_name, &len );
5373 service = OpenServiceW(scm, name, SERVICE_START);
5374 if (!service)
5376 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5377 goto done;
5380 vector = msi_service_args_to_vector(args, &numargs);
5382 if (!StartServiceW(service, numargs, vector) &&
5383 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5385 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5386 goto done;
5389 r = ERROR_SUCCESS;
5391 done:
5392 uirow = MSI_CreateRecord( 2 );
5393 MSI_RecordSetStringW( uirow, 1, display_name );
5394 MSI_RecordSetStringW( uirow, 2, name );
5395 ui_actiondata( package, szStartServices, uirow );
5396 msiobj_release( &uirow->hdr );
5398 CloseServiceHandle(service);
5399 CloseServiceHandle(scm);
5401 msi_free(name);
5402 msi_free(args);
5403 msi_free(vector);
5404 msi_free(display_name);
5405 return r;
5408 static UINT ACTION_StartServices( MSIPACKAGE *package )
5410 UINT rc;
5411 MSIQUERY *view;
5413 static const WCHAR query[] = {
5414 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5415 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5417 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5418 if (rc != ERROR_SUCCESS)
5419 return ERROR_SUCCESS;
5421 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5422 msiobj_release(&view->hdr);
5424 return rc;
5427 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5429 DWORD i, needed, count;
5430 ENUM_SERVICE_STATUSW *dependencies;
5431 SERVICE_STATUS ss;
5432 SC_HANDLE depserv;
5434 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5435 0, &needed, &count))
5436 return TRUE;
5438 if (GetLastError() != ERROR_MORE_DATA)
5439 return FALSE;
5441 dependencies = msi_alloc(needed);
5442 if (!dependencies)
5443 return FALSE;
5445 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5446 needed, &needed, &count))
5447 goto error;
5449 for (i = 0; i < count; i++)
5451 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5452 SERVICE_STOP | SERVICE_QUERY_STATUS);
5453 if (!depserv)
5454 goto error;
5456 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5457 goto error;
5460 return TRUE;
5462 error:
5463 msi_free(dependencies);
5464 return FALSE;
5467 static UINT stop_service( LPCWSTR name )
5469 SC_HANDLE scm = NULL, service = NULL;
5470 SERVICE_STATUS status;
5471 SERVICE_STATUS_PROCESS ssp;
5472 DWORD needed;
5474 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5475 if (!scm)
5477 WARN("Failed to open the SCM: %d\n", GetLastError());
5478 goto done;
5481 service = OpenServiceW(scm, name,
5482 SERVICE_STOP |
5483 SERVICE_QUERY_STATUS |
5484 SERVICE_ENUMERATE_DEPENDENTS);
5485 if (!service)
5487 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5488 goto done;
5491 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5492 sizeof(SERVICE_STATUS_PROCESS), &needed))
5494 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5495 goto done;
5498 if (ssp.dwCurrentState == SERVICE_STOPPED)
5499 goto done;
5501 stop_service_dependents(scm, service);
5503 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5504 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5506 done:
5507 CloseServiceHandle(service);
5508 CloseServiceHandle(scm);
5510 return ERROR_SUCCESS;
5513 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5515 MSIPACKAGE *package = param;
5516 MSICOMPONENT *comp;
5517 MSIRECORD *uirow;
5518 LPCWSTR component;
5519 LPWSTR name = NULL, display_name = NULL;
5520 DWORD event, len;
5521 SC_HANDLE scm;
5523 event = MSI_RecordGetInteger( rec, 3 );
5524 if (!(event & msidbServiceControlEventStop))
5525 return ERROR_SUCCESS;
5527 component = MSI_RecordGetString( rec, 6 );
5528 comp = get_loaded_component( package, component );
5529 if (!comp)
5530 return ERROR_SUCCESS;
5532 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5534 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5535 comp->Action = comp->Installed;
5536 return ERROR_SUCCESS;
5538 comp->Action = INSTALLSTATE_ABSENT;
5540 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5541 if (!scm)
5543 ERR("Failed to open the service control manager\n");
5544 goto done;
5547 len = 0;
5548 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5549 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5551 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5552 GetServiceDisplayNameW( scm, name, display_name, &len );
5554 CloseServiceHandle( scm );
5556 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5557 stop_service( name );
5559 done:
5560 uirow = MSI_CreateRecord( 2 );
5561 MSI_RecordSetStringW( uirow, 1, display_name );
5562 MSI_RecordSetStringW( uirow, 2, name );
5563 ui_actiondata( package, szStopServices, uirow );
5564 msiobj_release( &uirow->hdr );
5566 msi_free( name );
5567 msi_free( display_name );
5568 return ERROR_SUCCESS;
5571 static UINT ACTION_StopServices( MSIPACKAGE *package )
5573 UINT rc;
5574 MSIQUERY *view;
5576 static const WCHAR query[] = {
5577 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5578 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5580 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5581 if (rc != ERROR_SUCCESS)
5582 return ERROR_SUCCESS;
5584 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5585 msiobj_release(&view->hdr);
5587 return rc;
5590 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5592 MSIPACKAGE *package = param;
5593 MSICOMPONENT *comp;
5594 MSIRECORD *uirow;
5595 LPCWSTR component;
5596 LPWSTR name = NULL, display_name = NULL;
5597 DWORD event, len;
5598 SC_HANDLE scm = NULL, service = NULL;
5600 event = MSI_RecordGetInteger( rec, 3 );
5601 if (!(event & msidbServiceControlEventDelete))
5602 return ERROR_SUCCESS;
5604 component = MSI_RecordGetString(rec, 6);
5605 comp = get_loaded_component(package, component);
5606 if (!comp)
5607 return ERROR_SUCCESS;
5609 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5611 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5612 comp->Action = comp->Installed;
5613 return ERROR_SUCCESS;
5615 comp->Action = INSTALLSTATE_ABSENT;
5617 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5618 stop_service( name );
5620 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5621 if (!scm)
5623 WARN("Failed to open the SCM: %d\n", GetLastError());
5624 goto done;
5627 len = 0;
5628 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5629 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5631 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5632 GetServiceDisplayNameW( scm, name, display_name, &len );
5635 service = OpenServiceW( scm, name, DELETE );
5636 if (!service)
5638 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5639 goto done;
5642 if (!DeleteService( service ))
5643 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5645 done:
5646 uirow = MSI_CreateRecord( 2 );
5647 MSI_RecordSetStringW( uirow, 1, display_name );
5648 MSI_RecordSetStringW( uirow, 2, name );
5649 ui_actiondata( package, szDeleteServices, uirow );
5650 msiobj_release( &uirow->hdr );
5652 CloseServiceHandle( service );
5653 CloseServiceHandle( scm );
5654 msi_free( name );
5655 msi_free( display_name );
5657 return ERROR_SUCCESS;
5660 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5662 UINT rc;
5663 MSIQUERY *view;
5665 static const WCHAR query[] = {
5666 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5667 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5669 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5670 if (rc != ERROR_SUCCESS)
5671 return ERROR_SUCCESS;
5673 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5674 msiobj_release( &view->hdr );
5676 return rc;
5679 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5681 MSIFILE *file;
5683 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5685 if (!lstrcmpW(file->File, filename))
5686 return file;
5689 return NULL;
5692 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5694 MSIPACKAGE *package = param;
5695 LPWSTR driver, driver_path, ptr;
5696 WCHAR outpath[MAX_PATH];
5697 MSIFILE *driver_file, *setup_file;
5698 MSIRECORD *uirow;
5699 LPCWSTR desc;
5700 DWORD len, usage;
5701 UINT r = ERROR_SUCCESS;
5703 static const WCHAR driver_fmt[] = {
5704 'D','r','i','v','e','r','=','%','s',0};
5705 static const WCHAR setup_fmt[] = {
5706 'S','e','t','u','p','=','%','s',0};
5707 static const WCHAR usage_fmt[] = {
5708 'F','i','l','e','U','s','a','g','e','=','1',0};
5710 desc = MSI_RecordGetString(rec, 3);
5712 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5713 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5715 if (!driver_file)
5717 ERR("ODBC Driver entry not found!\n");
5718 return ERROR_FUNCTION_FAILED;
5721 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5722 if (setup_file)
5723 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5724 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5726 driver = msi_alloc(len * sizeof(WCHAR));
5727 if (!driver)
5728 return ERROR_OUTOFMEMORY;
5730 ptr = driver;
5731 lstrcpyW(ptr, desc);
5732 ptr += lstrlenW(ptr) + 1;
5734 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5735 ptr += len + 1;
5737 if (setup_file)
5739 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5740 ptr += len + 1;
5743 lstrcpyW(ptr, usage_fmt);
5744 ptr += lstrlenW(ptr) + 1;
5745 *ptr = '\0';
5747 driver_path = strdupW(driver_file->TargetPath);
5748 ptr = strrchrW(driver_path, '\\');
5749 if (ptr) *ptr = '\0';
5751 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5752 NULL, ODBC_INSTALL_COMPLETE, &usage))
5754 ERR("Failed to install SQL driver!\n");
5755 r = ERROR_FUNCTION_FAILED;
5758 uirow = MSI_CreateRecord( 5 );
5759 MSI_RecordSetStringW( uirow, 1, desc );
5760 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5761 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
5762 ui_actiondata( package, szInstallODBC, uirow );
5763 msiobj_release( &uirow->hdr );
5765 msi_free(driver);
5766 msi_free(driver_path);
5768 return r;
5771 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5773 MSIPACKAGE *package = param;
5774 LPWSTR translator, translator_path, ptr;
5775 WCHAR outpath[MAX_PATH];
5776 MSIFILE *translator_file, *setup_file;
5777 MSIRECORD *uirow;
5778 LPCWSTR desc;
5779 DWORD len, usage;
5780 UINT r = ERROR_SUCCESS;
5782 static const WCHAR translator_fmt[] = {
5783 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5784 static const WCHAR setup_fmt[] = {
5785 'S','e','t','u','p','=','%','s',0};
5787 desc = MSI_RecordGetString(rec, 3);
5789 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5790 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5792 if (!translator_file)
5794 ERR("ODBC Translator entry not found!\n");
5795 return ERROR_FUNCTION_FAILED;
5798 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5799 if (setup_file)
5800 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5802 translator = msi_alloc(len * sizeof(WCHAR));
5803 if (!translator)
5804 return ERROR_OUTOFMEMORY;
5806 ptr = translator;
5807 lstrcpyW(ptr, desc);
5808 ptr += lstrlenW(ptr) + 1;
5810 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5811 ptr += len + 1;
5813 if (setup_file)
5815 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5816 ptr += len + 1;
5818 *ptr = '\0';
5820 translator_path = strdupW(translator_file->TargetPath);
5821 ptr = strrchrW(translator_path, '\\');
5822 if (ptr) *ptr = '\0';
5824 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5825 NULL, ODBC_INSTALL_COMPLETE, &usage))
5827 ERR("Failed to install SQL translator!\n");
5828 r = ERROR_FUNCTION_FAILED;
5831 uirow = MSI_CreateRecord( 5 );
5832 MSI_RecordSetStringW( uirow, 1, desc );
5833 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5834 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
5835 ui_actiondata( package, szInstallODBC, uirow );
5836 msiobj_release( &uirow->hdr );
5838 msi_free(translator);
5839 msi_free(translator_path);
5841 return r;
5844 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5846 MSIPACKAGE *package = param;
5847 LPWSTR attrs;
5848 LPCWSTR desc, driver;
5849 WORD request = ODBC_ADD_SYS_DSN;
5850 INT registration;
5851 DWORD len;
5852 UINT r = ERROR_SUCCESS;
5853 MSIRECORD *uirow;
5855 static const WCHAR attrs_fmt[] = {
5856 'D','S','N','=','%','s',0 };
5858 desc = MSI_RecordGetString(rec, 3);
5859 driver = MSI_RecordGetString(rec, 4);
5860 registration = MSI_RecordGetInteger(rec, 5);
5862 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5863 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5865 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5866 attrs = msi_alloc(len * sizeof(WCHAR));
5867 if (!attrs)
5868 return ERROR_OUTOFMEMORY;
5870 len = sprintfW(attrs, attrs_fmt, desc);
5871 attrs[len + 1] = 0;
5873 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5875 ERR("Failed to install SQL data source!\n");
5876 r = ERROR_FUNCTION_FAILED;
5879 uirow = MSI_CreateRecord( 5 );
5880 MSI_RecordSetStringW( uirow, 1, desc );
5881 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5882 MSI_RecordSetInteger( uirow, 3, request );
5883 ui_actiondata( package, szInstallODBC, uirow );
5884 msiobj_release( &uirow->hdr );
5886 msi_free(attrs);
5888 return r;
5891 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5893 UINT rc;
5894 MSIQUERY *view;
5896 static const WCHAR driver_query[] = {
5897 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5898 'O','D','B','C','D','r','i','v','e','r',0 };
5900 static const WCHAR translator_query[] = {
5901 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5902 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5904 static const WCHAR source_query[] = {
5905 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5906 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5908 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5909 if (rc != ERROR_SUCCESS)
5910 return ERROR_SUCCESS;
5912 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5913 msiobj_release(&view->hdr);
5915 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5916 if (rc != ERROR_SUCCESS)
5917 return ERROR_SUCCESS;
5919 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5920 msiobj_release(&view->hdr);
5922 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5923 if (rc != ERROR_SUCCESS)
5924 return ERROR_SUCCESS;
5926 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5927 msiobj_release(&view->hdr);
5929 return rc;
5932 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5934 MSIPACKAGE *package = param;
5935 MSIRECORD *uirow;
5936 DWORD usage;
5937 LPCWSTR desc;
5939 desc = MSI_RecordGetString( rec, 3 );
5940 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5942 WARN("Failed to remove ODBC driver\n");
5944 else if (!usage)
5946 FIXME("Usage count reached 0\n");
5949 uirow = MSI_CreateRecord( 2 );
5950 MSI_RecordSetStringW( uirow, 1, desc );
5951 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5952 ui_actiondata( package, szRemoveODBC, uirow );
5953 msiobj_release( &uirow->hdr );
5955 return ERROR_SUCCESS;
5958 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5960 MSIPACKAGE *package = param;
5961 MSIRECORD *uirow;
5962 DWORD usage;
5963 LPCWSTR desc;
5965 desc = MSI_RecordGetString( rec, 3 );
5966 if (!SQLRemoveTranslatorW( desc, &usage ))
5968 WARN("Failed to remove ODBC translator\n");
5970 else if (!usage)
5972 FIXME("Usage count reached 0\n");
5975 uirow = MSI_CreateRecord( 2 );
5976 MSI_RecordSetStringW( uirow, 1, desc );
5977 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5978 ui_actiondata( package, szRemoveODBC, uirow );
5979 msiobj_release( &uirow->hdr );
5981 return ERROR_SUCCESS;
5984 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5986 MSIPACKAGE *package = param;
5987 MSIRECORD *uirow;
5988 LPWSTR attrs;
5989 LPCWSTR desc, driver;
5990 WORD request = ODBC_REMOVE_SYS_DSN;
5991 INT registration;
5992 DWORD len;
5994 static const WCHAR attrs_fmt[] = {
5995 'D','S','N','=','%','s',0 };
5997 desc = MSI_RecordGetString( rec, 3 );
5998 driver = MSI_RecordGetString( rec, 4 );
5999 registration = MSI_RecordGetInteger( rec, 5 );
6001 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6002 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6004 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6005 attrs = msi_alloc( len * sizeof(WCHAR) );
6006 if (!attrs)
6007 return ERROR_OUTOFMEMORY;
6009 FIXME("Use ODBCSourceAttribute table\n");
6011 len = sprintfW( attrs, attrs_fmt, desc );
6012 attrs[len + 1] = 0;
6014 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6016 WARN("Failed to remove ODBC data source\n");
6018 msi_free( attrs );
6020 uirow = MSI_CreateRecord( 3 );
6021 MSI_RecordSetStringW( uirow, 1, desc );
6022 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6023 MSI_RecordSetInteger( uirow, 3, request );
6024 ui_actiondata( package, szRemoveODBC, uirow );
6025 msiobj_release( &uirow->hdr );
6027 return ERROR_SUCCESS;
6030 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6032 UINT rc;
6033 MSIQUERY *view;
6035 static const WCHAR driver_query[] = {
6036 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6037 'O','D','B','C','D','r','i','v','e','r',0 };
6039 static const WCHAR translator_query[] = {
6040 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6041 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6043 static const WCHAR source_query[] = {
6044 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6045 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6047 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6048 if (rc != ERROR_SUCCESS)
6049 return ERROR_SUCCESS;
6051 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6052 msiobj_release( &view->hdr );
6054 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6055 if (rc != ERROR_SUCCESS)
6056 return ERROR_SUCCESS;
6058 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6059 msiobj_release( &view->hdr );
6061 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6062 if (rc != ERROR_SUCCESS)
6063 return ERROR_SUCCESS;
6065 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6066 msiobj_release( &view->hdr );
6068 return rc;
6071 #define ENV_ACT_SETALWAYS 0x1
6072 #define ENV_ACT_SETABSENT 0x2
6073 #define ENV_ACT_REMOVE 0x4
6074 #define ENV_ACT_REMOVEMATCH 0x8
6076 #define ENV_MOD_MACHINE 0x20000000
6077 #define ENV_MOD_APPEND 0x40000000
6078 #define ENV_MOD_PREFIX 0x80000000
6079 #define ENV_MOD_MASK 0xC0000000
6081 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6083 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6085 LPCWSTR cptr = *name;
6087 static const WCHAR prefix[] = {'[','~',']',0};
6088 static const int prefix_len = 3;
6090 *flags = 0;
6091 while (*cptr)
6093 if (*cptr == '=')
6094 *flags |= ENV_ACT_SETALWAYS;
6095 else if (*cptr == '+')
6096 *flags |= ENV_ACT_SETABSENT;
6097 else if (*cptr == '-')
6098 *flags |= ENV_ACT_REMOVE;
6099 else if (*cptr == '!')
6100 *flags |= ENV_ACT_REMOVEMATCH;
6101 else if (*cptr == '*')
6102 *flags |= ENV_MOD_MACHINE;
6103 else
6104 break;
6106 cptr++;
6107 (*name)++;
6110 if (!*cptr)
6112 ERR("Missing environment variable\n");
6113 return ERROR_FUNCTION_FAILED;
6116 if (*value)
6118 LPCWSTR ptr = *value;
6119 if (!strncmpW(ptr, prefix, prefix_len))
6121 if (ptr[prefix_len] == szSemiColon[0])
6123 *flags |= ENV_MOD_APPEND;
6124 *value += lstrlenW(prefix);
6126 else
6128 *value = NULL;
6131 else if (lstrlenW(*value) >= prefix_len)
6133 ptr += lstrlenW(ptr) - prefix_len;
6134 if (!lstrcmpW(ptr, prefix))
6136 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6138 *flags |= ENV_MOD_PREFIX;
6139 /* the "[~]" will be removed by deformat_string */;
6141 else
6143 *value = NULL;
6149 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6150 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6151 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6152 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6154 ERR("Invalid flags: %08x\n", *flags);
6155 return ERROR_FUNCTION_FAILED;
6158 if (!*flags)
6159 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6161 return ERROR_SUCCESS;
6164 static UINT open_env_key( DWORD flags, HKEY *key )
6166 static const WCHAR user_env[] =
6167 {'E','n','v','i','r','o','n','m','e','n','t',0};
6168 static const WCHAR machine_env[] =
6169 {'S','y','s','t','e','m','\\',
6170 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6171 'C','o','n','t','r','o','l','\\',
6172 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6173 'E','n','v','i','r','o','n','m','e','n','t',0};
6174 const WCHAR *env;
6175 HKEY root;
6176 LONG res;
6178 if (flags & ENV_MOD_MACHINE)
6180 env = machine_env;
6181 root = HKEY_LOCAL_MACHINE;
6183 else
6185 env = user_env;
6186 root = HKEY_CURRENT_USER;
6189 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6190 if (res != ERROR_SUCCESS)
6192 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6193 return ERROR_FUNCTION_FAILED;
6196 return ERROR_SUCCESS;
6199 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6201 MSIPACKAGE *package = param;
6202 LPCWSTR name, value, component;
6203 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6204 DWORD flags, type, size;
6205 UINT res;
6206 HKEY env = NULL;
6207 MSICOMPONENT *comp;
6208 MSIRECORD *uirow;
6209 int action = 0;
6211 component = MSI_RecordGetString(rec, 4);
6212 comp = get_loaded_component(package, component);
6213 if (!comp)
6214 return ERROR_SUCCESS;
6216 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6218 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6219 comp->Action = comp->Installed;
6220 return ERROR_SUCCESS;
6222 comp->Action = INSTALLSTATE_LOCAL;
6224 name = MSI_RecordGetString(rec, 2);
6225 value = MSI_RecordGetString(rec, 3);
6227 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6229 res = env_parse_flags(&name, &value, &flags);
6230 if (res != ERROR_SUCCESS || !value)
6231 goto done;
6233 if (value && !deformat_string(package, value, &deformatted))
6235 res = ERROR_OUTOFMEMORY;
6236 goto done;
6239 value = deformatted;
6241 res = open_env_key( flags, &env );
6242 if (res != ERROR_SUCCESS)
6243 goto done;
6245 if (flags & ENV_MOD_MACHINE)
6246 action |= 0x20000000;
6248 size = 0;
6249 type = REG_SZ;
6250 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6251 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6252 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6253 goto done;
6255 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6257 action = 0x2;
6259 /* Nothing to do. */
6260 if (!value)
6262 res = ERROR_SUCCESS;
6263 goto done;
6266 /* If we are appending but the string was empty, strip ; */
6267 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6269 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6270 newval = strdupW(value);
6271 if (!newval)
6273 res = ERROR_OUTOFMEMORY;
6274 goto done;
6277 else
6279 action = 0x1;
6281 /* Contrary to MSDN, +-variable to [~];path works */
6282 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6284 res = ERROR_SUCCESS;
6285 goto done;
6288 data = msi_alloc(size);
6289 if (!data)
6291 RegCloseKey(env);
6292 return ERROR_OUTOFMEMORY;
6295 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6296 if (res != ERROR_SUCCESS)
6297 goto done;
6299 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
6301 action = 0x4;
6302 res = RegDeleteValueW(env, name);
6303 if (res != ERROR_SUCCESS)
6304 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6305 goto done;
6308 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6309 if (flags & ENV_MOD_MASK)
6311 DWORD mod_size;
6312 int multiplier = 0;
6313 if (flags & ENV_MOD_APPEND) multiplier++;
6314 if (flags & ENV_MOD_PREFIX) multiplier++;
6315 mod_size = lstrlenW(value) * multiplier;
6316 size += mod_size * sizeof(WCHAR);
6319 newval = msi_alloc(size);
6320 ptr = newval;
6321 if (!newval)
6323 res = ERROR_OUTOFMEMORY;
6324 goto done;
6327 if (flags & ENV_MOD_PREFIX)
6329 lstrcpyW(newval, value);
6330 ptr = newval + lstrlenW(value);
6331 action |= 0x80000000;
6334 lstrcpyW(ptr, data);
6336 if (flags & ENV_MOD_APPEND)
6338 lstrcatW(newval, value);
6339 action |= 0x40000000;
6342 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6343 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6344 if (res)
6346 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6349 done:
6350 uirow = MSI_CreateRecord( 3 );
6351 MSI_RecordSetStringW( uirow, 1, name );
6352 MSI_RecordSetStringW( uirow, 2, newval );
6353 MSI_RecordSetInteger( uirow, 3, action );
6354 ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6355 msiobj_release( &uirow->hdr );
6357 if (env) RegCloseKey(env);
6358 msi_free(deformatted);
6359 msi_free(data);
6360 msi_free(newval);
6361 return res;
6364 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6366 UINT rc;
6367 MSIQUERY * view;
6368 static const WCHAR ExecSeqQuery[] =
6369 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6370 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6371 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6372 if (rc != ERROR_SUCCESS)
6373 return ERROR_SUCCESS;
6375 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6376 msiobj_release(&view->hdr);
6378 return rc;
6381 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6383 MSIPACKAGE *package = param;
6384 LPCWSTR name, value, component;
6385 LPWSTR deformatted = NULL;
6386 DWORD flags;
6387 HKEY env;
6388 MSICOMPONENT *comp;
6389 MSIRECORD *uirow;
6390 int action = 0;
6391 LONG res;
6392 UINT r;
6394 component = MSI_RecordGetString( rec, 4 );
6395 comp = get_loaded_component( package, component );
6396 if (!comp)
6397 return ERROR_SUCCESS;
6399 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6401 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6402 comp->Action = comp->Installed;
6403 return ERROR_SUCCESS;
6405 comp->Action = INSTALLSTATE_ABSENT;
6407 name = MSI_RecordGetString( rec, 2 );
6408 value = MSI_RecordGetString( rec, 3 );
6410 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6412 r = env_parse_flags( &name, &value, &flags );
6413 if (r != ERROR_SUCCESS)
6414 return r;
6416 if (!(flags & ENV_ACT_REMOVE))
6418 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6419 return ERROR_SUCCESS;
6422 if (value && !deformat_string( package, value, &deformatted ))
6423 return ERROR_OUTOFMEMORY;
6425 value = deformatted;
6427 r = open_env_key( flags, &env );
6428 if (r != ERROR_SUCCESS)
6430 r = ERROR_SUCCESS;
6431 goto done;
6434 if (flags & ENV_MOD_MACHINE)
6435 action |= 0x20000000;
6437 TRACE("Removing %s\n", debugstr_w(name));
6439 res = RegDeleteValueW( env, name );
6440 if (res != ERROR_SUCCESS)
6442 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6443 r = ERROR_SUCCESS;
6446 done:
6447 uirow = MSI_CreateRecord( 3 );
6448 MSI_RecordSetStringW( uirow, 1, name );
6449 MSI_RecordSetStringW( uirow, 2, value );
6450 MSI_RecordSetInteger( uirow, 3, action );
6451 ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6452 msiobj_release( &uirow->hdr );
6454 if (env) RegCloseKey( env );
6455 msi_free( deformatted );
6456 return r;
6459 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6461 UINT rc;
6462 MSIQUERY *view;
6463 static const WCHAR query[] =
6464 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6465 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6467 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6468 if (rc != ERROR_SUCCESS)
6469 return ERROR_SUCCESS;
6471 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6472 msiobj_release( &view->hdr );
6474 return rc;
6477 typedef struct tagMSIASSEMBLY
6479 struct list entry;
6480 MSICOMPONENT *component;
6481 MSIFEATURE *feature;
6482 MSIFILE *file;
6483 LPWSTR manifest;
6484 LPWSTR application;
6485 LPWSTR display_name;
6486 DWORD attributes;
6487 BOOL installed;
6488 } MSIASSEMBLY;
6490 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6491 DWORD dwReserved);
6492 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6493 LPVOID pvReserved, HMODULE *phModDll);
6495 static BOOL init_functionpointers(void)
6497 HRESULT hr;
6498 HMODULE hfusion;
6499 HMODULE hmscoree;
6501 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6503 hmscoree = LoadLibraryA("mscoree.dll");
6504 if (!hmscoree)
6506 WARN("mscoree.dll not available\n");
6507 return FALSE;
6510 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6511 if (!pLoadLibraryShim)
6513 WARN("LoadLibraryShim not available\n");
6514 FreeLibrary(hmscoree);
6515 return FALSE;
6518 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6519 if (FAILED(hr))
6521 WARN("fusion.dll not available\n");
6522 FreeLibrary(hmscoree);
6523 return FALSE;
6526 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6528 FreeLibrary(hmscoree);
6529 return TRUE;
6532 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6533 LPWSTR path)
6535 IAssemblyCache *cache;
6536 MSIRECORD *uirow;
6537 HRESULT hr;
6538 UINT r = ERROR_FUNCTION_FAILED;
6540 TRACE("installing assembly: %s\n", debugstr_w(path));
6542 uirow = MSI_CreateRecord( 2 );
6543 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
6544 ui_actiondata( package, szMsiPublishAssemblies, uirow );
6545 msiobj_release( &uirow->hdr );
6547 if (assembly->feature)
6548 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6550 if (assembly->manifest)
6551 FIXME("Manifest unhandled\n");
6553 if (assembly->application)
6555 FIXME("Assembly should be privately installed\n");
6556 return ERROR_SUCCESS;
6559 if (assembly->attributes == msidbAssemblyAttributesWin32)
6561 FIXME("Win32 assemblies not handled\n");
6562 return ERROR_SUCCESS;
6565 hr = pCreateAssemblyCache(&cache, 0);
6566 if (FAILED(hr))
6567 goto done;
6569 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6570 if (FAILED(hr))
6571 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6573 r = ERROR_SUCCESS;
6575 done:
6576 IAssemblyCache_Release(cache);
6577 return r;
6580 typedef struct tagASSEMBLY_LIST
6582 MSIPACKAGE *package;
6583 IAssemblyCache *cache;
6584 struct list *assemblies;
6585 } ASSEMBLY_LIST;
6587 typedef struct tagASSEMBLY_NAME
6589 LPWSTR name;
6590 LPWSTR version;
6591 LPWSTR culture;
6592 LPWSTR pubkeytoken;
6593 } ASSEMBLY_NAME;
6595 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6597 ASSEMBLY_NAME *asmname = param;
6598 LPCWSTR name = MSI_RecordGetString(rec, 2);
6599 LPWSTR val = msi_dup_record_field(rec, 3);
6601 static const WCHAR Name[] = {'N','a','m','e',0};
6602 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6603 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6604 static const WCHAR PublicKeyToken[] = {
6605 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6607 if (!strcmpiW(name, Name))
6608 asmname->name = val;
6609 else if (!strcmpiW(name, Version))
6610 asmname->version = val;
6611 else if (!strcmpiW(name, Culture))
6612 asmname->culture = val;
6613 else if (!strcmpiW(name, PublicKeyToken))
6614 asmname->pubkeytoken = val;
6615 else
6616 msi_free(val);
6618 return ERROR_SUCCESS;
6621 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6623 if (!*str)
6625 *size = lstrlenW(append) + 1;
6626 *str = msi_alloc((*size) * sizeof(WCHAR));
6627 lstrcpyW(*str, append);
6628 return;
6631 (*size) += lstrlenW(append);
6632 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6633 lstrcatW(*str, append);
6636 static WCHAR *get_assembly_display_name( MSIDATABASE *db, MSICOMPONENT *comp )
6638 static const WCHAR separator[] = {',',' ',0};
6639 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6640 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6641 static const WCHAR PublicKeyToken[] = {'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6642 static const WCHAR query[] = {
6643 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6644 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6645 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6646 '=','\'','%','s','\'',0};
6647 ASSEMBLY_NAME name;
6648 MSIQUERY *view;
6649 LPWSTR display_name;
6650 DWORD size;
6651 UINT r;
6653 display_name = NULL;
6654 memset( &name, 0, sizeof(ASSEMBLY_NAME) );
6656 r = MSI_OpenQuery( db, &view, query, comp->Component );
6657 if (r != ERROR_SUCCESS)
6658 return NULL;
6660 MSI_IterateRecords( view, NULL, parse_assembly_name, &name );
6661 msiobj_release( &view->hdr );
6663 if (!name.name)
6665 ERR("No assembly name specified!\n");
6666 return NULL;
6669 append_str( &display_name, &size, name.name );
6671 if (name.version)
6673 append_str( &display_name, &size, separator );
6674 append_str( &display_name, &size, Version );
6675 append_str( &display_name, &size, name.version );
6677 if (name.culture)
6679 append_str( &display_name, &size, separator );
6680 append_str( &display_name, &size, Culture );
6681 append_str( &display_name, &size, name.culture );
6683 if (name.pubkeytoken)
6685 append_str( &display_name, &size, separator );
6686 append_str( &display_name, &size, PublicKeyToken );
6687 append_str( &display_name, &size, name.pubkeytoken );
6690 msi_free( name.name );
6691 msi_free( name.version );
6692 msi_free( name.culture );
6693 msi_free( name.pubkeytoken );
6695 return display_name;
6698 static BOOL check_assembly_installed( MSIDATABASE *db, IAssemblyCache *cache, MSICOMPONENT *comp )
6700 ASSEMBLY_INFO asminfo;
6701 LPWSTR disp;
6702 BOOL found = FALSE;
6703 HRESULT hr;
6705 disp = get_assembly_display_name( db, comp );
6706 if (!disp)
6707 return FALSE;
6709 memset( &asminfo, 0, sizeof(ASSEMBLY_INFO) );
6710 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6712 hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_VALIDATE, disp, &asminfo );
6713 if (SUCCEEDED(hr))
6714 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6716 msi_free( disp );
6717 return found;
6720 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6722 ASSEMBLY_LIST *list = param;
6723 MSIASSEMBLY *assembly;
6724 LPCWSTR component;
6726 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6727 if (!assembly)
6728 return ERROR_OUTOFMEMORY;
6730 component = MSI_RecordGetString(rec, 1);
6731 assembly->component = get_loaded_component(list->package, component);
6732 if (!assembly->component)
6733 return ERROR_SUCCESS;
6735 if (assembly->component->ActionRequest != INSTALLSTATE_LOCAL &&
6736 assembly->component->ActionRequest != INSTALLSTATE_SOURCE)
6738 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6739 assembly->component->Action = assembly->component->Installed;
6740 return ERROR_SUCCESS;
6742 assembly->component->Action = assembly->component->ActionRequest;
6744 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6745 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6747 if (!assembly->file)
6749 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6750 return ERROR_FUNCTION_FAILED;
6753 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6754 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6755 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6757 if (assembly->application)
6759 WCHAR version[24];
6760 DWORD size = sizeof(version)/sizeof(WCHAR);
6762 /* FIXME: we should probably check the manifest file here */
6764 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6765 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6767 assembly->installed = TRUE;
6770 else
6771 assembly->installed = check_assembly_installed(list->package->db,
6772 list->cache,
6773 assembly->component);
6775 list_add_head(list->assemblies, &assembly->entry);
6776 return ERROR_SUCCESS;
6779 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6781 IAssemblyCache *cache = NULL;
6782 ASSEMBLY_LIST list;
6783 MSIQUERY *view;
6784 HRESULT hr;
6785 UINT r;
6787 static const WCHAR query[] =
6788 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6789 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6791 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6792 if (r != ERROR_SUCCESS)
6793 return ERROR_SUCCESS;
6795 hr = pCreateAssemblyCache(&cache, 0);
6796 if (FAILED(hr))
6797 return ERROR_FUNCTION_FAILED;
6799 list.package = package;
6800 list.cache = cache;
6801 list.assemblies = assemblies;
6803 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6804 msiobj_release(&view->hdr);
6806 IAssemblyCache_Release(cache);
6808 return r;
6811 static void free_assemblies(struct list *assemblies)
6813 struct list *item, *cursor;
6815 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6817 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6819 list_remove(&assembly->entry);
6820 msi_free(assembly->application);
6821 msi_free(assembly->manifest);
6822 msi_free(assembly->display_name);
6823 msi_free(assembly);
6827 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6829 MSIASSEMBLY *assembly;
6831 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6833 if (!lstrcmpW(assembly->file->File, file))
6835 *out = assembly;
6836 return TRUE;
6840 return FALSE;
6843 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6844 LPWSTR *path, DWORD *attrs, PVOID user)
6846 MSIASSEMBLY *assembly;
6847 WCHAR temppath[MAX_PATH];
6848 struct list *assemblies = user;
6849 UINT r;
6851 if (!find_assembly(assemblies, file, &assembly))
6852 return FALSE;
6854 GetTempPathW(MAX_PATH, temppath);
6855 PathAddBackslashW(temppath);
6856 lstrcatW(temppath, assembly->file->FileName);
6858 if (action == MSICABEXTRACT_BEGINEXTRACT)
6860 if (assembly->installed)
6861 return FALSE;
6863 *path = strdupW(temppath);
6864 *attrs = assembly->file->Attributes;
6866 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6868 assembly->installed = TRUE;
6870 r = install_assembly(package, assembly, temppath);
6871 if (r != ERROR_SUCCESS)
6872 ERR("Failed to install assembly\n");
6875 return TRUE;
6878 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6880 UINT r;
6881 struct list assemblies = LIST_INIT(assemblies);
6882 MSIASSEMBLY *assembly;
6883 MSIMEDIAINFO *mi;
6885 if (!init_functionpointers() || !pCreateAssemblyCache)
6886 return ERROR_FUNCTION_FAILED;
6888 r = load_assemblies(package, &assemblies);
6889 if (r != ERROR_SUCCESS)
6890 goto done;
6892 if (list_empty(&assemblies))
6893 goto done;
6895 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6896 if (!mi)
6898 r = ERROR_OUTOFMEMORY;
6899 goto done;
6902 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6904 if (assembly->installed && !mi->is_continuous)
6905 continue;
6907 if (assembly->file->IsCompressed)
6909 if (assembly->file->disk_id != mi->disk_id || mi->is_continuous)
6911 MSICABDATA data;
6913 r = ready_media(package, assembly->file, mi);
6914 if (r != ERROR_SUCCESS)
6916 ERR("Failed to ready media\n");
6917 break;
6920 data.mi = mi;
6921 data.package = package;
6922 data.cb = installassembly_cb;
6923 data.user = &assemblies;
6925 if (!msi_cabextract(package, mi, &data))
6927 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6928 r = ERROR_FUNCTION_FAILED;
6929 break;
6933 else
6935 LPWSTR source = resolve_file_source(package, assembly->file);
6937 r = install_assembly(package, assembly, source);
6938 if (r != ERROR_SUCCESS)
6939 ERR("Failed to install assembly\n");
6941 msi_free(source);
6944 /* FIXME: write Installer assembly reg values */
6947 done:
6948 free_assemblies(&assemblies);
6949 return r;
6952 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6954 LPWSTR key, template, id;
6955 UINT r = ERROR_SUCCESS;
6957 id = msi_dup_property( package->db, szProductID );
6958 if (id)
6960 msi_free( id );
6961 return ERROR_SUCCESS;
6963 template = msi_dup_property( package->db, szPIDTemplate );
6964 key = msi_dup_property( package->db, szPIDKEY );
6966 if (key && template)
6968 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6969 r = msi_set_property( package->db, szProductID, key );
6971 msi_free( template );
6972 msi_free( key );
6973 return r;
6976 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6978 TRACE("\n");
6979 package->need_reboot = 1;
6980 return ERROR_SUCCESS;
6983 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6985 static const WCHAR szAvailableFreeReg[] =
6986 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6987 MSIRECORD *uirow;
6988 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6990 TRACE("%p %d kilobytes\n", package, space);
6992 uirow = MSI_CreateRecord( 1 );
6993 MSI_RecordSetInteger( uirow, 1, space );
6994 ui_actiondata( package, szAllocateRegistrySpace, uirow );
6995 msiobj_release( &uirow->hdr );
6997 return ERROR_SUCCESS;
7000 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7002 FIXME("%p\n", package);
7003 return ERROR_SUCCESS;
7006 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7008 FIXME("%p\n", package);
7009 return ERROR_SUCCESS;
7012 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7014 UINT r, count;
7015 MSIQUERY *view;
7017 static const WCHAR driver_query[] = {
7018 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7019 'O','D','B','C','D','r','i','v','e','r',0 };
7021 static const WCHAR translator_query[] = {
7022 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7023 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7025 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7026 if (r == ERROR_SUCCESS)
7028 count = 0;
7029 r = MSI_IterateRecords( view, &count, NULL, package );
7030 msiobj_release( &view->hdr );
7031 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7034 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7035 if (r == ERROR_SUCCESS)
7037 count = 0;
7038 r = MSI_IterateRecords( view, &count, NULL, package );
7039 msiobj_release( &view->hdr );
7040 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7043 return ERROR_SUCCESS;
7046 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7047 LPCSTR action, LPCWSTR table )
7049 static const WCHAR query[] = {
7050 'S','E','L','E','C','T',' ','*',' ',
7051 'F','R','O','M',' ','`','%','s','`',0 };
7052 MSIQUERY *view = NULL;
7053 DWORD count = 0;
7054 UINT r;
7056 r = MSI_OpenQuery( package->db, &view, query, table );
7057 if (r == ERROR_SUCCESS)
7059 r = MSI_IterateRecords(view, &count, NULL, package);
7060 msiobj_release(&view->hdr);
7063 if (count)
7064 FIXME("%s -> %u ignored %s table values\n",
7065 action, count, debugstr_w(table));
7067 return ERROR_SUCCESS;
7070 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7072 static const WCHAR table[] = { 'P','a','t','c','h',0 };
7073 return msi_unimplemented_action_stub( package, "PatchFiles", table );
7076 static UINT ACTION_BindImage( MSIPACKAGE *package )
7078 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7079 return msi_unimplemented_action_stub( package, "BindImage", table );
7082 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7084 static const WCHAR table[] = {
7085 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7086 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7089 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7091 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7092 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
7095 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
7097 static const WCHAR table[] = {
7098 'M','s','i','A','s','s','e','m','b','l','y',0 };
7099 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
7102 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7104 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7105 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7108 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7110 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7111 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7114 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7116 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7117 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7120 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7122 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7123 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7126 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7128 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7129 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
7132 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7134 static const struct
7136 const WCHAR *action;
7137 UINT (*handler)(MSIPACKAGE *);
7139 StandardActions[] =
7141 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7142 { szAppSearch, ACTION_AppSearch },
7143 { szBindImage, ACTION_BindImage },
7144 { szCCPSearch, ACTION_CCPSearch },
7145 { szCostFinalize, ACTION_CostFinalize },
7146 { szCostInitialize, ACTION_CostInitialize },
7147 { szCreateFolders, ACTION_CreateFolders },
7148 { szCreateShortcuts, ACTION_CreateShortcuts },
7149 { szDeleteServices, ACTION_DeleteServices },
7150 { szDisableRollback, ACTION_DisableRollback },
7151 { szDuplicateFiles, ACTION_DuplicateFiles },
7152 { szExecuteAction, ACTION_ExecuteAction },
7153 { szFileCost, ACTION_FileCost },
7154 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7155 { szForceReboot, ACTION_ForceReboot },
7156 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7157 { szInstallExecute, ACTION_InstallExecute },
7158 { szInstallExecuteAgain, ACTION_InstallExecute },
7159 { szInstallFiles, ACTION_InstallFiles},
7160 { szInstallFinalize, ACTION_InstallFinalize },
7161 { szInstallInitialize, ACTION_InstallInitialize },
7162 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7163 { szInstallValidate, ACTION_InstallValidate },
7164 { szIsolateComponents, ACTION_IsolateComponents },
7165 { szLaunchConditions, ACTION_LaunchConditions },
7166 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7167 { szMoveFiles, ACTION_MoveFiles },
7168 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7169 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7170 { szInstallODBC, ACTION_InstallODBC },
7171 { szInstallServices, ACTION_InstallServices },
7172 { szPatchFiles, ACTION_PatchFiles },
7173 { szProcessComponents, ACTION_ProcessComponents },
7174 { szPublishComponents, ACTION_PublishComponents },
7175 { szPublishFeatures, ACTION_PublishFeatures },
7176 { szPublishProduct, ACTION_PublishProduct },
7177 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7178 { szRegisterComPlus, ACTION_RegisterComPlus},
7179 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7180 { szRegisterFonts, ACTION_RegisterFonts },
7181 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7182 { szRegisterProduct, ACTION_RegisterProduct },
7183 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7184 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7185 { szRegisterUser, ACTION_RegisterUser },
7186 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7187 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7188 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7189 { szRemoveFiles, ACTION_RemoveFiles },
7190 { szRemoveFolders, ACTION_RemoveFolders },
7191 { szRemoveIniValues, ACTION_RemoveIniValues },
7192 { szRemoveODBC, ACTION_RemoveODBC },
7193 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7194 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7195 { szResolveSource, ACTION_ResolveSource },
7196 { szRMCCPSearch, ACTION_RMCCPSearch },
7197 { szScheduleReboot, ACTION_ScheduleReboot },
7198 { szSelfRegModules, ACTION_SelfRegModules },
7199 { szSelfUnregModules, ACTION_SelfUnregModules },
7200 { szSetODBCFolders, ACTION_SetODBCFolders },
7201 { szStartServices, ACTION_StartServices },
7202 { szStopServices, ACTION_StopServices },
7203 { szUnpublishComponents, ACTION_UnpublishComponents },
7204 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7205 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7206 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7207 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7208 { szUnregisterFonts, ACTION_UnregisterFonts },
7209 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7210 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7211 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7212 { szValidateProductID, ACTION_ValidateProductID },
7213 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7214 { szWriteIniValues, ACTION_WriteIniValues },
7215 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7216 { NULL, NULL },
7219 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
7220 UINT* rc, BOOL force )
7222 BOOL ret = FALSE;
7223 BOOL run = force;
7224 int i;
7226 if (!run && !package->script->CurrentlyScripting)
7227 run = TRUE;
7229 if (!run)
7231 if (strcmpW(action,szInstallFinalize) == 0 ||
7232 strcmpW(action,szInstallExecute) == 0 ||
7233 strcmpW(action,szInstallExecuteAgain) == 0)
7234 run = TRUE;
7237 i = 0;
7238 while (StandardActions[i].action != NULL)
7240 if (strcmpW(StandardActions[i].action, action)==0)
7242 if (!run)
7244 ui_actioninfo(package, action, TRUE, 0);
7245 *rc = schedule_action(package,INSTALL_SCRIPT,action);
7246 ui_actioninfo(package, action, FALSE, *rc);
7248 else
7250 ui_actionstart(package, action);
7251 if (StandardActions[i].handler)
7253 *rc = StandardActions[i].handler(package);
7255 else
7257 FIXME("unhandled standard action %s\n",debugstr_w(action));
7258 *rc = ERROR_SUCCESS;
7261 ret = TRUE;
7262 break;
7264 i++;
7266 return ret;
7269 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
7271 UINT rc = ERROR_SUCCESS;
7272 BOOL handled;
7274 TRACE("Performing action (%s)\n", debugstr_w(action));
7276 handled = ACTION_HandleStandardAction(package, action, &rc, force);
7278 if (!handled)
7279 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
7281 if (!handled)
7283 WARN("unhandled msi action %s\n", debugstr_w(action));
7284 rc = ERROR_FUNCTION_NOT_CALLED;
7287 return rc;
7290 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7292 UINT rc = ERROR_SUCCESS;
7293 BOOL handled = FALSE;
7295 TRACE("Performing action (%s)\n", debugstr_w(action));
7297 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
7299 if (!handled)
7300 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7302 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7303 handled = TRUE;
7305 if (!handled)
7307 WARN("unhandled msi action %s\n", debugstr_w(action));
7308 rc = ERROR_FUNCTION_NOT_CALLED;
7311 return rc;
7314 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7316 UINT rc = ERROR_SUCCESS;
7317 MSIRECORD *row;
7319 static const WCHAR ExecSeqQuery[] =
7320 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7321 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7322 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7323 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7324 static const WCHAR UISeqQuery[] =
7325 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7326 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7327 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7328 ' ', '=',' ','%','i',0};
7330 if (needs_ui_sequence(package))
7331 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7332 else
7333 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7335 if (row)
7337 LPCWSTR action, cond;
7339 TRACE("Running the actions\n");
7341 /* check conditions */
7342 cond = MSI_RecordGetString(row, 2);
7344 /* this is a hack to skip errors in the condition code */
7345 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7347 msiobj_release(&row->hdr);
7348 return ERROR_SUCCESS;
7351 action = MSI_RecordGetString(row, 1);
7352 if (!action)
7354 ERR("failed to fetch action\n");
7355 msiobj_release(&row->hdr);
7356 return ERROR_FUNCTION_FAILED;
7359 if (needs_ui_sequence(package))
7360 rc = ACTION_PerformUIAction(package, action, -1);
7361 else
7362 rc = ACTION_PerformAction(package, action, -1, FALSE);
7364 msiobj_release(&row->hdr);
7367 return rc;
7370 /****************************************************
7371 * TOP level entry points
7372 *****************************************************/
7374 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7375 LPCWSTR szCommandLine )
7377 UINT rc;
7378 BOOL ui_exists;
7380 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7381 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7383 msi_set_property( package->db, szAction, szInstall );
7385 package->script->InWhatSequence = SEQUENCE_INSTALL;
7387 if (szPackagePath)
7389 LPWSTR p, dir;
7390 LPCWSTR file;
7392 dir = strdupW(szPackagePath);
7393 p = strrchrW(dir, '\\');
7394 if (p)
7396 *(++p) = 0;
7397 file = szPackagePath + (p - dir);
7399 else
7401 msi_free(dir);
7402 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7403 GetCurrentDirectoryW(MAX_PATH, dir);
7404 lstrcatW(dir, szBackSlash);
7405 file = szPackagePath;
7408 msi_free( package->PackagePath );
7409 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7410 if (!package->PackagePath)
7412 msi_free(dir);
7413 return ERROR_OUTOFMEMORY;
7416 lstrcpyW(package->PackagePath, dir);
7417 lstrcatW(package->PackagePath, file);
7418 msi_free(dir);
7420 msi_set_sourcedir_props(package, FALSE);
7423 msi_parse_command_line( package, szCommandLine, FALSE );
7425 msi_apply_transforms( package );
7426 msi_apply_patches( package );
7428 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7430 TRACE("setting reinstall property\n");
7431 msi_set_property( package->db, szReinstall, szAll );
7434 /* properties may have been added by a transform */
7435 msi_clone_properties( package );
7436 msi_set_context( package );
7438 if (needs_ui_sequence( package))
7440 package->script->InWhatSequence |= SEQUENCE_UI;
7441 rc = ACTION_ProcessUISequence(package);
7442 ui_exists = ui_sequence_exists(package);
7443 if (rc == ERROR_SUCCESS || !ui_exists)
7445 package->script->InWhatSequence |= SEQUENCE_EXEC;
7446 rc = ACTION_ProcessExecSequence(package, ui_exists);
7449 else
7450 rc = ACTION_ProcessExecSequence(package, FALSE);
7452 package->script->CurrentlyScripting = FALSE;
7454 /* process the ending type action */
7455 if (rc == ERROR_SUCCESS)
7456 ACTION_PerformActionSequence(package, -1);
7457 else if (rc == ERROR_INSTALL_USEREXIT)
7458 ACTION_PerformActionSequence(package, -2);
7459 else if (rc == ERROR_INSTALL_SUSPEND)
7460 ACTION_PerformActionSequence(package, -4);
7461 else /* failed */
7462 ACTION_PerformActionSequence(package, -3);
7464 /* finish up running custom actions */
7465 ACTION_FinishCustomActions(package);
7467 if (rc == ERROR_SUCCESS && package->need_reboot)
7468 return ERROR_SUCCESS_REBOOT_REQUIRED;
7470 return rc;