wined3d: Add Nvidia 8800GTX detection.
[wine.git] / dlls / msi / action.c
blobb101533f16893215bf8705fdf26d14394ff6e430
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 patch->state = MSIPATCHSTATE_APPLIED;
571 list_add_tail( &package->patches, &patch->entry );
572 return ERROR_SUCCESS;
575 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
577 static const WCHAR dotmsp[] = {'.','m','s','p',0};
578 MSIDATABASE *patch_db = NULL;
579 WCHAR localfile[MAX_PATH];
580 MSISUMMARYINFO *si;
581 MSIPATCHINFO *patch = NULL;
582 UINT r = ERROR_SUCCESS;
584 TRACE("%p %s\n", package, debugstr_w( file ) );
586 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
587 if ( r != ERROR_SUCCESS )
589 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
590 return r;
593 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
594 if (!si)
596 msiobj_release( &patch_db->hdr );
597 return ERROR_FUNCTION_FAILED;
600 r = msi_check_patch_applicable( package, si );
601 if (r != ERROR_SUCCESS)
603 TRACE("patch not applicable\n");
604 r = ERROR_SUCCESS;
605 goto done;
608 r = msi_parse_patch_summary( si, &patch );
609 if ( r != ERROR_SUCCESS )
610 goto done;
612 r = msi_get_local_package_name( localfile, dotmsp );
613 if ( r != ERROR_SUCCESS )
614 goto done;
616 TRACE("copying to local package %s\n", debugstr_w(localfile));
618 if (!CopyFileW( file, localfile, FALSE ))
620 ERR("Unable to copy package (%s -> %s) (error %u)\n",
621 debugstr_w(file), debugstr_w(localfile), GetLastError());
622 r = GetLastError();
623 goto done;
625 patch->localfile = strdupW( localfile );
627 r = msi_apply_patch_db( package, patch_db, patch );
628 if ( r != ERROR_SUCCESS )
629 WARN("patch failed to apply %u\n", r);
631 done:
632 msiobj_release( &si->hdr );
633 msiobj_release( &patch_db->hdr );
634 if (patch && r != ERROR_SUCCESS)
636 if (patch->localfile)
637 DeleteFileW( patch->localfile );
639 msi_free( patch->patchcode );
640 msi_free( patch->transforms );
641 msi_free( patch->localfile );
642 msi_free( patch );
644 return r;
647 /* get the PATCH property, and apply all the patches it specifies */
648 static UINT msi_apply_patches( MSIPACKAGE *package )
650 LPWSTR patch_list, *patches;
651 UINT i, r = ERROR_SUCCESS;
653 patch_list = msi_dup_property( package->db, szPatch );
655 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
657 patches = msi_split_string( patch_list, ';' );
658 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
659 r = msi_apply_patch_package( package, patches[i] );
661 msi_free( patches );
662 msi_free( patch_list );
664 return r;
667 static UINT msi_apply_transforms( MSIPACKAGE *package )
669 static const WCHAR szTransforms[] = {
670 'T','R','A','N','S','F','O','R','M','S',0 };
671 LPWSTR xform_list, *xforms;
672 UINT i, r = ERROR_SUCCESS;
674 xform_list = msi_dup_property( package->db, szTransforms );
675 xforms = msi_split_string( xform_list, ';' );
677 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
679 if (xforms[i][0] == ':')
680 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
681 else
682 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
685 msi_free( xforms );
686 msi_free( xform_list );
688 return r;
691 static BOOL ui_sequence_exists( MSIPACKAGE *package )
693 MSIQUERY *view;
694 UINT rc;
696 static const WCHAR ExecSeqQuery [] =
697 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
698 '`','I','n','s','t','a','l','l',
699 'U','I','S','e','q','u','e','n','c','e','`',
700 ' ','W','H','E','R','E',' ',
701 '`','S','e','q','u','e','n','c','e','`',' ',
702 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
703 '`','S','e','q','u','e','n','c','e','`',0};
705 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
706 if (rc == ERROR_SUCCESS)
708 msiobj_release(&view->hdr);
709 return TRUE;
712 return FALSE;
715 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
717 LPWSTR source, check;
719 if (msi_get_property_int( package->db, szInstalled, 0 ))
721 HKEY hkey;
723 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
724 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
725 RegCloseKey( hkey );
727 else
729 LPWSTR p, db;
730 DWORD len;
732 db = msi_dup_property( package->db, szOriginalDatabase );
733 if (!db)
734 return ERROR_OUTOFMEMORY;
736 p = strrchrW( db, '\\' );
737 if (!p)
739 p = strrchrW( db, '/' );
740 if (!p)
742 msi_free(db);
743 return ERROR_SUCCESS;
747 len = p - db + 2;
748 source = msi_alloc( len * sizeof(WCHAR) );
749 lstrcpynW( source, db, len );
750 msi_free( db );
753 check = msi_dup_property( package->db, cszSourceDir );
754 if (!check || replace)
756 UINT r = msi_set_property( package->db, cszSourceDir, source );
757 if (r == ERROR_SUCCESS)
758 msi_reset_folders( package, TRUE );
760 msi_free( check );
762 check = msi_dup_property( package->db, cszSOURCEDIR );
763 if (!check || replace)
764 msi_set_property( package->db, cszSOURCEDIR, source );
766 msi_free( check );
767 msi_free( source );
769 return ERROR_SUCCESS;
772 static BOOL needs_ui_sequence(MSIPACKAGE *package)
774 INT level = msi_get_property_int(package->db, szUILevel, 0);
775 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
778 UINT msi_set_context(MSIPACKAGE *package)
780 int num;
782 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
784 num = msi_get_property_int(package->db, szAllUsers, 0);
785 if (num == 1 || num == 2)
786 package->Context = MSIINSTALLCONTEXT_MACHINE;
788 return ERROR_SUCCESS;
791 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
793 UINT rc;
794 LPCWSTR cond, action;
795 MSIPACKAGE *package = param;
797 action = MSI_RecordGetString(row,1);
798 if (!action)
800 ERR("Error is retrieving action name\n");
801 return ERROR_FUNCTION_FAILED;
804 /* check conditions */
805 cond = MSI_RecordGetString(row,2);
807 /* this is a hack to skip errors in the condition code */
808 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
810 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
811 return ERROR_SUCCESS;
814 if (needs_ui_sequence(package))
815 rc = ACTION_PerformUIAction(package, action, -1);
816 else
817 rc = ACTION_PerformAction(package, action, -1, FALSE);
819 msi_dialog_check_messages( NULL );
821 if (package->CurrentInstallState != ERROR_SUCCESS)
822 rc = package->CurrentInstallState;
824 if (rc == ERROR_FUNCTION_NOT_CALLED)
825 rc = ERROR_SUCCESS;
827 if (rc != ERROR_SUCCESS)
828 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
830 return rc;
833 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
835 MSIQUERY * view;
836 UINT r;
837 static const WCHAR query[] =
838 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
839 '`','%','s','`',
840 ' ','W','H','E','R','E',' ',
841 '`','S','e','q','u','e','n','c','e','`',' ',
842 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
843 '`','S','e','q','u','e','n','c','e','`',0};
845 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
847 r = MSI_OpenQuery( package->db, &view, query, szTable );
848 if (r == ERROR_SUCCESS)
850 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
851 msiobj_release(&view->hdr);
854 return r;
857 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
859 MSIQUERY * view;
860 UINT rc;
861 static const WCHAR ExecSeqQuery[] =
862 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
863 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
864 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
865 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
866 'O','R','D','E','R',' ', 'B','Y',' ',
867 '`','S','e','q','u','e','n','c','e','`',0 };
868 static const WCHAR IVQuery[] =
869 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
870 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
871 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
872 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
873 ' ','\'', 'I','n','s','t','a','l','l',
874 'V','a','l','i','d','a','t','e','\'', 0};
875 INT seq = 0;
877 if (package->script->ExecuteSequenceRun)
879 TRACE("Execute Sequence already Run\n");
880 return ERROR_SUCCESS;
883 package->script->ExecuteSequenceRun = TRUE;
885 /* get the sequence number */
886 if (UIran)
888 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
889 if( !row )
890 return ERROR_FUNCTION_FAILED;
891 seq = MSI_RecordGetInteger(row,1);
892 msiobj_release(&row->hdr);
895 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
896 if (rc == ERROR_SUCCESS)
898 TRACE("Running the actions\n");
900 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
901 msiobj_release(&view->hdr);
904 return rc;
907 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
909 MSIQUERY * view;
910 UINT rc;
911 static const WCHAR ExecSeqQuery [] =
912 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
913 '`','I','n','s','t','a','l','l',
914 'U','I','S','e','q','u','e','n','c','e','`',
915 ' ','W','H','E','R','E',' ',
916 '`','S','e','q','u','e','n','c','e','`',' ',
917 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
918 '`','S','e','q','u','e','n','c','e','`',0};
920 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
921 if (rc == ERROR_SUCCESS)
923 TRACE("Running the actions\n");
925 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
926 msiobj_release(&view->hdr);
929 return rc;
932 /********************************************************
933 * ACTION helper functions and functions that perform the actions
934 *******************************************************/
935 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
936 UINT* rc, UINT script, BOOL force )
938 BOOL ret=FALSE;
939 UINT arc;
941 arc = ACTION_CustomAction(package, action, script, force);
943 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
945 *rc = arc;
946 ret = TRUE;
948 return ret;
952 * Actual Action Handlers
955 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
957 MSIPACKAGE *package = param;
958 LPCWSTR dir, component;
959 LPWSTR full_path;
960 MSIRECORD *uirow;
961 MSIFOLDER *folder;
962 MSICOMPONENT *comp;
964 component = MSI_RecordGetString(row, 2);
965 comp = get_loaded_component(package, component);
966 if (!comp)
967 return ERROR_SUCCESS;
969 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
971 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
972 comp->Action = comp->Installed;
973 return ERROR_SUCCESS;
975 comp->Action = INSTALLSTATE_LOCAL;
977 dir = MSI_RecordGetString(row,1);
978 if (!dir)
980 ERR("Unable to get folder id\n");
981 return ERROR_SUCCESS;
984 uirow = MSI_CreateRecord(1);
985 MSI_RecordSetStringW(uirow, 1, dir);
986 ui_actiondata(package, szCreateFolders, uirow);
987 msiobj_release(&uirow->hdr);
989 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
990 if (!full_path)
992 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
993 return ERROR_SUCCESS;
996 TRACE("Folder is %s\n",debugstr_w(full_path));
998 if (folder->State == 0)
999 create_full_pathW(full_path);
1001 folder->State = 3;
1003 msi_free(full_path);
1004 return ERROR_SUCCESS;
1007 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1009 static const WCHAR ExecSeqQuery[] =
1010 {'S','E','L','E','C','T',' ',
1011 '`','D','i','r','e','c','t','o','r','y','_','`',
1012 ' ','F','R','O','M',' ',
1013 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1014 UINT rc;
1015 MSIQUERY *view;
1017 /* create all the empty folders specified in the CreateFolder table */
1018 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1019 if (rc != ERROR_SUCCESS)
1020 return ERROR_SUCCESS;
1022 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1023 msiobj_release(&view->hdr);
1025 return rc;
1028 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1030 MSIPACKAGE *package = param;
1031 LPCWSTR dir, component;
1032 LPWSTR full_path;
1033 MSIRECORD *uirow;
1034 MSIFOLDER *folder;
1035 MSICOMPONENT *comp;
1037 component = MSI_RecordGetString(row, 2);
1038 comp = get_loaded_component(package, component);
1039 if (!comp)
1040 return ERROR_SUCCESS;
1042 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1044 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1045 comp->Action = comp->Installed;
1046 return ERROR_SUCCESS;
1048 comp->Action = INSTALLSTATE_ABSENT;
1050 dir = MSI_RecordGetString( row, 1 );
1051 if (!dir)
1053 ERR("Unable to get folder id\n");
1054 return ERROR_SUCCESS;
1057 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1058 if (!full_path)
1060 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1061 return ERROR_SUCCESS;
1064 TRACE("folder is %s\n", debugstr_w(full_path));
1066 uirow = MSI_CreateRecord( 1 );
1067 MSI_RecordSetStringW( uirow, 1, dir );
1068 ui_actiondata( package, szRemoveFolders, uirow );
1069 msiobj_release( &uirow->hdr );
1071 RemoveDirectoryW( full_path );
1072 folder->State = 0;
1074 msi_free( full_path );
1075 return ERROR_SUCCESS;
1078 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1080 static const WCHAR query[] =
1081 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1082 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1084 MSIQUERY *view;
1085 UINT rc;
1087 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1088 if (rc != ERROR_SUCCESS)
1089 return ERROR_SUCCESS;
1091 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1092 msiobj_release( &view->hdr );
1094 return rc;
1097 static UINT load_component( MSIRECORD *row, LPVOID param )
1099 MSIPACKAGE *package = param;
1100 MSICOMPONENT *comp;
1102 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1103 if (!comp)
1104 return ERROR_FUNCTION_FAILED;
1106 list_add_tail( &package->components, &comp->entry );
1108 /* fill in the data */
1109 comp->Component = msi_dup_record_field( row, 1 );
1111 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1113 comp->ComponentId = msi_dup_record_field( row, 2 );
1114 comp->Directory = msi_dup_record_field( row, 3 );
1115 comp->Attributes = MSI_RecordGetInteger(row,4);
1116 comp->Condition = msi_dup_record_field( row, 5 );
1117 comp->KeyPath = msi_dup_record_field( row, 6 );
1119 comp->Installed = INSTALLSTATE_UNKNOWN;
1120 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1122 return ERROR_SUCCESS;
1125 static UINT load_all_components( MSIPACKAGE *package )
1127 static const WCHAR query[] = {
1128 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1129 '`','C','o','m','p','o','n','e','n','t','`',0 };
1130 MSIQUERY *view;
1131 UINT r;
1133 if (!list_empty(&package->components))
1134 return ERROR_SUCCESS;
1136 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1137 if (r != ERROR_SUCCESS)
1138 return r;
1140 r = MSI_IterateRecords(view, NULL, load_component, package);
1141 msiobj_release(&view->hdr);
1142 return r;
1145 typedef struct {
1146 MSIPACKAGE *package;
1147 MSIFEATURE *feature;
1148 } _ilfs;
1150 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1152 ComponentList *cl;
1154 cl = msi_alloc( sizeof (*cl) );
1155 if ( !cl )
1156 return ERROR_NOT_ENOUGH_MEMORY;
1157 cl->component = comp;
1158 list_add_tail( &feature->Components, &cl->entry );
1160 return ERROR_SUCCESS;
1163 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1165 FeatureList *fl;
1167 fl = msi_alloc( sizeof(*fl) );
1168 if ( !fl )
1169 return ERROR_NOT_ENOUGH_MEMORY;
1170 fl->feature = child;
1171 list_add_tail( &parent->Children, &fl->entry );
1173 return ERROR_SUCCESS;
1176 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1178 _ilfs* ilfs = param;
1179 LPCWSTR component;
1180 MSICOMPONENT *comp;
1182 component = MSI_RecordGetString(row,1);
1184 /* check to see if the component is already loaded */
1185 comp = get_loaded_component( ilfs->package, component );
1186 if (!comp)
1188 ERR("unknown component %s\n", debugstr_w(component));
1189 return ERROR_FUNCTION_FAILED;
1192 add_feature_component( ilfs->feature, comp );
1193 comp->Enabled = TRUE;
1195 return ERROR_SUCCESS;
1198 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1200 MSIFEATURE *feature;
1202 if ( !name )
1203 return NULL;
1205 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1207 if ( !lstrcmpW( feature->Feature, name ) )
1208 return feature;
1211 return NULL;
1214 static UINT load_feature(MSIRECORD * row, LPVOID param)
1216 MSIPACKAGE* package = param;
1217 MSIFEATURE* feature;
1218 static const WCHAR Query1[] =
1219 {'S','E','L','E','C','T',' ',
1220 '`','C','o','m','p','o','n','e','n','t','_','`',
1221 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1222 'C','o','m','p','o','n','e','n','t','s','`',' ',
1223 'W','H','E','R','E',' ',
1224 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1225 MSIQUERY * view;
1226 UINT rc;
1227 _ilfs ilfs;
1229 /* fill in the data */
1231 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1232 if (!feature)
1233 return ERROR_NOT_ENOUGH_MEMORY;
1235 list_init( &feature->Children );
1236 list_init( &feature->Components );
1238 feature->Feature = msi_dup_record_field( row, 1 );
1240 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1242 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1243 feature->Title = msi_dup_record_field( row, 3 );
1244 feature->Description = msi_dup_record_field( row, 4 );
1246 if (!MSI_RecordIsNull(row,5))
1247 feature->Display = MSI_RecordGetInteger(row,5);
1249 feature->Level= MSI_RecordGetInteger(row,6);
1250 feature->Directory = msi_dup_record_field( row, 7 );
1251 feature->Attributes = MSI_RecordGetInteger(row,8);
1253 feature->Installed = INSTALLSTATE_UNKNOWN;
1254 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1256 list_add_tail( &package->features, &feature->entry );
1258 /* load feature components */
1260 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1261 if (rc != ERROR_SUCCESS)
1262 return ERROR_SUCCESS;
1264 ilfs.package = package;
1265 ilfs.feature = feature;
1267 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1268 msiobj_release(&view->hdr);
1270 return ERROR_SUCCESS;
1273 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1275 MSIPACKAGE* package = param;
1276 MSIFEATURE *parent, *child;
1278 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1279 if (!child)
1280 return ERROR_FUNCTION_FAILED;
1282 if (!child->Feature_Parent)
1283 return ERROR_SUCCESS;
1285 parent = find_feature_by_name( package, child->Feature_Parent );
1286 if (!parent)
1287 return ERROR_FUNCTION_FAILED;
1289 add_feature_child( parent, child );
1290 return ERROR_SUCCESS;
1293 static UINT load_all_features( MSIPACKAGE *package )
1295 static const WCHAR query[] = {
1296 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1297 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1298 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1299 MSIQUERY *view;
1300 UINT r;
1302 if (!list_empty(&package->features))
1303 return ERROR_SUCCESS;
1305 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1306 if (r != ERROR_SUCCESS)
1307 return r;
1309 r = MSI_IterateRecords( view, NULL, load_feature, package );
1310 if (r != ERROR_SUCCESS)
1311 return r;
1313 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1314 msiobj_release( &view->hdr );
1316 return r;
1319 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1321 if (!p)
1322 return p;
1323 p = strchrW(p, ch);
1324 if (!p)
1325 return p;
1326 *p = 0;
1327 return p+1;
1330 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1332 static const WCHAR query[] = {
1333 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1334 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1335 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1336 MSIQUERY *view = NULL;
1337 MSIRECORD *row = NULL;
1338 UINT r;
1340 TRACE("%s\n", debugstr_w(file->File));
1342 r = MSI_OpenQuery(package->db, &view, query, file->File);
1343 if (r != ERROR_SUCCESS)
1344 goto done;
1346 r = MSI_ViewExecute(view, NULL);
1347 if (r != ERROR_SUCCESS)
1348 goto done;
1350 r = MSI_ViewFetch(view, &row);
1351 if (r != ERROR_SUCCESS)
1352 goto done;
1354 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1355 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1356 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1357 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1358 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1360 done:
1361 if (view) msiobj_release(&view->hdr);
1362 if (row) msiobj_release(&row->hdr);
1363 return r;
1366 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1368 MSIRECORD *row;
1369 static const WCHAR query[] = {
1370 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1371 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1372 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1374 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1375 if (!row)
1377 WARN("query failed\n");
1378 return ERROR_FUNCTION_FAILED;
1381 file->disk_id = MSI_RecordGetInteger( row, 1 );
1382 msiobj_release( &row->hdr );
1383 return ERROR_SUCCESS;
1386 static UINT load_file(MSIRECORD *row, LPVOID param)
1388 MSIPACKAGE* package = param;
1389 LPCWSTR component;
1390 MSIFILE *file;
1392 /* fill in the data */
1394 file = msi_alloc_zero( sizeof (MSIFILE) );
1395 if (!file)
1396 return ERROR_NOT_ENOUGH_MEMORY;
1398 file->File = msi_dup_record_field( row, 1 );
1400 component = MSI_RecordGetString( row, 2 );
1401 file->Component = get_loaded_component( package, component );
1403 if (!file->Component)
1405 WARN("Component not found: %s\n", debugstr_w(component));
1406 msi_free(file->File);
1407 msi_free(file);
1408 return ERROR_SUCCESS;
1411 file->FileName = msi_dup_record_field( row, 3 );
1412 reduce_to_longfilename( file->FileName );
1414 file->ShortName = msi_dup_record_field( row, 3 );
1415 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1417 file->FileSize = MSI_RecordGetInteger( row, 4 );
1418 file->Version = msi_dup_record_field( row, 5 );
1419 file->Language = msi_dup_record_field( row, 6 );
1420 file->Attributes = MSI_RecordGetInteger( row, 7 );
1421 file->Sequence = MSI_RecordGetInteger( row, 8 );
1423 file->state = msifs_invalid;
1425 /* if the compressed bits are not set in the file attributes,
1426 * then read the information from the package word count property
1428 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1430 file->IsCompressed = FALSE;
1432 else if (file->Attributes &
1433 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1435 file->IsCompressed = TRUE;
1437 else if (file->Attributes & msidbFileAttributesNoncompressed)
1439 file->IsCompressed = FALSE;
1441 else
1443 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1446 load_file_hash(package, file);
1447 load_file_disk_id(package, file);
1449 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1451 list_add_tail( &package->files, &file->entry );
1453 return ERROR_SUCCESS;
1456 static UINT load_all_files(MSIPACKAGE *package)
1458 MSIQUERY * view;
1459 UINT rc;
1460 static const WCHAR Query[] =
1461 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1462 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1463 '`','S','e','q','u','e','n','c','e','`', 0};
1465 if (!list_empty(&package->files))
1466 return ERROR_SUCCESS;
1468 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1469 if (rc != ERROR_SUCCESS)
1470 return ERROR_SUCCESS;
1472 rc = MSI_IterateRecords(view, NULL, load_file, package);
1473 msiobj_release(&view->hdr);
1475 return ERROR_SUCCESS;
1478 static UINT load_folder( MSIRECORD *row, LPVOID param )
1480 MSIPACKAGE *package = param;
1481 static WCHAR szEmpty[] = { 0 };
1482 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1483 MSIFOLDER *folder;
1485 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1486 if (!folder)
1487 return ERROR_NOT_ENOUGH_MEMORY;
1489 folder->Directory = msi_dup_record_field( row, 1 );
1491 TRACE("%s\n", debugstr_w(folder->Directory));
1493 p = msi_dup_record_field(row, 3);
1495 /* split src and target dir */
1496 tgt_short = p;
1497 src_short = folder_split_path( p, ':' );
1499 /* split the long and short paths */
1500 tgt_long = folder_split_path( tgt_short, '|' );
1501 src_long = folder_split_path( src_short, '|' );
1503 /* check for no-op dirs */
1504 if (!lstrcmpW(szDot, tgt_short))
1505 tgt_short = szEmpty;
1506 if (!lstrcmpW(szDot, src_short))
1507 src_short = szEmpty;
1509 if (!tgt_long)
1510 tgt_long = tgt_short;
1512 if (!src_short) {
1513 src_short = tgt_short;
1514 src_long = tgt_long;
1517 if (!src_long)
1518 src_long = src_short;
1520 /* FIXME: use the target short path too */
1521 folder->TargetDefault = strdupW(tgt_long);
1522 folder->SourceShortPath = strdupW(src_short);
1523 folder->SourceLongPath = strdupW(src_long);
1524 msi_free(p);
1526 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1527 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1528 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1530 folder->Parent = msi_dup_record_field( row, 2 );
1532 folder->Property = msi_dup_property( package->db, folder->Directory );
1534 list_add_tail( &package->folders, &folder->entry );
1536 TRACE("returning %p\n", folder);
1538 return ERROR_SUCCESS;
1541 static UINT load_all_folders( MSIPACKAGE *package )
1543 static const WCHAR query[] = {
1544 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1545 '`','D','i','r','e','c','t','o','r','y','`',0 };
1546 MSIQUERY *view;
1547 UINT r;
1549 if (!list_empty(&package->folders))
1550 return ERROR_SUCCESS;
1552 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1553 if (r != ERROR_SUCCESS)
1554 return r;
1556 r = MSI_IterateRecords(view, NULL, load_folder, package);
1557 msiobj_release(&view->hdr);
1558 return r;
1562 * I am not doing any of the costing functionality yet.
1563 * Mostly looking at doing the Component and Feature loading
1565 * The native MSI does A LOT of modification to tables here. Mostly adding
1566 * a lot of temporary columns to the Feature and Component tables.
1568 * note: Native msi also tracks the short filename. But I am only going to
1569 * track the long ones. Also looking at this directory table
1570 * it appears that the directory table does not get the parents
1571 * resolved base on property only based on their entries in the
1572 * directory table.
1574 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1576 static const WCHAR szCosting[] =
1577 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1579 msi_set_property( package->db, szCosting, szZero );
1580 msi_set_property( package->db, cszRootDrive, c_colon );
1582 load_all_folders( package );
1583 load_all_components( package );
1584 load_all_features( package );
1585 load_all_files( package );
1587 return ERROR_SUCCESS;
1590 static UINT execute_script(MSIPACKAGE *package, UINT script )
1592 UINT i;
1593 UINT rc = ERROR_SUCCESS;
1595 TRACE("Executing Script %i\n",script);
1597 if (!package->script)
1599 ERR("no script!\n");
1600 return ERROR_FUNCTION_FAILED;
1603 for (i = 0; i < package->script->ActionCount[script]; i++)
1605 LPWSTR action;
1606 action = package->script->Actions[script][i];
1607 ui_actionstart(package, action);
1608 TRACE("Executing Action (%s)\n",debugstr_w(action));
1609 rc = ACTION_PerformAction(package, action, script, TRUE);
1610 if (rc != ERROR_SUCCESS)
1611 break;
1613 msi_free_action_script(package, script);
1614 return rc;
1617 static UINT ACTION_FileCost(MSIPACKAGE *package)
1619 return ERROR_SUCCESS;
1622 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1624 MSICOMPONENT *comp;
1625 INSTALLSTATE state;
1626 UINT r;
1628 state = MsiQueryProductStateW(package->ProductCode);
1630 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1632 if (!comp->ComponentId)
1633 continue;
1635 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1636 comp->Installed = INSTALLSTATE_ABSENT;
1637 else
1639 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1640 package->Context, comp->ComponentId,
1641 &comp->Installed);
1642 if (r != ERROR_SUCCESS)
1643 comp->Installed = INSTALLSTATE_ABSENT;
1648 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1650 MSIFEATURE *feature;
1651 INSTALLSTATE state;
1653 state = MsiQueryProductStateW(package->ProductCode);
1655 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1657 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1658 feature->Installed = INSTALLSTATE_ABSENT;
1659 else
1661 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1662 feature->Feature);
1667 static BOOL process_state_property(MSIPACKAGE* package, int level,
1668 LPCWSTR property, INSTALLSTATE state)
1670 LPWSTR override;
1671 MSIFEATURE *feature;
1673 override = msi_dup_property( package->db, property );
1674 if (!override)
1675 return FALSE;
1677 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1679 if (lstrcmpW(property, szRemove) &&
1680 (feature->Level <= 0 || feature->Level > level))
1681 continue;
1683 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1685 if (strcmpiW(override, szAll)==0)
1686 msi_feature_set_state(package, feature, state);
1687 else
1689 LPWSTR ptr = override;
1690 LPWSTR ptr2 = strchrW(override,',');
1692 while (ptr)
1694 int len = ptr2 - ptr;
1696 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1697 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1699 msi_feature_set_state(package, feature, state);
1700 break;
1702 if (ptr2)
1704 ptr=ptr2+1;
1705 ptr2 = strchrW(ptr,',');
1707 else
1708 break;
1712 msi_free(override);
1714 return TRUE;
1717 static BOOL process_overrides( MSIPACKAGE *package, int level )
1719 static const WCHAR szAddLocal[] =
1720 {'A','D','D','L','O','C','A','L',0};
1721 static const WCHAR szAddSource[] =
1722 {'A','D','D','S','O','U','R','C','E',0};
1723 static const WCHAR szAdvertise[] =
1724 {'A','D','V','E','R','T','I','S','E',0};
1725 BOOL ret = FALSE;
1727 /* all these activation/deactivation things happen in order and things
1728 * later on the list override things earlier on the list.
1730 * 0 INSTALLLEVEL processing
1731 * 1 ADDLOCAL
1732 * 2 REMOVE
1733 * 3 ADDSOURCE
1734 * 4 ADDDEFAULT
1735 * 5 REINSTALL
1736 * 6 ADVERTISE
1737 * 7 COMPADDLOCAL
1738 * 8 COMPADDSOURCE
1739 * 9 FILEADDLOCAL
1740 * 10 FILEADDSOURCE
1741 * 11 FILEADDDEFAULT
1743 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1744 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1745 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1746 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1747 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1749 if (ret)
1750 msi_set_property( package->db, szPreselected, szOne );
1752 return ret;
1755 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1757 int level;
1758 static const WCHAR szlevel[] =
1759 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1760 MSICOMPONENT* component;
1761 MSIFEATURE *feature;
1763 TRACE("Checking Install Level\n");
1765 level = msi_get_property_int(package->db, szlevel, 1);
1767 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1769 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1771 BOOL feature_state = ((feature->Level > 0) &&
1772 (feature->Level <= level));
1774 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1776 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1777 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1778 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1779 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1780 else
1781 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1785 /* disable child features of unselected parent features */
1786 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1788 FeatureList *fl;
1790 if (feature->Level > 0 && feature->Level <= level)
1791 continue;
1793 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1794 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1799 * now we want to enable or disable components base on feature
1802 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1804 ComponentList *cl;
1806 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1807 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1809 if (!feature->Level)
1810 continue;
1812 /* features with components that have compressed files are made local */
1813 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1815 if (cl->component->Enabled &&
1816 cl->component->ForceLocalState &&
1817 feature->Action == INSTALLSTATE_SOURCE)
1819 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1820 break;
1824 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1826 component = cl->component;
1828 if (!component->Enabled)
1829 continue;
1831 switch (feature->Action)
1833 case INSTALLSTATE_ABSENT:
1834 component->anyAbsent = 1;
1835 break;
1836 case INSTALLSTATE_ADVERTISED:
1837 component->hasAdvertiseFeature = 1;
1838 break;
1839 case INSTALLSTATE_SOURCE:
1840 component->hasSourceFeature = 1;
1841 break;
1842 case INSTALLSTATE_LOCAL:
1843 component->hasLocalFeature = 1;
1844 break;
1845 case INSTALLSTATE_DEFAULT:
1846 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1847 component->hasAdvertiseFeature = 1;
1848 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1849 component->hasSourceFeature = 1;
1850 else
1851 component->hasLocalFeature = 1;
1852 break;
1853 default:
1854 break;
1859 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1861 /* if the component isn't enabled, leave it alone */
1862 if (!component->Enabled)
1863 continue;
1865 /* check if it's local or source */
1866 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1867 (component->hasLocalFeature || component->hasSourceFeature))
1869 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1870 !component->ForceLocalState)
1871 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1872 else
1873 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1874 continue;
1877 /* if any feature is local, the component must be local too */
1878 if (component->hasLocalFeature)
1880 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1881 continue;
1884 if (component->hasSourceFeature)
1886 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1887 continue;
1890 if (component->hasAdvertiseFeature)
1892 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1893 continue;
1896 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1897 if (component->anyAbsent)
1898 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1901 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1903 if (component->Action == INSTALLSTATE_DEFAULT)
1905 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1906 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1909 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1910 debugstr_w(component->Component), component->Installed, component->Action);
1914 return ERROR_SUCCESS;
1917 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1919 MSIPACKAGE *package = param;
1920 LPCWSTR name;
1921 LPWSTR path;
1922 MSIFOLDER *f;
1924 name = MSI_RecordGetString(row,1);
1926 f = get_loaded_folder(package, name);
1927 if (!f) return ERROR_SUCCESS;
1929 /* reset the ResolvedTarget */
1930 msi_free(f->ResolvedTarget);
1931 f->ResolvedTarget = NULL;
1933 /* This helper function now does ALL the work */
1934 TRACE("Dir %s ...\n",debugstr_w(name));
1935 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1936 TRACE("resolves to %s\n",debugstr_w(path));
1937 msi_free(path);
1939 return ERROR_SUCCESS;
1942 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1944 MSIPACKAGE *package = param;
1945 LPCWSTR name;
1946 MSIFEATURE *feature;
1948 name = MSI_RecordGetString( row, 1 );
1950 feature = get_loaded_feature( package, name );
1951 if (!feature)
1952 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1953 else
1955 LPCWSTR Condition;
1956 Condition = MSI_RecordGetString(row,3);
1958 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1960 int level = MSI_RecordGetInteger(row,2);
1961 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1962 feature->Level = level;
1965 return ERROR_SUCCESS;
1968 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1970 static const WCHAR name[] = {'\\',0};
1971 VS_FIXEDFILEINFO *ret;
1972 LPVOID version;
1973 DWORD versize, handle;
1974 UINT sz;
1976 TRACE("%s\n", debugstr_w(filename));
1978 versize = GetFileVersionInfoSizeW( filename, &handle );
1979 if (!versize)
1980 return NULL;
1982 version = msi_alloc( versize );
1983 if (!version)
1984 return NULL;
1986 GetFileVersionInfoW( filename, 0, versize, version );
1988 if (!VerQueryValueW( version, name, (LPVOID *)&ret, &sz ))
1990 msi_free( version );
1991 return NULL;
1994 msi_free( version );
1995 return ret;
1998 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2000 DWORD ms, ls;
2002 msi_parse_version_string( version, &ms, &ls );
2004 if (fi->dwFileVersionMS > ms) return 1;
2005 else if (fi->dwFileVersionMS < ms) return -1;
2006 else if (fi->dwFileVersionLS > ls) return 1;
2007 else if (fi->dwFileVersionLS < ls) return -1;
2008 return 0;
2011 static DWORD get_disk_file_size( LPCWSTR filename )
2013 HANDLE file;
2014 DWORD size;
2016 TRACE("%s\n", debugstr_w(filename));
2018 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2019 if (file == INVALID_HANDLE_VALUE)
2020 return INVALID_FILE_SIZE;
2022 size = GetFileSize( file, NULL );
2023 CloseHandle( file );
2024 return size;
2027 static BOOL hash_matches( MSIFILE *file )
2029 UINT r;
2030 MSIFILEHASHINFO hash;
2032 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2033 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2034 if (r != ERROR_SUCCESS)
2035 return FALSE;
2037 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2040 static UINT set_file_install_states( MSIPACKAGE *package )
2042 VS_FIXEDFILEINFO *file_version;
2043 MSIFILE *file;
2045 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2047 MSICOMPONENT* comp = file->Component;
2048 DWORD file_size;
2049 LPWSTR p;
2051 if (!comp)
2052 continue;
2054 if (file->IsCompressed)
2055 comp->ForceLocalState = TRUE;
2057 /* calculate target */
2058 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2059 msi_free(file->TargetPath);
2061 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2063 file->TargetPath = build_directory_name(2, p, file->FileName);
2064 msi_free(p);
2066 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2068 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2070 file->state = msifs_missing;
2071 comp->Cost += file->FileSize;
2072 continue;
2074 if (file->Version && (file_version = msi_get_disk_file_version( file->TargetPath )))
2076 TRACE("new %s old %u.%u.%u.%u\n", debugstr_w(file->Version),
2077 HIWORD(file_version->dwFileVersionMS),
2078 LOWORD(file_version->dwFileVersionMS),
2079 HIWORD(file_version->dwFileVersionLS),
2080 LOWORD(file_version->dwFileVersionLS));
2082 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2084 file->state = msifs_overwrite;
2085 comp->Cost += file->FileSize;
2087 else
2089 TRACE("Destination file version equal or greater, not overwriting\n");
2090 file->state = msifs_present;
2092 msi_free( file_version );
2093 continue;
2095 if ((file_size = get_disk_file_size( file->TargetPath )) != file->FileSize)
2097 file->state = msifs_overwrite;
2098 comp->Cost += file->FileSize - file_size;
2099 continue;
2101 if (file->hash.dwFileHashInfoSize && hash_matches( file ))
2103 TRACE("File hashes match, not overwriting\n");
2104 file->state = msifs_present;
2105 continue;
2107 file->state = msifs_overwrite;
2108 comp->Cost += file->FileSize - file_size;
2111 return ERROR_SUCCESS;
2115 * A lot is done in this function aside from just the costing.
2116 * The costing needs to be implemented at some point but for now I am going
2117 * to focus on the directory building
2120 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2122 static const WCHAR ExecSeqQuery[] =
2123 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2124 '`','D','i','r','e','c','t','o','r','y','`',0};
2125 static const WCHAR ConditionQuery[] =
2126 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2127 '`','C','o','n','d','i','t','i','o','n','`',0};
2128 static const WCHAR szCosting[] =
2129 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2130 static const WCHAR szlevel[] =
2131 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2132 static const WCHAR szOutOfDiskSpace[] =
2133 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2134 MSICOMPONENT *comp;
2135 UINT rc = ERROR_SUCCESS;
2136 MSIQUERY * view;
2137 LPWSTR level;
2139 TRACE("Building Directory properties\n");
2141 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2142 if (rc == ERROR_SUCCESS)
2144 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2145 package);
2146 msiobj_release(&view->hdr);
2149 /* read components states from the registry */
2150 ACTION_GetComponentInstallStates(package);
2151 ACTION_GetFeatureInstallStates(package);
2153 TRACE("Calculating file install states\n");
2154 set_file_install_states( package );
2156 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2158 TRACE("Evaluating feature conditions\n");
2160 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2161 if (rc == ERROR_SUCCESS)
2163 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2164 msiobj_release( &view->hdr );
2167 TRACE("Evaluating component conditions\n");
2169 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2171 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2173 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2174 comp->Enabled = FALSE;
2176 else
2177 comp->Enabled = TRUE;
2180 msi_set_property( package->db, szCosting, szOne );
2181 /* set default run level if not set */
2182 level = msi_dup_property( package->db, szlevel );
2183 if (!level)
2184 msi_set_property( package->db, szlevel, szOne );
2185 msi_free(level);
2187 /* FIXME: check volume disk space */
2188 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2190 return MSI_SetFeatureStates(package);
2193 /* OK this value is "interpreted" and then formatted based on the
2194 first few characters */
2195 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2196 DWORD *size)
2198 LPSTR data = NULL;
2200 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2202 if (value[1]=='x')
2204 LPWSTR ptr;
2205 CHAR byte[5];
2206 LPWSTR deformated = NULL;
2207 int count;
2209 deformat_string(package, &value[2], &deformated);
2211 /* binary value type */
2212 ptr = deformated;
2213 *type = REG_BINARY;
2214 if (strlenW(ptr)%2)
2215 *size = (strlenW(ptr)/2)+1;
2216 else
2217 *size = strlenW(ptr)/2;
2219 data = msi_alloc(*size);
2221 byte[0] = '0';
2222 byte[1] = 'x';
2223 byte[4] = 0;
2224 count = 0;
2225 /* if uneven pad with a zero in front */
2226 if (strlenW(ptr)%2)
2228 byte[2]= '0';
2229 byte[3]= *ptr;
2230 ptr++;
2231 data[count] = (BYTE)strtol(byte,NULL,0);
2232 count ++;
2233 TRACE("Uneven byte count\n");
2235 while (*ptr)
2237 byte[2]= *ptr;
2238 ptr++;
2239 byte[3]= *ptr;
2240 ptr++;
2241 data[count] = (BYTE)strtol(byte,NULL,0);
2242 count ++;
2244 msi_free(deformated);
2246 TRACE("Data %i bytes(%i)\n",*size,count);
2248 else
2250 LPWSTR deformated;
2251 LPWSTR p;
2252 DWORD d = 0;
2253 deformat_string(package, &value[1], &deformated);
2255 *type=REG_DWORD;
2256 *size = sizeof(DWORD);
2257 data = msi_alloc(*size);
2258 p = deformated;
2259 if (*p == '-')
2260 p++;
2261 while (*p)
2263 if ( (*p < '0') || (*p > '9') )
2264 break;
2265 d *= 10;
2266 d += (*p - '0');
2267 p++;
2269 if (deformated[0] == '-')
2270 d = -d;
2271 *(LPDWORD)data = d;
2272 TRACE("DWORD %i\n",*(LPDWORD)data);
2274 msi_free(deformated);
2277 else
2279 static const WCHAR szMulti[] = {'[','~',']',0};
2280 LPCWSTR ptr;
2281 *type=REG_SZ;
2283 if (value[0]=='#')
2285 if (value[1]=='%')
2287 ptr = &value[2];
2288 *type=REG_EXPAND_SZ;
2290 else
2291 ptr = &value[1];
2293 else
2294 ptr=value;
2296 if (strstrW(value,szMulti))
2297 *type = REG_MULTI_SZ;
2299 /* remove initial delimiter */
2300 if (!strncmpW(value, szMulti, 3))
2301 ptr = value + 3;
2303 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2305 /* add double NULL terminator */
2306 if (*type == REG_MULTI_SZ)
2308 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2309 data = msi_realloc_zero(data, *size);
2312 return data;
2315 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2317 const WCHAR *ret;
2319 switch (root)
2321 case -1:
2322 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2324 *root_key = HKEY_LOCAL_MACHINE;
2325 ret = szHLM;
2327 else
2329 *root_key = HKEY_CURRENT_USER;
2330 ret = szHCU;
2332 break;
2333 case 0:
2334 *root_key = HKEY_CLASSES_ROOT;
2335 ret = szHCR;
2336 break;
2337 case 1:
2338 *root_key = HKEY_CURRENT_USER;
2339 ret = szHCU;
2340 break;
2341 case 2:
2342 *root_key = HKEY_LOCAL_MACHINE;
2343 ret = szHLM;
2344 break;
2345 case 3:
2346 *root_key = HKEY_USERS;
2347 ret = szHU;
2348 break;
2349 default:
2350 ERR("Unknown root %i\n", root);
2351 return NULL;
2354 return ret;
2357 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2359 MSIPACKAGE *package = param;
2360 LPSTR value_data = NULL;
2361 HKEY root_key, hkey;
2362 DWORD type,size;
2363 LPWSTR deformated;
2364 LPCWSTR szRoot, component, name, key, value;
2365 MSICOMPONENT *comp;
2366 MSIRECORD * uirow;
2367 LPWSTR uikey;
2368 INT root;
2369 BOOL check_first = FALSE;
2370 UINT rc;
2372 ui_progress(package,2,0,0,0);
2374 value = NULL;
2375 key = NULL;
2376 uikey = NULL;
2377 name = NULL;
2379 component = MSI_RecordGetString(row, 6);
2380 comp = get_loaded_component(package,component);
2381 if (!comp)
2382 return ERROR_SUCCESS;
2384 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2386 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2387 comp->Action = comp->Installed;
2388 return ERROR_SUCCESS;
2390 comp->Action = INSTALLSTATE_LOCAL;
2392 name = MSI_RecordGetString(row, 4);
2393 if( MSI_RecordIsNull(row,5) && name )
2395 /* null values can have special meanings */
2396 if (name[0]=='-' && name[1] == 0)
2397 return ERROR_SUCCESS;
2398 else if ((name[0]=='+' && name[1] == 0) ||
2399 (name[0] == '*' && name[1] == 0))
2400 name = NULL;
2401 check_first = TRUE;
2404 root = MSI_RecordGetInteger(row,2);
2405 key = MSI_RecordGetString(row, 3);
2407 szRoot = get_root_key( package, root, &root_key );
2408 if (!szRoot)
2409 return ERROR_SUCCESS;
2411 deformat_string(package, key , &deformated);
2412 size = strlenW(deformated) + strlenW(szRoot) + 1;
2413 uikey = msi_alloc(size*sizeof(WCHAR));
2414 strcpyW(uikey,szRoot);
2415 strcatW(uikey,deformated);
2417 if (RegCreateKeyW( root_key, deformated, &hkey))
2419 ERR("Could not create key %s\n",debugstr_w(deformated));
2420 msi_free(deformated);
2421 msi_free(uikey);
2422 return ERROR_SUCCESS;
2424 msi_free(deformated);
2426 value = MSI_RecordGetString(row,5);
2427 if (value)
2428 value_data = parse_value(package, value, &type, &size);
2429 else
2431 value_data = (LPSTR)strdupW(szEmpty);
2432 size = sizeof(szEmpty);
2433 type = REG_SZ;
2436 deformat_string(package, name, &deformated);
2438 if (!check_first)
2440 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2441 debugstr_w(uikey));
2442 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2444 else
2446 DWORD sz = 0;
2447 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2448 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2450 TRACE("value %s of %s checked already exists\n",
2451 debugstr_w(deformated), debugstr_w(uikey));
2453 else
2455 TRACE("Checked and setting value %s of %s\n",
2456 debugstr_w(deformated), debugstr_w(uikey));
2457 if (deformated || size)
2458 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2461 RegCloseKey(hkey);
2463 uirow = MSI_CreateRecord(3);
2464 MSI_RecordSetStringW(uirow,2,deformated);
2465 MSI_RecordSetStringW(uirow,1,uikey);
2466 if (type == REG_SZ || type == REG_EXPAND_SZ)
2467 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2468 ui_actiondata(package,szWriteRegistryValues,uirow);
2469 msiobj_release( &uirow->hdr );
2471 msi_free(value_data);
2472 msi_free(deformated);
2473 msi_free(uikey);
2475 return ERROR_SUCCESS;
2478 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2480 UINT rc;
2481 MSIQUERY * view;
2482 static const WCHAR ExecSeqQuery[] =
2483 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2484 '`','R','e','g','i','s','t','r','y','`',0 };
2486 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2487 if (rc != ERROR_SUCCESS)
2488 return ERROR_SUCCESS;
2490 /* increment progress bar each time action data is sent */
2491 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2493 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2495 msiobj_release(&view->hdr);
2496 return rc;
2499 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2501 LONG res;
2502 HKEY hkey;
2503 DWORD num_subkeys, num_values;
2505 if (delete_key)
2507 if ((res = RegDeleteTreeW( hkey_root, key )))
2509 WARN("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2511 return;
2514 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2516 if ((res = RegDeleteValueW( hkey, value )))
2518 WARN("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2520 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2521 NULL, NULL, NULL, NULL );
2522 RegCloseKey( hkey );
2524 if (!res && !num_subkeys && !num_values)
2526 TRACE("Removing empty key %s\n", debugstr_w(key));
2527 RegDeleteKeyW( hkey_root, key );
2529 return;
2531 WARN("Failed to open key %s (%d)\n", debugstr_w(key), res);
2535 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2537 MSIPACKAGE *package = param;
2538 LPCWSTR component, name, key_str, root_key_str;
2539 LPWSTR deformated_key, deformated_name, ui_key_str;
2540 MSICOMPONENT *comp;
2541 MSIRECORD *uirow;
2542 BOOL delete_key = FALSE;
2543 HKEY hkey_root;
2544 UINT size;
2545 INT root;
2547 ui_progress( package, 2, 0, 0, 0 );
2549 component = MSI_RecordGetString( row, 6 );
2550 comp = get_loaded_component( package, component );
2551 if (!comp)
2552 return ERROR_SUCCESS;
2554 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2556 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2557 comp->Action = comp->Installed;
2558 return ERROR_SUCCESS;
2560 comp->Action = INSTALLSTATE_ABSENT;
2562 name = MSI_RecordGetString( row, 4 );
2563 if (MSI_RecordIsNull( row, 5 ) && name )
2565 if (name[0] == '+' && !name[1])
2566 return ERROR_SUCCESS;
2567 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2569 delete_key = TRUE;
2570 name = NULL;
2574 root = MSI_RecordGetInteger( row, 2 );
2575 key_str = MSI_RecordGetString( row, 3 );
2577 root_key_str = get_root_key( package, root, &hkey_root );
2578 if (!root_key_str)
2579 return ERROR_SUCCESS;
2581 deformat_string( package, key_str, &deformated_key );
2582 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2583 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2584 strcpyW( ui_key_str, root_key_str );
2585 strcatW( ui_key_str, deformated_key );
2587 deformat_string( package, name, &deformated_name );
2589 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2590 msi_free( deformated_key );
2592 uirow = MSI_CreateRecord( 2 );
2593 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2594 MSI_RecordSetStringW( uirow, 2, deformated_name );
2596 ui_actiondata( package, szRemoveRegistryValues, uirow );
2597 msiobj_release( &uirow->hdr );
2599 msi_free( ui_key_str );
2600 msi_free( deformated_name );
2601 return ERROR_SUCCESS;
2604 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2606 MSIPACKAGE *package = param;
2607 LPCWSTR component, name, key_str, root_key_str;
2608 LPWSTR deformated_key, deformated_name, ui_key_str;
2609 MSICOMPONENT *comp;
2610 MSIRECORD *uirow;
2611 BOOL delete_key = FALSE;
2612 HKEY hkey_root;
2613 UINT size;
2614 INT root;
2616 ui_progress( package, 2, 0, 0, 0 );
2618 component = MSI_RecordGetString( row, 5 );
2619 comp = get_loaded_component( package, component );
2620 if (!comp)
2621 return ERROR_SUCCESS;
2623 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2625 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2626 comp->Action = comp->Installed;
2627 return ERROR_SUCCESS;
2629 comp->Action = INSTALLSTATE_LOCAL;
2631 if ((name = MSI_RecordGetString( row, 4 )))
2633 if (name[0] == '-' && !name[1])
2635 delete_key = TRUE;
2636 name = NULL;
2640 root = MSI_RecordGetInteger( row, 2 );
2641 key_str = MSI_RecordGetString( row, 3 );
2643 root_key_str = get_root_key( package, root, &hkey_root );
2644 if (!root_key_str)
2645 return ERROR_SUCCESS;
2647 deformat_string( package, key_str, &deformated_key );
2648 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2649 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2650 strcpyW( ui_key_str, root_key_str );
2651 strcatW( ui_key_str, deformated_key );
2653 deformat_string( package, name, &deformated_name );
2655 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2656 msi_free( deformated_key );
2658 uirow = MSI_CreateRecord( 2 );
2659 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2660 MSI_RecordSetStringW( uirow, 2, deformated_name );
2662 ui_actiondata( package, szRemoveRegistryValues, uirow );
2663 msiobj_release( &uirow->hdr );
2665 msi_free( ui_key_str );
2666 msi_free( deformated_name );
2667 return ERROR_SUCCESS;
2670 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2672 UINT rc;
2673 MSIQUERY *view;
2674 static const WCHAR registry_query[] =
2675 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2676 '`','R','e','g','i','s','t','r','y','`',0 };
2677 static const WCHAR remove_registry_query[] =
2678 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2679 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2681 /* increment progress bar each time action data is sent */
2682 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2684 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2685 if (rc == ERROR_SUCCESS)
2687 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2688 msiobj_release( &view->hdr );
2689 if (rc != ERROR_SUCCESS)
2690 return rc;
2693 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2694 if (rc == ERROR_SUCCESS)
2696 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2697 msiobj_release( &view->hdr );
2698 if (rc != ERROR_SUCCESS)
2699 return rc;
2702 return ERROR_SUCCESS;
2705 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2707 package->script->CurrentlyScripting = TRUE;
2709 return ERROR_SUCCESS;
2713 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2715 MSICOMPONENT *comp;
2716 DWORD progress = 0;
2717 DWORD total = 0;
2718 static const WCHAR q1[]=
2719 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2720 '`','R','e','g','i','s','t','r','y','`',0};
2721 UINT rc;
2722 MSIQUERY * view;
2723 MSIFEATURE *feature;
2724 MSIFILE *file;
2726 TRACE("InstallValidate\n");
2728 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2729 if (rc == ERROR_SUCCESS)
2731 MSI_IterateRecords( view, &progress, NULL, package );
2732 msiobj_release( &view->hdr );
2733 total += progress * REG_PROGRESS_VALUE;
2736 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2737 total += COMPONENT_PROGRESS_VALUE;
2739 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2740 total += file->FileSize;
2742 ui_progress(package,0,total,0,0);
2744 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2746 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2747 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2748 feature->ActionRequest);
2751 return ERROR_SUCCESS;
2754 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2756 MSIPACKAGE* package = param;
2757 LPCWSTR cond = NULL;
2758 LPCWSTR message = NULL;
2759 UINT r;
2761 static const WCHAR title[]=
2762 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2764 cond = MSI_RecordGetString(row,1);
2766 r = MSI_EvaluateConditionW(package,cond);
2767 if (r == MSICONDITION_FALSE)
2769 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2771 LPWSTR deformated;
2772 message = MSI_RecordGetString(row,2);
2773 deformat_string(package,message,&deformated);
2774 MessageBoxW(NULL,deformated,title,MB_OK);
2775 msi_free(deformated);
2778 return ERROR_INSTALL_FAILURE;
2781 return ERROR_SUCCESS;
2784 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2786 UINT rc;
2787 MSIQUERY * view = NULL;
2788 static const WCHAR ExecSeqQuery[] =
2789 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2790 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2792 TRACE("Checking launch conditions\n");
2794 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2795 if (rc != ERROR_SUCCESS)
2796 return ERROR_SUCCESS;
2798 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2799 msiobj_release(&view->hdr);
2801 return rc;
2804 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2807 if (!cmp->KeyPath)
2808 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2810 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2812 MSIRECORD * row = 0;
2813 UINT root,len;
2814 LPWSTR deformated,buffer,deformated_name;
2815 LPCWSTR key,name;
2816 static const WCHAR ExecSeqQuery[] =
2817 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2818 '`','R','e','g','i','s','t','r','y','`',' ',
2819 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2820 ' ','=',' ' ,'\'','%','s','\'',0 };
2821 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2822 static const WCHAR fmt2[]=
2823 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2825 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2826 if (!row)
2827 return NULL;
2829 root = MSI_RecordGetInteger(row,2);
2830 key = MSI_RecordGetString(row, 3);
2831 name = MSI_RecordGetString(row, 4);
2832 deformat_string(package, key , &deformated);
2833 deformat_string(package, name, &deformated_name);
2835 len = strlenW(deformated) + 6;
2836 if (deformated_name)
2837 len+=strlenW(deformated_name);
2839 buffer = msi_alloc( len *sizeof(WCHAR));
2841 if (deformated_name)
2842 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2843 else
2844 sprintfW(buffer,fmt,root,deformated);
2846 msi_free(deformated);
2847 msi_free(deformated_name);
2848 msiobj_release(&row->hdr);
2850 return buffer;
2852 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2854 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2855 return NULL;
2857 else
2859 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2861 if (file)
2862 return strdupW( file->TargetPath );
2864 return NULL;
2867 static HKEY openSharedDLLsKey(void)
2869 HKEY hkey=0;
2870 static const WCHAR path[] =
2871 {'S','o','f','t','w','a','r','e','\\',
2872 'M','i','c','r','o','s','o','f','t','\\',
2873 'W','i','n','d','o','w','s','\\',
2874 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2875 'S','h','a','r','e','d','D','L','L','s',0};
2877 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2878 return hkey;
2881 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2883 HKEY hkey;
2884 DWORD count=0;
2885 DWORD type;
2886 DWORD sz = sizeof(count);
2887 DWORD rc;
2889 hkey = openSharedDLLsKey();
2890 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2891 if (rc != ERROR_SUCCESS)
2892 count = 0;
2893 RegCloseKey(hkey);
2894 return count;
2897 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2899 HKEY hkey;
2901 hkey = openSharedDLLsKey();
2902 if (count > 0)
2903 msi_reg_set_val_dword( hkey, path, count );
2904 else
2905 RegDeleteValueW(hkey,path);
2906 RegCloseKey(hkey);
2907 return count;
2911 * Return TRUE if the count should be written out and FALSE if not
2913 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2915 MSIFEATURE *feature;
2916 INT count = 0;
2917 BOOL write = FALSE;
2919 /* only refcount DLLs */
2920 if (comp->KeyPath == NULL ||
2921 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2922 comp->Attributes & msidbComponentAttributesODBCDataSource)
2923 write = FALSE;
2924 else
2926 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2927 write = (count > 0);
2929 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2930 write = TRUE;
2933 /* increment counts */
2934 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2936 ComponentList *cl;
2938 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2939 continue;
2941 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2943 if ( cl->component == comp )
2944 count++;
2948 /* decrement counts */
2949 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2951 ComponentList *cl;
2953 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2954 continue;
2956 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2958 if ( cl->component == comp )
2959 count--;
2963 /* ref count all the files in the component */
2964 if (write)
2966 MSIFILE *file;
2968 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2970 if (file->Component == comp)
2971 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2975 /* add a count for permanent */
2976 if (comp->Attributes & msidbComponentAttributesPermanent)
2977 count ++;
2979 comp->RefCount = count;
2981 if (write)
2982 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2985 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2987 WCHAR squished_pc[GUID_SIZE];
2988 WCHAR squished_cc[GUID_SIZE];
2989 UINT rc;
2990 MSICOMPONENT *comp;
2991 HKEY hkey;
2993 TRACE("\n");
2995 squash_guid(package->ProductCode,squished_pc);
2996 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2998 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3000 MSIRECORD * uirow;
3002 ui_progress(package,2,0,0,0);
3003 if (!comp->ComponentId)
3004 continue;
3006 squash_guid(comp->ComponentId,squished_cc);
3008 msi_free(comp->FullKeypath);
3009 comp->FullKeypath = resolve_keypath( package, comp );
3011 ACTION_RefCountComponent( package, comp );
3013 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
3014 debugstr_w(comp->Component),
3015 debugstr_w(squished_cc),
3016 debugstr_w(comp->FullKeypath),
3017 comp->RefCount);
3019 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3020 comp->ActionRequest == INSTALLSTATE_SOURCE)
3022 if (!comp->FullKeypath)
3023 continue;
3025 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3026 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
3027 &hkey, TRUE);
3028 else
3029 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
3030 &hkey, TRUE);
3032 if (rc != ERROR_SUCCESS)
3033 continue;
3035 if (comp->Attributes & msidbComponentAttributesPermanent)
3037 static const WCHAR szPermKey[] =
3038 { '0','0','0','0','0','0','0','0','0','0','0','0',
3039 '0','0','0','0','0','0','0','0','0','0','0','0',
3040 '0','0','0','0','0','0','0','0',0 };
3042 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3045 if (comp->Action == INSTALLSTATE_LOCAL)
3046 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3047 else
3049 MSIFILE *file;
3050 MSIRECORD *row;
3051 LPWSTR ptr, ptr2;
3052 WCHAR source[MAX_PATH];
3053 WCHAR base[MAX_PATH];
3054 LPWSTR sourcepath;
3056 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3057 static const WCHAR query[] = {
3058 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3059 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3060 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3061 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3062 '`','D','i','s','k','I','d','`',0};
3064 file = get_loaded_file(package, comp->KeyPath);
3065 if (!file)
3066 continue;
3068 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3069 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3070 ptr2 = strrchrW(source, '\\') + 1;
3071 msiobj_release(&row->hdr);
3073 lstrcpyW(base, package->PackagePath);
3074 ptr = strrchrW(base, '\\');
3075 *(ptr + 1) = '\0';
3077 sourcepath = resolve_file_source(package, file);
3078 ptr = sourcepath + lstrlenW(base);
3079 lstrcpyW(ptr2, ptr);
3080 msi_free(sourcepath);
3082 msi_reg_set_val_str(hkey, squished_pc, source);
3084 RegCloseKey(hkey);
3086 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3088 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3089 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3090 else
3091 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3093 comp->Action = comp->ActionRequest;
3095 /* UI stuff */
3096 uirow = MSI_CreateRecord(3);
3097 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3098 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3099 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3100 ui_actiondata(package,szProcessComponents,uirow);
3101 msiobj_release( &uirow->hdr );
3104 return ERROR_SUCCESS;
3107 typedef struct {
3108 CLSID clsid;
3109 LPWSTR source;
3111 LPWSTR path;
3112 ITypeLib *ptLib;
3113 } typelib_struct;
3115 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3116 LPWSTR lpszName, LONG_PTR lParam)
3118 TLIBATTR *attr;
3119 typelib_struct *tl_struct = (typelib_struct*) lParam;
3120 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3121 int sz;
3122 HRESULT res;
3124 if (!IS_INTRESOURCE(lpszName))
3126 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3127 return TRUE;
3130 sz = strlenW(tl_struct->source)+4;
3131 sz *= sizeof(WCHAR);
3133 if ((INT_PTR)lpszName == 1)
3134 tl_struct->path = strdupW(tl_struct->source);
3135 else
3137 tl_struct->path = msi_alloc(sz);
3138 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3141 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3142 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3143 if (FAILED(res))
3145 msi_free(tl_struct->path);
3146 tl_struct->path = NULL;
3148 return TRUE;
3151 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3152 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3154 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3155 return FALSE;
3158 msi_free(tl_struct->path);
3159 tl_struct->path = NULL;
3161 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3162 ITypeLib_Release(tl_struct->ptLib);
3164 return TRUE;
3167 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3169 MSIPACKAGE* package = param;
3170 LPCWSTR component;
3171 MSICOMPONENT *comp;
3172 MSIFILE *file;
3173 typelib_struct tl_struct;
3174 ITypeLib *tlib;
3175 HMODULE module;
3176 HRESULT hr;
3178 component = MSI_RecordGetString(row,3);
3179 comp = get_loaded_component(package,component);
3180 if (!comp)
3181 return ERROR_SUCCESS;
3183 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3185 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3186 comp->Action = comp->Installed;
3187 return ERROR_SUCCESS;
3189 comp->Action = INSTALLSTATE_LOCAL;
3191 file = get_loaded_file( package, comp->KeyPath );
3192 if (!file)
3193 return ERROR_SUCCESS;
3195 ui_actiondata( package, szRegisterTypeLibraries, row );
3197 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3198 if (module)
3200 LPCWSTR guid;
3201 guid = MSI_RecordGetString(row,1);
3202 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3203 tl_struct.source = strdupW( file->TargetPath );
3204 tl_struct.path = NULL;
3206 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3207 (LONG_PTR)&tl_struct);
3209 if (tl_struct.path)
3211 LPWSTR help = NULL;
3212 LPCWSTR helpid;
3213 HRESULT res;
3215 helpid = MSI_RecordGetString(row,6);
3217 if (helpid)
3218 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3219 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3220 msi_free(help);
3222 if (FAILED(res))
3223 ERR("Failed to register type library %s\n",
3224 debugstr_w(tl_struct.path));
3225 else
3226 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3228 ITypeLib_Release(tl_struct.ptLib);
3229 msi_free(tl_struct.path);
3231 else
3232 ERR("Failed to load type library %s\n",
3233 debugstr_w(tl_struct.source));
3235 FreeLibrary(module);
3236 msi_free(tl_struct.source);
3238 else
3240 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3241 if (FAILED(hr))
3243 ERR("Failed to load type library: %08x\n", hr);
3244 return ERROR_INSTALL_FAILURE;
3247 ITypeLib_Release(tlib);
3250 return ERROR_SUCCESS;
3253 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3256 * OK this is a bit confusing.. I am given a _Component key and I believe
3257 * that the file that is being registered as a type library is the "key file
3258 * of that component" which I interpret to mean "The file in the KeyPath of
3259 * that component".
3261 UINT rc;
3262 MSIQUERY * view;
3263 static const WCHAR Query[] =
3264 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3265 '`','T','y','p','e','L','i','b','`',0};
3267 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3268 if (rc != ERROR_SUCCESS)
3269 return ERROR_SUCCESS;
3271 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3272 msiobj_release(&view->hdr);
3273 return rc;
3276 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3278 MSIPACKAGE *package = param;
3279 LPCWSTR component, guid;
3280 MSICOMPONENT *comp;
3281 GUID libid;
3282 UINT version;
3283 LCID language;
3284 SYSKIND syskind;
3285 HRESULT hr;
3287 component = MSI_RecordGetString( row, 3 );
3288 comp = get_loaded_component( package, component );
3289 if (!comp)
3290 return ERROR_SUCCESS;
3292 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3294 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3295 comp->Action = comp->Installed;
3296 return ERROR_SUCCESS;
3298 comp->Action = INSTALLSTATE_ABSENT;
3300 ui_actiondata( package, szUnregisterTypeLibraries, row );
3302 guid = MSI_RecordGetString( row, 1 );
3303 CLSIDFromString( (LPCWSTR)guid, &libid );
3304 version = MSI_RecordGetInteger( row, 4 );
3305 language = MSI_RecordGetInteger( row, 2 );
3307 #ifdef _WIN64
3308 syskind = SYS_WIN64;
3309 #else
3310 syskind = SYS_WIN32;
3311 #endif
3313 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3314 if (FAILED(hr))
3316 WARN("Failed to unregister typelib: %08x\n", hr);
3319 return ERROR_SUCCESS;
3322 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3324 UINT rc;
3325 MSIQUERY *view;
3326 static const WCHAR query[] =
3327 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3328 '`','T','y','p','e','L','i','b','`',0};
3330 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3331 if (rc != ERROR_SUCCESS)
3332 return ERROR_SUCCESS;
3334 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3335 msiobj_release( &view->hdr );
3336 return rc;
3339 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3341 static const WCHAR szlnk[] = {'.','l','n','k',0};
3342 LPCWSTR directory, extension;
3343 LPWSTR link_folder, link_file, filename;
3345 directory = MSI_RecordGetString( row, 2 );
3346 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3348 /* may be needed because of a bug somewhere else */
3349 create_full_pathW( link_folder );
3351 filename = msi_dup_record_field( row, 3 );
3352 reduce_to_longfilename( filename );
3354 extension = strchrW( filename, '.' );
3355 if (!extension || strcmpiW( extension, szlnk ))
3357 int len = strlenW( filename );
3358 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3359 memcpy( filename + len, szlnk, sizeof(szlnk) );
3361 link_file = build_directory_name( 2, link_folder, filename );
3362 msi_free( link_folder );
3363 msi_free( filename );
3365 return link_file;
3368 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3370 MSIPACKAGE *package = param;
3371 LPWSTR link_file, deformated, path;
3372 LPCWSTR component, target;
3373 MSICOMPONENT *comp;
3374 IShellLinkW *sl = NULL;
3375 IPersistFile *pf = NULL;
3376 HRESULT res;
3378 component = MSI_RecordGetString(row, 4);
3379 comp = get_loaded_component(package, component);
3380 if (!comp)
3381 return ERROR_SUCCESS;
3383 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3385 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3386 comp->Action = comp->Installed;
3387 return ERROR_SUCCESS;
3389 comp->Action = INSTALLSTATE_LOCAL;
3391 ui_actiondata(package,szCreateShortcuts,row);
3393 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3394 &IID_IShellLinkW, (LPVOID *) &sl );
3396 if (FAILED( res ))
3398 ERR("CLSID_ShellLink not available\n");
3399 goto err;
3402 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3403 if (FAILED( res ))
3405 ERR("QueryInterface(IID_IPersistFile) failed\n");
3406 goto err;
3409 target = MSI_RecordGetString(row, 5);
3410 if (strchrW(target, '['))
3412 deformat_string(package, target, &deformated);
3413 IShellLinkW_SetPath(sl,deformated);
3414 msi_free(deformated);
3416 else
3418 FIXME("poorly handled shortcut format, advertised shortcut\n");
3419 IShellLinkW_SetPath(sl,comp->FullKeypath);
3422 if (!MSI_RecordIsNull(row,6))
3424 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3425 deformat_string(package, arguments, &deformated);
3426 IShellLinkW_SetArguments(sl,deformated);
3427 msi_free(deformated);
3430 if (!MSI_RecordIsNull(row,7))
3432 LPCWSTR description = MSI_RecordGetString(row, 7);
3433 IShellLinkW_SetDescription(sl, description);
3436 if (!MSI_RecordIsNull(row,8))
3437 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3439 if (!MSI_RecordIsNull(row,9))
3441 INT index;
3442 LPCWSTR icon = MSI_RecordGetString(row, 9);
3444 path = build_icon_path(package, icon);
3445 index = MSI_RecordGetInteger(row,10);
3447 /* no value means 0 */
3448 if (index == MSI_NULL_INTEGER)
3449 index = 0;
3451 IShellLinkW_SetIconLocation(sl, path, index);
3452 msi_free(path);
3455 if (!MSI_RecordIsNull(row,11))
3456 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3458 if (!MSI_RecordIsNull(row,12))
3460 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3461 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3462 if (path)
3463 IShellLinkW_SetWorkingDirectory(sl, path);
3464 msi_free(path);
3467 link_file = get_link_file(package, row);
3469 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3470 IPersistFile_Save(pf, link_file, FALSE);
3472 msi_free(link_file);
3474 err:
3475 if (pf)
3476 IPersistFile_Release( pf );
3477 if (sl)
3478 IShellLinkW_Release( sl );
3480 return ERROR_SUCCESS;
3483 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3485 UINT rc;
3486 HRESULT res;
3487 MSIQUERY * view;
3488 static const WCHAR Query[] =
3489 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3490 '`','S','h','o','r','t','c','u','t','`',0};
3492 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3493 if (rc != ERROR_SUCCESS)
3494 return ERROR_SUCCESS;
3496 res = CoInitialize( NULL );
3498 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3499 msiobj_release(&view->hdr);
3501 if (SUCCEEDED(res))
3502 CoUninitialize();
3504 return rc;
3507 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3509 MSIPACKAGE *package = param;
3510 LPWSTR link_file;
3511 LPCWSTR component;
3512 MSICOMPONENT *comp;
3514 component = MSI_RecordGetString( row, 4 );
3515 comp = get_loaded_component( package, component );
3516 if (!comp)
3517 return ERROR_SUCCESS;
3519 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3521 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3522 comp->Action = comp->Installed;
3523 return ERROR_SUCCESS;
3525 comp->Action = INSTALLSTATE_ABSENT;
3527 ui_actiondata( package, szRemoveShortcuts, row );
3529 link_file = get_link_file( package, row );
3531 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3532 if (!DeleteFileW( link_file ))
3534 WARN("Failed to remove shortcut file %u\n", GetLastError());
3536 msi_free( link_file );
3538 return ERROR_SUCCESS;
3541 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3543 UINT rc;
3544 MSIQUERY *view;
3545 static const WCHAR query[] =
3546 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3547 '`','S','h','o','r','t','c','u','t','`',0};
3549 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3550 if (rc != ERROR_SUCCESS)
3551 return ERROR_SUCCESS;
3553 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3554 msiobj_release( &view->hdr );
3556 return rc;
3559 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3561 MSIPACKAGE* package = param;
3562 HANDLE the_file;
3563 LPWSTR FilePath;
3564 LPCWSTR FileName;
3565 CHAR buffer[1024];
3566 DWORD sz;
3567 UINT rc;
3569 FileName = MSI_RecordGetString(row,1);
3570 if (!FileName)
3572 ERR("Unable to get FileName\n");
3573 return ERROR_SUCCESS;
3576 FilePath = build_icon_path(package,FileName);
3578 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3580 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3581 FILE_ATTRIBUTE_NORMAL, NULL);
3583 if (the_file == INVALID_HANDLE_VALUE)
3585 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3586 msi_free(FilePath);
3587 return ERROR_SUCCESS;
3592 DWORD write;
3593 sz = 1024;
3594 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3595 if (rc != ERROR_SUCCESS)
3597 ERR("Failed to get stream\n");
3598 CloseHandle(the_file);
3599 DeleteFileW(FilePath);
3600 break;
3602 WriteFile(the_file,buffer,sz,&write,NULL);
3603 } while (sz == 1024);
3605 msi_free(FilePath);
3606 CloseHandle(the_file);
3608 return ERROR_SUCCESS;
3611 static UINT msi_publish_icons(MSIPACKAGE *package)
3613 UINT r;
3614 MSIQUERY *view;
3616 static const WCHAR query[]= {
3617 'S','E','L','E','C','T',' ','*',' ',
3618 'F','R','O','M',' ','`','I','c','o','n','`',0};
3620 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3621 if (r == ERROR_SUCCESS)
3623 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3624 msiobj_release(&view->hdr);
3627 return ERROR_SUCCESS;
3630 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3632 UINT r;
3633 HKEY source;
3634 LPWSTR buffer;
3635 MSIMEDIADISK *disk;
3636 MSISOURCELISTINFO *info;
3638 r = RegCreateKeyW(hkey, szSourceList, &source);
3639 if (r != ERROR_SUCCESS)
3640 return r;
3642 RegCloseKey(source);
3644 buffer = strrchrW(package->PackagePath, '\\') + 1;
3645 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3646 package->Context, MSICODE_PRODUCT,
3647 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3648 if (r != ERROR_SUCCESS)
3649 return r;
3651 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3652 package->Context, MSICODE_PRODUCT,
3653 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3654 if (r != ERROR_SUCCESS)
3655 return r;
3657 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3658 package->Context, MSICODE_PRODUCT,
3659 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3660 if (r != ERROR_SUCCESS)
3661 return r;
3663 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3665 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3666 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3667 info->options, info->value);
3668 else
3669 MsiSourceListSetInfoW(package->ProductCode, NULL,
3670 info->context, info->options,
3671 info->property, info->value);
3674 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3676 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3677 disk->context, disk->options,
3678 disk->disk_id, disk->volume_label, disk->disk_prompt);
3681 return ERROR_SUCCESS;
3684 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3686 MSIHANDLE hdb, suminfo;
3687 WCHAR guids[MAX_PATH];
3688 WCHAR packcode[SQUISH_GUID_SIZE];
3689 LPWSTR buffer;
3690 LPWSTR ptr;
3691 DWORD langid;
3692 DWORD size;
3693 UINT r;
3695 static const WCHAR szProductLanguage[] =
3696 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3697 static const WCHAR szARPProductIcon[] =
3698 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3699 static const WCHAR szProductVersion[] =
3700 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3701 static const WCHAR szAssignment[] =
3702 {'A','s','s','i','g','n','m','e','n','t',0};
3703 static const WCHAR szAdvertiseFlags[] =
3704 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3705 static const WCHAR szClients[] =
3706 {'C','l','i','e','n','t','s',0};
3707 static const WCHAR szColon[] = {':',0};
3709 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3710 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3711 msi_free(buffer);
3713 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3714 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3716 /* FIXME */
3717 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3719 buffer = msi_dup_property(package->db, szARPProductIcon);
3720 if (buffer)
3722 LPWSTR path = build_icon_path(package,buffer);
3723 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3724 msi_free(path);
3725 msi_free(buffer);
3728 buffer = msi_dup_property(package->db, szProductVersion);
3729 if (buffer)
3731 DWORD verdword = msi_version_str_to_dword(buffer);
3732 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3733 msi_free(buffer);
3736 msi_reg_set_val_dword(hkey, szAssignment, 0);
3737 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3738 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3739 msi_reg_set_val_str(hkey, szClients, szColon);
3741 hdb = alloc_msihandle(&package->db->hdr);
3742 if (!hdb)
3743 return ERROR_NOT_ENOUGH_MEMORY;
3745 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3746 MsiCloseHandle(hdb);
3747 if (r != ERROR_SUCCESS)
3748 goto done;
3750 size = MAX_PATH;
3751 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3752 NULL, guids, &size);
3753 if (r != ERROR_SUCCESS)
3754 goto done;
3756 ptr = strchrW(guids, ';');
3757 if (ptr) *ptr = 0;
3758 squash_guid(guids, packcode);
3759 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3761 done:
3762 MsiCloseHandle(suminfo);
3763 return ERROR_SUCCESS;
3766 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3768 UINT r;
3769 HKEY hkey;
3770 LPWSTR upgrade;
3771 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3773 static const WCHAR szUpgradeCode[] =
3774 {'U','p','g','r','a','d','e','C','o','d','e',0};
3776 upgrade = msi_dup_property(package->db, szUpgradeCode);
3777 if (!upgrade)
3778 return ERROR_SUCCESS;
3780 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3782 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3783 if (r != ERROR_SUCCESS)
3784 goto done;
3786 else
3788 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3789 if (r != ERROR_SUCCESS)
3790 goto done;
3793 squash_guid(package->ProductCode, squashed_pc);
3794 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3796 RegCloseKey(hkey);
3798 done:
3799 msi_free(upgrade);
3800 return r;
3803 static BOOL msi_check_publish(MSIPACKAGE *package)
3805 MSIFEATURE *feature;
3807 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3809 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3810 return TRUE;
3813 return FALSE;
3816 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3818 MSIFEATURE *feature;
3820 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3822 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3823 return FALSE;
3826 return TRUE;
3829 static UINT msi_publish_patches( MSIPACKAGE *package, HKEY prodkey )
3831 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
3832 WCHAR patch_squashed[GUID_SIZE];
3833 HKEY patches_key = NULL, product_patches_key;
3834 LONG res;
3835 MSIPATCHINFO *patch;
3836 UINT r;
3837 WCHAR *p, *all_patches = NULL;
3838 DWORD len = 0;
3840 res = RegCreateKeyExW( prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
3841 if (res != ERROR_SUCCESS)
3842 return ERROR_FUNCTION_FAILED;
3844 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
3845 if (r != ERROR_SUCCESS)
3846 goto done;
3848 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3850 squash_guid( patch->patchcode, patch_squashed );
3851 len += strlenW( patch_squashed ) + 1;
3854 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
3855 if (!all_patches)
3856 goto done;
3858 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3860 HKEY patch_key;
3862 squash_guid( patch->patchcode, p );
3863 p += strlenW( p ) + 1;
3865 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
3866 (const BYTE *)patch->transforms,
3867 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
3868 if (res != ERROR_SUCCESS)
3869 goto done;
3871 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
3872 if (r != ERROR_SUCCESS)
3873 goto done;
3875 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
3876 (const BYTE *)patch->localfile,
3877 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
3878 RegCloseKey( patch_key );
3879 if (res != ERROR_SUCCESS)
3880 goto done;
3882 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
3883 if (res != ERROR_SUCCESS)
3884 goto done;
3886 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
3887 RegCloseKey( patch_key );
3888 if (res != ERROR_SUCCESS)
3889 goto done;
3892 all_patches[len] = 0;
3893 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
3894 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3895 if (res != ERROR_SUCCESS)
3896 goto done;
3898 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
3899 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3900 if (res != ERROR_SUCCESS)
3901 r = ERROR_FUNCTION_FAILED;
3903 done:
3904 RegCloseKey( product_patches_key );
3905 RegCloseKey( patches_key );
3906 msi_free( all_patches );
3907 return r;
3911 * 99% of the work done here is only done for
3912 * advertised installs. However this is where the
3913 * Icon table is processed and written out
3914 * so that is what I am going to do here.
3916 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3918 UINT rc;
3919 HKEY hukey = NULL, hudkey = NULL;
3920 MSIRECORD *uirow;
3922 /* FIXME: also need to publish if the product is in advertise mode */
3923 if (!msi_check_publish(package))
3924 return ERROR_SUCCESS;
3926 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3927 &hukey, TRUE);
3928 if (rc != ERROR_SUCCESS)
3929 goto end;
3931 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3932 NULL, &hudkey, TRUE);
3933 if (rc != ERROR_SUCCESS)
3934 goto end;
3936 rc = msi_publish_upgrade_code(package);
3937 if (rc != ERROR_SUCCESS)
3938 goto end;
3940 if (!list_empty(&package->patches))
3942 rc = msi_publish_patches(package, hukey);
3943 if (rc != ERROR_SUCCESS)
3944 goto end;
3947 rc = msi_publish_product_properties(package, hukey);
3948 if (rc != ERROR_SUCCESS)
3949 goto end;
3951 rc = msi_publish_sourcelist(package, hukey);
3952 if (rc != ERROR_SUCCESS)
3953 goto end;
3955 rc = msi_publish_icons(package);
3957 end:
3958 uirow = MSI_CreateRecord( 1 );
3959 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
3960 ui_actiondata( package, szPublishProduct, uirow );
3961 msiobj_release( &uirow->hdr );
3963 RegCloseKey(hukey);
3964 RegCloseKey(hudkey);
3966 return rc;
3969 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
3971 WCHAR *filename, *ptr, *folder, *ret;
3972 const WCHAR *dirprop;
3974 filename = msi_dup_record_field( row, 2 );
3975 if (filename && (ptr = strchrW( filename, '|' )))
3976 ptr++;
3977 else
3978 ptr = filename;
3980 dirprop = MSI_RecordGetString( row, 3 );
3981 if (dirprop)
3983 folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
3984 if (!folder)
3985 folder = msi_dup_property( package->db, dirprop );
3987 else
3988 folder = msi_dup_property( package->db, szWindowsFolder );
3990 if (!folder)
3992 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
3993 msi_free( filename );
3994 return NULL;
3997 ret = build_directory_name( 2, folder, ptr );
3999 msi_free( filename );
4000 msi_free( folder );
4001 return ret;
4004 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4006 MSIPACKAGE *package = param;
4007 LPCWSTR component, section, key, value, identifier;
4008 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4009 MSIRECORD * uirow;
4010 INT action;
4011 MSICOMPONENT *comp;
4013 component = MSI_RecordGetString(row, 8);
4014 comp = get_loaded_component(package,component);
4015 if (!comp)
4016 return ERROR_SUCCESS;
4018 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4020 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4021 comp->Action = comp->Installed;
4022 return ERROR_SUCCESS;
4024 comp->Action = INSTALLSTATE_LOCAL;
4026 identifier = MSI_RecordGetString(row,1);
4027 section = MSI_RecordGetString(row,4);
4028 key = MSI_RecordGetString(row,5);
4029 value = MSI_RecordGetString(row,6);
4030 action = MSI_RecordGetInteger(row,7);
4032 deformat_string(package,section,&deformated_section);
4033 deformat_string(package,key,&deformated_key);
4034 deformat_string(package,value,&deformated_value);
4036 fullname = get_ini_file_name(package, row);
4038 if (action == 0)
4040 TRACE("Adding value %s to section %s in %s\n",
4041 debugstr_w(deformated_key), debugstr_w(deformated_section),
4042 debugstr_w(fullname));
4043 WritePrivateProfileStringW(deformated_section, deformated_key,
4044 deformated_value, fullname);
4046 else if (action == 1)
4048 WCHAR returned[10];
4049 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4050 returned, 10, fullname);
4051 if (returned[0] == 0)
4053 TRACE("Adding value %s to section %s in %s\n",
4054 debugstr_w(deformated_key), debugstr_w(deformated_section),
4055 debugstr_w(fullname));
4057 WritePrivateProfileStringW(deformated_section, deformated_key,
4058 deformated_value, fullname);
4061 else if (action == 3)
4062 FIXME("Append to existing section not yet implemented\n");
4064 uirow = MSI_CreateRecord(4);
4065 MSI_RecordSetStringW(uirow,1,identifier);
4066 MSI_RecordSetStringW(uirow,2,deformated_section);
4067 MSI_RecordSetStringW(uirow,3,deformated_key);
4068 MSI_RecordSetStringW(uirow,4,deformated_value);
4069 ui_actiondata(package,szWriteIniValues,uirow);
4070 msiobj_release( &uirow->hdr );
4072 msi_free(fullname);
4073 msi_free(deformated_key);
4074 msi_free(deformated_value);
4075 msi_free(deformated_section);
4076 return ERROR_SUCCESS;
4079 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4081 UINT rc;
4082 MSIQUERY * view;
4083 static const WCHAR ExecSeqQuery[] =
4084 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4085 '`','I','n','i','F','i','l','e','`',0};
4087 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4088 if (rc != ERROR_SUCCESS)
4090 TRACE("no IniFile table\n");
4091 return ERROR_SUCCESS;
4094 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4095 msiobj_release(&view->hdr);
4096 return rc;
4099 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4101 MSIPACKAGE *package = param;
4102 LPCWSTR component, section, key, value, identifier;
4103 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4104 MSICOMPONENT *comp;
4105 MSIRECORD *uirow;
4106 INT action;
4108 component = MSI_RecordGetString( row, 8 );
4109 comp = get_loaded_component( package, component );
4110 if (!comp)
4111 return ERROR_SUCCESS;
4113 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4115 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4116 comp->Action = comp->Installed;
4117 return ERROR_SUCCESS;
4119 comp->Action = INSTALLSTATE_ABSENT;
4121 identifier = MSI_RecordGetString( row, 1 );
4122 section = MSI_RecordGetString( row, 4 );
4123 key = MSI_RecordGetString( row, 5 );
4124 value = MSI_RecordGetString( row, 6 );
4125 action = MSI_RecordGetInteger( row, 7 );
4127 deformat_string( package, section, &deformated_section );
4128 deformat_string( package, key, &deformated_key );
4129 deformat_string( package, value, &deformated_value );
4131 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4133 filename = get_ini_file_name( package, row );
4135 TRACE("Removing key %s from section %s in %s\n",
4136 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4138 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4140 WARN("Unable to remove key %u\n", GetLastError());
4142 msi_free( filename );
4144 else
4145 FIXME("Unsupported action %d\n", action);
4148 uirow = MSI_CreateRecord( 4 );
4149 MSI_RecordSetStringW( uirow, 1, identifier );
4150 MSI_RecordSetStringW( uirow, 2, deformated_section );
4151 MSI_RecordSetStringW( uirow, 3, deformated_key );
4152 MSI_RecordSetStringW( uirow, 4, deformated_value );
4153 ui_actiondata( package, szRemoveIniValues, uirow );
4154 msiobj_release( &uirow->hdr );
4156 msi_free( deformated_key );
4157 msi_free( deformated_value );
4158 msi_free( deformated_section );
4159 return ERROR_SUCCESS;
4162 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4164 MSIPACKAGE *package = param;
4165 LPCWSTR component, section, key, value, identifier;
4166 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4167 MSICOMPONENT *comp;
4168 MSIRECORD *uirow;
4169 INT action;
4171 component = MSI_RecordGetString( row, 8 );
4172 comp = get_loaded_component( package, component );
4173 if (!comp)
4174 return ERROR_SUCCESS;
4176 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4178 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4179 comp->Action = comp->Installed;
4180 return ERROR_SUCCESS;
4182 comp->Action = INSTALLSTATE_LOCAL;
4184 identifier = MSI_RecordGetString( row, 1 );
4185 section = MSI_RecordGetString( row, 4 );
4186 key = MSI_RecordGetString( row, 5 );
4187 value = MSI_RecordGetString( row, 6 );
4188 action = MSI_RecordGetInteger( row, 7 );
4190 deformat_string( package, section, &deformated_section );
4191 deformat_string( package, key, &deformated_key );
4192 deformat_string( package, value, &deformated_value );
4194 if (action == msidbIniFileActionRemoveLine)
4196 filename = get_ini_file_name( package, row );
4198 TRACE("Removing key %s from section %s in %s\n",
4199 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4201 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4203 WARN("Unable to remove key %u\n", GetLastError());
4205 msi_free( filename );
4207 else
4208 FIXME("Unsupported action %d\n", action);
4210 uirow = MSI_CreateRecord( 4 );
4211 MSI_RecordSetStringW( uirow, 1, identifier );
4212 MSI_RecordSetStringW( uirow, 2, deformated_section );
4213 MSI_RecordSetStringW( uirow, 3, deformated_key );
4214 MSI_RecordSetStringW( uirow, 4, deformated_value );
4215 ui_actiondata( package, szRemoveIniValues, uirow );
4216 msiobj_release( &uirow->hdr );
4218 msi_free( deformated_key );
4219 msi_free( deformated_value );
4220 msi_free( deformated_section );
4221 return ERROR_SUCCESS;
4224 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4226 UINT rc;
4227 MSIQUERY *view;
4228 static const WCHAR query[] =
4229 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4230 '`','I','n','i','F','i','l','e','`',0};
4231 static const WCHAR remove_query[] =
4232 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4233 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4235 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4236 if (rc == ERROR_SUCCESS)
4238 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4239 msiobj_release( &view->hdr );
4240 if (rc != ERROR_SUCCESS)
4241 return rc;
4244 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4245 if (rc == ERROR_SUCCESS)
4247 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4248 msiobj_release( &view->hdr );
4249 if (rc != ERROR_SUCCESS)
4250 return rc;
4253 return ERROR_SUCCESS;
4256 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4258 MSIPACKAGE *package = param;
4259 LPCWSTR filename;
4260 LPWSTR FullName;
4261 MSIFILE *file;
4262 DWORD len;
4263 static const WCHAR ExeStr[] =
4264 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
4265 static const WCHAR close[] = {'\"',0};
4266 STARTUPINFOW si;
4267 PROCESS_INFORMATION info;
4268 BOOL brc;
4269 MSIRECORD *uirow;
4271 memset(&si,0,sizeof(STARTUPINFOW));
4273 filename = MSI_RecordGetString(row,1);
4274 file = get_loaded_file( package, filename );
4276 if (!file)
4278 ERR("Unable to find file id %s\n",debugstr_w(filename));
4279 return ERROR_SUCCESS;
4282 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
4284 FullName = msi_alloc(len*sizeof(WCHAR));
4285 strcpyW(FullName,ExeStr);
4286 strcatW( FullName, file->TargetPath );
4287 strcatW(FullName,close);
4289 TRACE("Registering %s\n",debugstr_w(FullName));
4290 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
4291 &si, &info);
4293 if (brc)
4295 CloseHandle(info.hThread);
4296 msi_dialog_check_messages(info.hProcess);
4297 CloseHandle(info.hProcess);
4300 uirow = MSI_CreateRecord( 2 );
4301 MSI_RecordSetStringW( uirow, 1, filename );
4302 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4303 ui_actiondata( package, szSelfRegModules, uirow );
4304 msiobj_release( &uirow->hdr );
4306 msi_free( FullName );
4307 return ERROR_SUCCESS;
4310 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4312 UINT rc;
4313 MSIQUERY * view;
4314 static const WCHAR ExecSeqQuery[] =
4315 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4316 '`','S','e','l','f','R','e','g','`',0};
4318 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4319 if (rc != ERROR_SUCCESS)
4321 TRACE("no SelfReg table\n");
4322 return ERROR_SUCCESS;
4325 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4326 msiobj_release(&view->hdr);
4328 return ERROR_SUCCESS;
4331 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4333 static const WCHAR regsvr32[] =
4334 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
4335 static const WCHAR close[] = {'\"',0};
4336 MSIPACKAGE *package = param;
4337 LPCWSTR filename;
4338 LPWSTR cmdline;
4339 MSIFILE *file;
4340 DWORD len;
4341 STARTUPINFOW si;
4342 PROCESS_INFORMATION pi;
4343 BOOL ret;
4344 MSIRECORD *uirow;
4346 memset( &si, 0, sizeof(STARTUPINFOW) );
4348 filename = MSI_RecordGetString( row, 1 );
4349 file = get_loaded_file( package, filename );
4351 if (!file)
4353 ERR("Unable to find file id %s\n", debugstr_w(filename));
4354 return ERROR_SUCCESS;
4357 len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
4359 cmdline = msi_alloc( len * sizeof(WCHAR) );
4360 strcpyW( cmdline, regsvr32 );
4361 strcatW( cmdline, file->TargetPath );
4362 strcatW( cmdline, close );
4364 TRACE("Unregistering %s\n", debugstr_w(cmdline));
4366 ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
4367 if (ret)
4369 CloseHandle( pi.hThread );
4370 msi_dialog_check_messages( pi.hProcess );
4371 CloseHandle( pi.hProcess );
4374 uirow = MSI_CreateRecord( 2 );
4375 MSI_RecordSetStringW( uirow, 1, filename );
4376 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4377 ui_actiondata( package, szSelfUnregModules, uirow );
4378 msiobj_release( &uirow->hdr );
4380 msi_free( cmdline );
4381 return ERROR_SUCCESS;
4384 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4386 UINT rc;
4387 MSIQUERY *view;
4388 static const WCHAR query[] =
4389 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4390 '`','S','e','l','f','R','e','g','`',0};
4392 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4393 if (rc != ERROR_SUCCESS)
4395 TRACE("no SelfReg table\n");
4396 return ERROR_SUCCESS;
4399 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4400 msiobj_release( &view->hdr );
4402 return ERROR_SUCCESS;
4405 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4407 MSIFEATURE *feature;
4408 UINT rc;
4409 HKEY hkey = NULL, userdata = NULL;
4411 if (!msi_check_publish(package))
4412 return ERROR_SUCCESS;
4414 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4415 &hkey, TRUE);
4416 if (rc != ERROR_SUCCESS)
4417 goto end;
4419 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4420 &userdata, TRUE);
4421 if (rc != ERROR_SUCCESS)
4422 goto end;
4424 /* here the guids are base 85 encoded */
4425 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4427 ComponentList *cl;
4428 LPWSTR data = NULL;
4429 GUID clsid;
4430 INT size;
4431 BOOL absent = FALSE;
4432 MSIRECORD *uirow;
4434 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4435 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4436 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4438 size = 1;
4439 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4441 size += 21;
4443 if (feature->Feature_Parent)
4444 size += strlenW( feature->Feature_Parent )+2;
4446 data = msi_alloc(size * sizeof(WCHAR));
4448 data[0] = 0;
4449 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4451 MSICOMPONENT* component = cl->component;
4452 WCHAR buf[21];
4454 buf[0] = 0;
4455 if (component->ComponentId)
4457 TRACE("From %s\n",debugstr_w(component->ComponentId));
4458 CLSIDFromString(component->ComponentId, &clsid);
4459 encode_base85_guid(&clsid,buf);
4460 TRACE("to %s\n",debugstr_w(buf));
4461 strcatW(data,buf);
4465 if (feature->Feature_Parent)
4467 static const WCHAR sep[] = {'\2',0};
4468 strcatW(data,sep);
4469 strcatW(data,feature->Feature_Parent);
4472 msi_reg_set_val_str( userdata, feature->Feature, data );
4473 msi_free(data);
4475 size = 0;
4476 if (feature->Feature_Parent)
4477 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4478 if (!absent)
4480 size += sizeof(WCHAR);
4481 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4482 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4484 else
4486 size += 2*sizeof(WCHAR);
4487 data = msi_alloc(size);
4488 data[0] = 0x6;
4489 data[1] = 0;
4490 if (feature->Feature_Parent)
4491 strcpyW( &data[1], feature->Feature_Parent );
4492 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4493 (LPBYTE)data,size);
4494 msi_free(data);
4497 /* the UI chunk */
4498 uirow = MSI_CreateRecord( 1 );
4499 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4500 ui_actiondata( package, szPublishFeatures, uirow);
4501 msiobj_release( &uirow->hdr );
4502 /* FIXME: call ui_progress? */
4505 end:
4506 RegCloseKey(hkey);
4507 RegCloseKey(userdata);
4508 return rc;
4511 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4513 UINT r;
4514 HKEY hkey;
4515 MSIRECORD *uirow;
4517 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4519 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4520 &hkey, FALSE);
4521 if (r == ERROR_SUCCESS)
4523 RegDeleteValueW(hkey, feature->Feature);
4524 RegCloseKey(hkey);
4527 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4528 &hkey, FALSE);
4529 if (r == ERROR_SUCCESS)
4531 RegDeleteValueW(hkey, feature->Feature);
4532 RegCloseKey(hkey);
4535 uirow = MSI_CreateRecord( 1 );
4536 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4537 ui_actiondata( package, szUnpublishFeatures, uirow );
4538 msiobj_release( &uirow->hdr );
4540 return ERROR_SUCCESS;
4543 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4545 MSIFEATURE *feature;
4547 if (!msi_check_unpublish(package))
4548 return ERROR_SUCCESS;
4550 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4552 msi_unpublish_feature(package, feature);
4555 return ERROR_SUCCESS;
4558 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4560 SYSTEMTIME systime;
4561 DWORD size, langid;
4562 WCHAR date[9], *val, *buffer;
4563 const WCHAR *prop, *key;
4565 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4566 static const WCHAR szWindowsInstaller[] =
4567 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4568 static const WCHAR modpath_fmt[] =
4569 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4570 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4571 static const WCHAR szModifyPath[] =
4572 {'M','o','d','i','f','y','P','a','t','h',0};
4573 static const WCHAR szUninstallString[] =
4574 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4575 static const WCHAR szEstimatedSize[] =
4576 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4577 static const WCHAR szProductLanguage[] =
4578 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4579 static const WCHAR szProductVersion[] =
4580 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4581 static const WCHAR szDisplayVersion[] =
4582 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4583 static const WCHAR szInstallSource[] =
4584 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4585 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4586 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4587 static const WCHAR szAuthorizedCDFPrefix[] =
4588 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4589 static const WCHAR szARPCONTACT[] =
4590 {'A','R','P','C','O','N','T','A','C','T',0};
4591 static const WCHAR szContact[] =
4592 {'C','o','n','t','a','c','t',0};
4593 static const WCHAR szARPCOMMENTS[] =
4594 {'A','R','P','C','O','M','M','E','N','T','S',0};
4595 static const WCHAR szComments[] =
4596 {'C','o','m','m','e','n','t','s',0};
4597 static const WCHAR szProductName[] =
4598 {'P','r','o','d','u','c','t','N','a','m','e',0};
4599 static const WCHAR szDisplayName[] =
4600 {'D','i','s','p','l','a','y','N','a','m','e',0};
4601 static const WCHAR szARPHELPLINK[] =
4602 {'A','R','P','H','E','L','P','L','I','N','K',0};
4603 static const WCHAR szHelpLink[] =
4604 {'H','e','l','p','L','i','n','k',0};
4605 static const WCHAR szARPHELPTELEPHONE[] =
4606 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4607 static const WCHAR szHelpTelephone[] =
4608 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4609 static const WCHAR szARPINSTALLLOCATION[] =
4610 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4611 static const WCHAR szInstallLocation[] =
4612 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4613 static const WCHAR szManufacturer[] =
4614 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4615 static const WCHAR szPublisher[] =
4616 {'P','u','b','l','i','s','h','e','r',0};
4617 static const WCHAR szARPREADME[] =
4618 {'A','R','P','R','E','A','D','M','E',0};
4619 static const WCHAR szReadme[] =
4620 {'R','e','a','d','M','e',0};
4621 static const WCHAR szARPSIZE[] =
4622 {'A','R','P','S','I','Z','E',0};
4623 static const WCHAR szSize[] =
4624 {'S','i','z','e',0};
4625 static const WCHAR szARPURLINFOABOUT[] =
4626 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4627 static const WCHAR szURLInfoAbout[] =
4628 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4629 static const WCHAR szARPURLUPDATEINFO[] =
4630 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4631 static const WCHAR szURLUpdateInfo[] =
4632 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4634 static const WCHAR *propval[] = {
4635 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4636 szARPCONTACT, szContact,
4637 szARPCOMMENTS, szComments,
4638 szProductName, szDisplayName,
4639 szARPHELPLINK, szHelpLink,
4640 szARPHELPTELEPHONE, szHelpTelephone,
4641 szARPINSTALLLOCATION, szInstallLocation,
4642 cszSourceDir, szInstallSource,
4643 szManufacturer, szPublisher,
4644 szARPREADME, szReadme,
4645 szARPSIZE, szSize,
4646 szARPURLINFOABOUT, szURLInfoAbout,
4647 szARPURLUPDATEINFO, szURLUpdateInfo,
4648 NULL
4650 const WCHAR **p = propval;
4652 while (*p)
4654 prop = *p++;
4655 key = *p++;
4656 val = msi_dup_property(package->db, prop);
4657 msi_reg_set_val_str(hkey, key, val);
4658 msi_free(val);
4661 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4663 size = deformat_string(package, modpath_fmt, &buffer);
4664 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4665 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4666 msi_free(buffer);
4668 /* FIXME: Write real Estimated Size when we have it */
4669 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4671 GetLocalTime(&systime);
4672 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4673 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4675 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4676 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4678 buffer = msi_dup_property(package->db, szProductVersion);
4679 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4680 if (buffer)
4682 DWORD verdword = msi_version_str_to_dword(buffer);
4684 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4685 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4686 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4687 msi_free(buffer);
4690 return ERROR_SUCCESS;
4693 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4695 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4696 MSIRECORD *uirow;
4697 LPWSTR upgrade_code;
4698 HKEY hkey, props;
4699 HKEY upgrade;
4700 UINT rc;
4702 static const WCHAR szUpgradeCode[] = {
4703 'U','p','g','r','a','d','e','C','o','d','e',0};
4705 /* FIXME: also need to publish if the product is in advertise mode */
4706 if (!msi_check_publish(package))
4707 return ERROR_SUCCESS;
4709 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4710 if (rc != ERROR_SUCCESS)
4711 return rc;
4713 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4714 NULL, &props, TRUE);
4715 if (rc != ERROR_SUCCESS)
4716 goto done;
4718 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4719 msi_free( package->db->localfile );
4720 package->db->localfile = NULL;
4722 rc = msi_publish_install_properties(package, hkey);
4723 if (rc != ERROR_SUCCESS)
4724 goto done;
4726 rc = msi_publish_install_properties(package, props);
4727 if (rc != ERROR_SUCCESS)
4728 goto done;
4730 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4731 if (upgrade_code)
4733 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4734 squash_guid(package->ProductCode, squashed_pc);
4735 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4736 RegCloseKey(upgrade);
4737 msi_free(upgrade_code);
4740 done:
4741 uirow = MSI_CreateRecord( 1 );
4742 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4743 ui_actiondata( package, szRegisterProduct, uirow );
4744 msiobj_release( &uirow->hdr );
4746 RegCloseKey(hkey);
4747 return ERROR_SUCCESS;
4750 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4752 return execute_script(package,INSTALL_SCRIPT);
4755 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
4757 WCHAR *upgrade, **features;
4758 BOOL full_uninstall = TRUE;
4759 MSIFEATURE *feature;
4760 MSIPATCHINFO *patch;
4762 static const WCHAR szUpgradeCode[] =
4763 {'U','p','g','r','a','d','e','C','o','d','e',0};
4765 features = msi_split_string(remove, ',');
4766 if (!features)
4768 ERR("REMOVE feature list is empty!\n");
4769 return ERROR_FUNCTION_FAILED;
4772 if (!lstrcmpW(features[0], szAll))
4773 full_uninstall = TRUE;
4774 else
4776 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4778 if (feature->Action != INSTALLSTATE_ABSENT)
4779 full_uninstall = FALSE;
4782 msi_free(features);
4784 if (!full_uninstall)
4785 return ERROR_SUCCESS;
4787 MSIREG_DeleteProductKey(package->ProductCode);
4788 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4789 MSIREG_DeleteUninstallKey(package->ProductCode);
4791 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4793 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4794 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4796 else
4798 MSIREG_DeleteUserProductKey(package->ProductCode);
4799 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4802 upgrade = msi_dup_property(package->db, szUpgradeCode);
4803 if (upgrade)
4805 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4806 msi_free(upgrade);
4809 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4811 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4814 return ERROR_SUCCESS;
4817 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4819 UINT rc;
4820 WCHAR *remove;
4822 /* turn off scheduling */
4823 package->script->CurrentlyScripting= FALSE;
4825 /* first do the same as an InstallExecute */
4826 rc = ACTION_InstallExecute(package);
4827 if (rc != ERROR_SUCCESS)
4828 return rc;
4830 /* then handle Commit Actions */
4831 rc = execute_script(package,COMMIT_SCRIPT);
4832 if (rc != ERROR_SUCCESS)
4833 return rc;
4835 remove = msi_dup_property(package->db, szRemove);
4836 if (remove)
4837 rc = msi_unpublish_product(package, remove);
4839 msi_free(remove);
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;