wined3d: Cleanup IWineD3DBaseSwapChainImpl_GetBackBuffer() a bit.
[wine/multimedia.git] / dlls / msi / action.c
blobbb559c016af72eba6d073acabdca1e0c31ce2458
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 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);
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 msi_set_property(package->db, cszSourceDir, NULL);
902 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
903 msiobj_release(&view->hdr);
906 return rc;
909 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
911 MSIQUERY * view;
912 UINT rc;
913 static const WCHAR ExecSeqQuery [] =
914 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
915 '`','I','n','s','t','a','l','l',
916 'U','I','S','e','q','u','e','n','c','e','`',
917 ' ','W','H','E','R','E',' ',
918 '`','S','e','q','u','e','n','c','e','`',' ',
919 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
920 '`','S','e','q','u','e','n','c','e','`',0};
922 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
923 if (rc == ERROR_SUCCESS)
925 TRACE("Running the actions\n");
927 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
928 msiobj_release(&view->hdr);
931 return rc;
934 /********************************************************
935 * ACTION helper functions and functions that perform the actions
936 *******************************************************/
937 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
938 UINT* rc, UINT script, BOOL force )
940 BOOL ret=FALSE;
941 UINT arc;
943 arc = ACTION_CustomAction(package, action, script, force);
945 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
947 *rc = arc;
948 ret = TRUE;
950 return ret;
954 * Actual Action Handlers
957 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
959 MSIPACKAGE *package = param;
960 LPCWSTR dir, component;
961 LPWSTR full_path;
962 MSIRECORD *uirow;
963 MSIFOLDER *folder;
964 MSICOMPONENT *comp;
966 component = MSI_RecordGetString(row, 2);
967 comp = get_loaded_component(package, component);
968 if (!comp)
969 return ERROR_SUCCESS;
971 if (!comp->Enabled)
973 TRACE("component is disabled\n");
974 return ERROR_SUCCESS;
977 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
979 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
980 comp->Action = comp->Installed;
981 return ERROR_SUCCESS;
983 comp->Action = INSTALLSTATE_LOCAL;
985 dir = MSI_RecordGetString(row,1);
986 if (!dir)
988 ERR("Unable to get folder id\n");
989 return ERROR_SUCCESS;
992 uirow = MSI_CreateRecord(1);
993 MSI_RecordSetStringW(uirow, 1, dir);
994 ui_actiondata(package, szCreateFolders, uirow);
995 msiobj_release(&uirow->hdr);
997 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
998 if (!full_path)
1000 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1001 return ERROR_SUCCESS;
1004 TRACE("Folder is %s\n",debugstr_w(full_path));
1006 if (folder->State == 0)
1007 create_full_pathW(full_path);
1009 folder->State = 3;
1011 msi_free(full_path);
1012 return ERROR_SUCCESS;
1015 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1017 static const WCHAR ExecSeqQuery[] =
1018 {'S','E','L','E','C','T',' ',
1019 '`','D','i','r','e','c','t','o','r','y','_','`',
1020 ' ','F','R','O','M',' ',
1021 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1022 UINT rc;
1023 MSIQUERY *view;
1025 /* create all the empty folders specified in the CreateFolder table */
1026 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1027 if (rc != ERROR_SUCCESS)
1028 return ERROR_SUCCESS;
1030 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1031 msiobj_release(&view->hdr);
1033 return rc;
1036 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1038 MSIPACKAGE *package = param;
1039 LPCWSTR dir, component;
1040 LPWSTR full_path;
1041 MSIRECORD *uirow;
1042 MSIFOLDER *folder;
1043 MSICOMPONENT *comp;
1045 component = MSI_RecordGetString(row, 2);
1046 comp = get_loaded_component(package, component);
1047 if (!comp)
1048 return ERROR_SUCCESS;
1050 if (!comp->Enabled)
1052 TRACE("component is disabled\n");
1053 return ERROR_SUCCESS;
1056 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1058 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1059 comp->Action = comp->Installed;
1060 return ERROR_SUCCESS;
1062 comp->Action = INSTALLSTATE_ABSENT;
1064 dir = MSI_RecordGetString( row, 1 );
1065 if (!dir)
1067 ERR("Unable to get folder id\n");
1068 return ERROR_SUCCESS;
1071 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1072 if (!full_path)
1074 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1075 return ERROR_SUCCESS;
1078 TRACE("folder is %s\n", debugstr_w(full_path));
1080 uirow = MSI_CreateRecord( 1 );
1081 MSI_RecordSetStringW( uirow, 1, dir );
1082 ui_actiondata( package, szRemoveFolders, uirow );
1083 msiobj_release( &uirow->hdr );
1085 RemoveDirectoryW( full_path );
1086 folder->State = 0;
1088 msi_free( full_path );
1089 return ERROR_SUCCESS;
1092 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1094 static const WCHAR query[] =
1095 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1096 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1098 MSIQUERY *view;
1099 UINT rc;
1101 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1102 if (rc != ERROR_SUCCESS)
1103 return ERROR_SUCCESS;
1105 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1106 msiobj_release( &view->hdr );
1108 return rc;
1111 static UINT load_component( MSIRECORD *row, LPVOID param )
1113 MSIPACKAGE *package = param;
1114 MSICOMPONENT *comp;
1116 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1117 if (!comp)
1118 return ERROR_FUNCTION_FAILED;
1120 list_add_tail( &package->components, &comp->entry );
1122 /* fill in the data */
1123 comp->Component = msi_dup_record_field( row, 1 );
1125 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1127 comp->ComponentId = msi_dup_record_field( row, 2 );
1128 comp->Directory = msi_dup_record_field( row, 3 );
1129 comp->Attributes = MSI_RecordGetInteger(row,4);
1130 comp->Condition = msi_dup_record_field( row, 5 );
1131 comp->KeyPath = msi_dup_record_field( row, 6 );
1133 comp->Installed = INSTALLSTATE_UNKNOWN;
1134 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1136 return ERROR_SUCCESS;
1139 static UINT load_all_components( MSIPACKAGE *package )
1141 static const WCHAR query[] = {
1142 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1143 '`','C','o','m','p','o','n','e','n','t','`',0 };
1144 MSIQUERY *view;
1145 UINT r;
1147 if (!list_empty(&package->components))
1148 return ERROR_SUCCESS;
1150 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1151 if (r != ERROR_SUCCESS)
1152 return r;
1154 r = MSI_IterateRecords(view, NULL, load_component, package);
1155 msiobj_release(&view->hdr);
1156 return r;
1159 typedef struct {
1160 MSIPACKAGE *package;
1161 MSIFEATURE *feature;
1162 } _ilfs;
1164 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1166 ComponentList *cl;
1168 cl = msi_alloc( sizeof (*cl) );
1169 if ( !cl )
1170 return ERROR_NOT_ENOUGH_MEMORY;
1171 cl->component = comp;
1172 list_add_tail( &feature->Components, &cl->entry );
1174 return ERROR_SUCCESS;
1177 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1179 FeatureList *fl;
1181 fl = msi_alloc( sizeof(*fl) );
1182 if ( !fl )
1183 return ERROR_NOT_ENOUGH_MEMORY;
1184 fl->feature = child;
1185 list_add_tail( &parent->Children, &fl->entry );
1187 return ERROR_SUCCESS;
1190 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1192 _ilfs* ilfs = param;
1193 LPCWSTR component;
1194 MSICOMPONENT *comp;
1196 component = MSI_RecordGetString(row,1);
1198 /* check to see if the component is already loaded */
1199 comp = get_loaded_component( ilfs->package, component );
1200 if (!comp)
1202 ERR("unknown component %s\n", debugstr_w(component));
1203 return ERROR_FUNCTION_FAILED;
1206 add_feature_component( ilfs->feature, comp );
1207 comp->Enabled = TRUE;
1209 return ERROR_SUCCESS;
1212 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1214 MSIFEATURE *feature;
1216 if ( !name )
1217 return NULL;
1219 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1221 if ( !lstrcmpW( feature->Feature, name ) )
1222 return feature;
1225 return NULL;
1228 static UINT load_feature(MSIRECORD * row, LPVOID param)
1230 MSIPACKAGE* package = param;
1231 MSIFEATURE* feature;
1232 static const WCHAR Query1[] =
1233 {'S','E','L','E','C','T',' ',
1234 '`','C','o','m','p','o','n','e','n','t','_','`',
1235 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1236 'C','o','m','p','o','n','e','n','t','s','`',' ',
1237 'W','H','E','R','E',' ',
1238 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1239 MSIQUERY * view;
1240 UINT rc;
1241 _ilfs ilfs;
1243 /* fill in the data */
1245 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1246 if (!feature)
1247 return ERROR_NOT_ENOUGH_MEMORY;
1249 list_init( &feature->Children );
1250 list_init( &feature->Components );
1252 feature->Feature = msi_dup_record_field( row, 1 );
1254 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1256 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1257 feature->Title = msi_dup_record_field( row, 3 );
1258 feature->Description = msi_dup_record_field( row, 4 );
1260 if (!MSI_RecordIsNull(row,5))
1261 feature->Display = MSI_RecordGetInteger(row,5);
1263 feature->Level= MSI_RecordGetInteger(row,6);
1264 feature->Directory = msi_dup_record_field( row, 7 );
1265 feature->Attributes = MSI_RecordGetInteger(row,8);
1267 feature->Installed = INSTALLSTATE_UNKNOWN;
1268 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1270 list_add_tail( &package->features, &feature->entry );
1272 /* load feature components */
1274 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1275 if (rc != ERROR_SUCCESS)
1276 return ERROR_SUCCESS;
1278 ilfs.package = package;
1279 ilfs.feature = feature;
1281 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1282 msiobj_release(&view->hdr);
1284 return ERROR_SUCCESS;
1287 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1289 MSIPACKAGE* package = param;
1290 MSIFEATURE *parent, *child;
1292 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1293 if (!child)
1294 return ERROR_FUNCTION_FAILED;
1296 if (!child->Feature_Parent)
1297 return ERROR_SUCCESS;
1299 parent = find_feature_by_name( package, child->Feature_Parent );
1300 if (!parent)
1301 return ERROR_FUNCTION_FAILED;
1303 add_feature_child( parent, child );
1304 return ERROR_SUCCESS;
1307 static UINT load_all_features( MSIPACKAGE *package )
1309 static const WCHAR query[] = {
1310 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1311 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1312 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1313 MSIQUERY *view;
1314 UINT r;
1316 if (!list_empty(&package->features))
1317 return ERROR_SUCCESS;
1319 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1320 if (r != ERROR_SUCCESS)
1321 return r;
1323 r = MSI_IterateRecords( view, NULL, load_feature, package );
1324 if (r != ERROR_SUCCESS)
1325 return r;
1327 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1328 msiobj_release( &view->hdr );
1330 return r;
1333 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1335 if (!p)
1336 return p;
1337 p = strchrW(p, ch);
1338 if (!p)
1339 return p;
1340 *p = 0;
1341 return p+1;
1344 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1346 static const WCHAR query[] = {
1347 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1348 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1349 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1350 MSIQUERY *view = NULL;
1351 MSIRECORD *row = NULL;
1352 UINT r;
1354 TRACE("%s\n", debugstr_w(file->File));
1356 r = MSI_OpenQuery(package->db, &view, query, file->File);
1357 if (r != ERROR_SUCCESS)
1358 goto done;
1360 r = MSI_ViewExecute(view, NULL);
1361 if (r != ERROR_SUCCESS)
1362 goto done;
1364 r = MSI_ViewFetch(view, &row);
1365 if (r != ERROR_SUCCESS)
1366 goto done;
1368 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1369 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1370 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1371 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1372 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1374 done:
1375 if (view) msiobj_release(&view->hdr);
1376 if (row) msiobj_release(&row->hdr);
1377 return r;
1380 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1382 MSIRECORD *row;
1383 static const WCHAR query[] = {
1384 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1385 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1386 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1388 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1389 if (!row)
1391 WARN("query failed\n");
1392 return ERROR_FUNCTION_FAILED;
1395 file->disk_id = MSI_RecordGetInteger( row, 1 );
1396 msiobj_release( &row->hdr );
1397 return ERROR_SUCCESS;
1400 static UINT load_file(MSIRECORD *row, LPVOID param)
1402 MSIPACKAGE* package = param;
1403 LPCWSTR component;
1404 MSIFILE *file;
1406 /* fill in the data */
1408 file = msi_alloc_zero( sizeof (MSIFILE) );
1409 if (!file)
1410 return ERROR_NOT_ENOUGH_MEMORY;
1412 file->File = msi_dup_record_field( row, 1 );
1414 component = MSI_RecordGetString( row, 2 );
1415 file->Component = get_loaded_component( package, component );
1417 if (!file->Component)
1419 WARN("Component not found: %s\n", debugstr_w(component));
1420 msi_free(file->File);
1421 msi_free(file);
1422 return ERROR_SUCCESS;
1425 file->FileName = msi_dup_record_field( row, 3 );
1426 reduce_to_longfilename( file->FileName );
1428 file->ShortName = msi_dup_record_field( row, 3 );
1429 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1431 file->FileSize = MSI_RecordGetInteger( row, 4 );
1432 file->Version = msi_dup_record_field( row, 5 );
1433 file->Language = msi_dup_record_field( row, 6 );
1434 file->Attributes = MSI_RecordGetInteger( row, 7 );
1435 file->Sequence = MSI_RecordGetInteger( row, 8 );
1437 file->state = msifs_invalid;
1439 /* if the compressed bits are not set in the file attributes,
1440 * then read the information from the package word count property
1442 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1444 file->IsCompressed = FALSE;
1446 else if (file->Attributes &
1447 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1449 file->IsCompressed = TRUE;
1451 else if (file->Attributes & msidbFileAttributesNoncompressed)
1453 file->IsCompressed = FALSE;
1455 else
1457 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1460 load_file_hash(package, file);
1461 load_file_disk_id(package, file);
1463 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1465 list_add_tail( &package->files, &file->entry );
1467 return ERROR_SUCCESS;
1470 static UINT load_all_files(MSIPACKAGE *package)
1472 MSIQUERY * view;
1473 UINT rc;
1474 static const WCHAR Query[] =
1475 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1476 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1477 '`','S','e','q','u','e','n','c','e','`', 0};
1479 if (!list_empty(&package->files))
1480 return ERROR_SUCCESS;
1482 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1483 if (rc != ERROR_SUCCESS)
1484 return ERROR_SUCCESS;
1486 rc = MSI_IterateRecords(view, NULL, load_file, package);
1487 msiobj_release(&view->hdr);
1489 return ERROR_SUCCESS;
1492 static UINT load_folder( MSIRECORD *row, LPVOID param )
1494 MSIPACKAGE *package = param;
1495 static WCHAR szEmpty[] = { 0 };
1496 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1497 MSIFOLDER *folder;
1499 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1500 if (!folder)
1501 return ERROR_NOT_ENOUGH_MEMORY;
1503 folder->Directory = msi_dup_record_field( row, 1 );
1505 TRACE("%s\n", debugstr_w(folder->Directory));
1507 p = msi_dup_record_field(row, 3);
1509 /* split src and target dir */
1510 tgt_short = p;
1511 src_short = folder_split_path( p, ':' );
1513 /* split the long and short paths */
1514 tgt_long = folder_split_path( tgt_short, '|' );
1515 src_long = folder_split_path( src_short, '|' );
1517 /* check for no-op dirs */
1518 if (!lstrcmpW(szDot, tgt_short))
1519 tgt_short = szEmpty;
1520 if (!lstrcmpW(szDot, src_short))
1521 src_short = szEmpty;
1523 if (!tgt_long)
1524 tgt_long = tgt_short;
1526 if (!src_short) {
1527 src_short = tgt_short;
1528 src_long = tgt_long;
1531 if (!src_long)
1532 src_long = src_short;
1534 /* FIXME: use the target short path too */
1535 folder->TargetDefault = strdupW(tgt_long);
1536 folder->SourceShortPath = strdupW(src_short);
1537 folder->SourceLongPath = strdupW(src_long);
1538 msi_free(p);
1540 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1541 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1542 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1544 folder->Parent = msi_dup_record_field( row, 2 );
1546 folder->Property = msi_dup_property( package->db, folder->Directory );
1548 list_add_tail( &package->folders, &folder->entry );
1550 TRACE("returning %p\n", folder);
1552 return ERROR_SUCCESS;
1555 static UINT load_all_folders( MSIPACKAGE *package )
1557 static const WCHAR query[] = {
1558 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1559 '`','D','i','r','e','c','t','o','r','y','`',0 };
1560 MSIQUERY *view;
1561 UINT r;
1563 if (!list_empty(&package->folders))
1564 return ERROR_SUCCESS;
1566 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1567 if (r != ERROR_SUCCESS)
1568 return r;
1570 r = MSI_IterateRecords(view, NULL, load_folder, package);
1571 msiobj_release(&view->hdr);
1572 return r;
1576 * I am not doing any of the costing functionality yet.
1577 * Mostly looking at doing the Component and Feature loading
1579 * The native MSI does A LOT of modification to tables here. Mostly adding
1580 * a lot of temporary columns to the Feature and Component tables.
1582 * note: Native msi also tracks the short filename. But I am only going to
1583 * track the long ones. Also looking at this directory table
1584 * it appears that the directory table does not get the parents
1585 * resolved base on property only based on their entries in the
1586 * directory table.
1588 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1590 static const WCHAR szCosting[] =
1591 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1593 msi_set_property( package->db, szCosting, szZero );
1594 msi_set_property( package->db, cszRootDrive, c_colon );
1596 load_all_folders( package );
1597 load_all_components( package );
1598 load_all_features( package );
1599 load_all_files( package );
1601 return ERROR_SUCCESS;
1604 static UINT execute_script(MSIPACKAGE *package, UINT script )
1606 UINT i;
1607 UINT rc = ERROR_SUCCESS;
1609 TRACE("Executing Script %i\n",script);
1611 if (!package->script)
1613 ERR("no script!\n");
1614 return ERROR_FUNCTION_FAILED;
1617 for (i = 0; i < package->script->ActionCount[script]; i++)
1619 LPWSTR action;
1620 action = package->script->Actions[script][i];
1621 ui_actionstart(package, action);
1622 TRACE("Executing Action (%s)\n",debugstr_w(action));
1623 rc = ACTION_PerformAction(package, action, script);
1624 if (rc != ERROR_SUCCESS)
1625 break;
1627 msi_free_action_script(package, script);
1628 return rc;
1631 static UINT ACTION_FileCost(MSIPACKAGE *package)
1633 return ERROR_SUCCESS;
1636 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1638 MSICOMPONENT *comp;
1639 INSTALLSTATE state;
1640 UINT r;
1642 state = MsiQueryProductStateW(package->ProductCode);
1644 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1646 if (!comp->ComponentId)
1647 continue;
1649 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1650 comp->Installed = INSTALLSTATE_ABSENT;
1651 else
1653 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1654 package->Context, comp->ComponentId,
1655 &comp->Installed);
1656 if (r != ERROR_SUCCESS)
1657 comp->Installed = INSTALLSTATE_ABSENT;
1662 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1664 MSIFEATURE *feature;
1665 INSTALLSTATE state;
1667 state = MsiQueryProductStateW(package->ProductCode);
1669 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1671 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1672 feature->Installed = INSTALLSTATE_ABSENT;
1673 else
1675 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1676 feature->Feature);
1681 static BOOL process_state_property(MSIPACKAGE* package, int level,
1682 LPCWSTR property, INSTALLSTATE state)
1684 LPWSTR override;
1685 MSIFEATURE *feature;
1687 override = msi_dup_property( package->db, property );
1688 if (!override)
1689 return FALSE;
1691 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1693 if (lstrcmpW(property, szRemove) &&
1694 (feature->Level <= 0 || feature->Level > level))
1695 continue;
1697 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1699 if (strcmpiW(override, szAll)==0)
1700 msi_feature_set_state(package, feature, state);
1701 else
1703 LPWSTR ptr = override;
1704 LPWSTR ptr2 = strchrW(override,',');
1706 while (ptr)
1708 int len = ptr2 - ptr;
1710 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1711 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1713 msi_feature_set_state(package, feature, state);
1714 break;
1716 if (ptr2)
1718 ptr=ptr2+1;
1719 ptr2 = strchrW(ptr,',');
1721 else
1722 break;
1726 msi_free(override);
1728 return TRUE;
1731 static BOOL process_overrides( MSIPACKAGE *package, int level )
1733 static const WCHAR szAddLocal[] =
1734 {'A','D','D','L','O','C','A','L',0};
1735 static const WCHAR szAddSource[] =
1736 {'A','D','D','S','O','U','R','C','E',0};
1737 static const WCHAR szAdvertise[] =
1738 {'A','D','V','E','R','T','I','S','E',0};
1739 BOOL ret = FALSE;
1741 /* all these activation/deactivation things happen in order and things
1742 * later on the list override things earlier on the list.
1744 * 0 INSTALLLEVEL processing
1745 * 1 ADDLOCAL
1746 * 2 REMOVE
1747 * 3 ADDSOURCE
1748 * 4 ADDDEFAULT
1749 * 5 REINSTALL
1750 * 6 ADVERTISE
1751 * 7 COMPADDLOCAL
1752 * 8 COMPADDSOURCE
1753 * 9 FILEADDLOCAL
1754 * 10 FILEADDSOURCE
1755 * 11 FILEADDDEFAULT
1757 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1758 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1759 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1760 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1761 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1763 if (ret)
1764 msi_set_property( package->db, szPreselected, szOne );
1766 return ret;
1769 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1771 int level;
1772 static const WCHAR szlevel[] =
1773 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1774 MSICOMPONENT* component;
1775 MSIFEATURE *feature;
1777 TRACE("Checking Install Level\n");
1779 level = msi_get_property_int(package->db, szlevel, 1);
1781 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1783 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1785 BOOL feature_state = ((feature->Level > 0) &&
1786 (feature->Level <= level));
1788 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1790 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1791 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1792 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1793 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1794 else
1795 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1799 /* disable child features of unselected parent features */
1800 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1802 FeatureList *fl;
1804 if (feature->Level > 0 && feature->Level <= level)
1805 continue;
1807 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1808 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1811 else
1813 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1815 BOOL selected = feature->Level > 0 && feature->Level <= level;
1817 if (selected && feature->Action == INSTALLSTATE_UNKNOWN)
1819 msi_feature_set_state(package, feature, feature->Installed);
1825 * now we want to enable or disable components based on feature
1827 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1829 ComponentList *cl;
1831 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1832 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1833 feature->ActionRequest, feature->Action);
1835 if (!feature->Level)
1836 continue;
1838 /* features with components that have compressed files are made local */
1839 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1841 if (cl->component->ForceLocalState &&
1842 feature->Action == INSTALLSTATE_SOURCE)
1844 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1845 break;
1849 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1851 component = cl->component;
1853 switch (feature->Action)
1855 case INSTALLSTATE_ABSENT:
1856 component->anyAbsent = 1;
1857 break;
1858 case INSTALLSTATE_ADVERTISED:
1859 component->hasAdvertiseFeature = 1;
1860 break;
1861 case INSTALLSTATE_SOURCE:
1862 component->hasSourceFeature = 1;
1863 break;
1864 case INSTALLSTATE_LOCAL:
1865 component->hasLocalFeature = 1;
1866 break;
1867 case INSTALLSTATE_DEFAULT:
1868 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1869 component->hasAdvertiseFeature = 1;
1870 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1871 component->hasSourceFeature = 1;
1872 else
1873 component->hasLocalFeature = 1;
1874 break;
1875 default:
1876 break;
1881 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1883 /* check if it's local or source */
1884 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1885 (component->hasLocalFeature || component->hasSourceFeature))
1887 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1888 !component->ForceLocalState)
1889 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1890 else
1891 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1892 continue;
1895 /* if any feature is local, the component must be local too */
1896 if (component->hasLocalFeature)
1898 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1899 continue;
1902 if (component->hasSourceFeature)
1904 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1905 continue;
1908 if (component->hasAdvertiseFeature)
1910 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1911 continue;
1914 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1915 if (component->anyAbsent)
1916 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1919 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1921 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1923 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1924 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1927 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1928 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1931 return ERROR_SUCCESS;
1934 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1936 MSIPACKAGE *package = param;
1937 LPCWSTR name;
1938 LPWSTR path;
1939 MSIFOLDER *f;
1941 name = MSI_RecordGetString(row,1);
1943 f = get_loaded_folder(package, name);
1944 if (!f) return ERROR_SUCCESS;
1946 /* reset the ResolvedTarget */
1947 msi_free(f->ResolvedTarget);
1948 f->ResolvedTarget = NULL;
1950 /* This helper function now does ALL the work */
1951 TRACE("Dir %s ...\n",debugstr_w(name));
1952 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1953 TRACE("resolves to %s\n",debugstr_w(path));
1954 msi_free(path);
1956 return ERROR_SUCCESS;
1959 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1961 MSIPACKAGE *package = param;
1962 LPCWSTR name;
1963 MSIFEATURE *feature;
1965 name = MSI_RecordGetString( row, 1 );
1967 feature = get_loaded_feature( package, name );
1968 if (!feature)
1969 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1970 else
1972 LPCWSTR Condition;
1973 Condition = MSI_RecordGetString(row,3);
1975 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1977 int level = MSI_RecordGetInteger(row,2);
1978 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1979 feature->Level = level;
1982 return ERROR_SUCCESS;
1985 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1987 static const WCHAR name[] = {'\\',0};
1988 VS_FIXEDFILEINFO *ret;
1989 LPVOID version;
1990 DWORD versize, handle;
1991 UINT sz;
1993 TRACE("%s\n", debugstr_w(filename));
1995 versize = GetFileVersionInfoSizeW( filename, &handle );
1996 if (!versize)
1997 return NULL;
1999 version = msi_alloc( versize );
2000 if (!version)
2001 return NULL;
2003 GetFileVersionInfoW( filename, 0, versize, version );
2005 if (!VerQueryValueW( version, name, (LPVOID *)&ret, &sz ))
2007 msi_free( version );
2008 return NULL;
2011 msi_free( version );
2012 return ret;
2015 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2017 DWORD ms, ls;
2019 msi_parse_version_string( version, &ms, &ls );
2021 if (fi->dwFileVersionMS > ms) return 1;
2022 else if (fi->dwFileVersionMS < ms) return -1;
2023 else if (fi->dwFileVersionLS > ls) return 1;
2024 else if (fi->dwFileVersionLS < ls) return -1;
2025 return 0;
2028 static DWORD get_disk_file_size( LPCWSTR filename )
2030 HANDLE file;
2031 DWORD size;
2033 TRACE("%s\n", debugstr_w(filename));
2035 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2036 if (file == INVALID_HANDLE_VALUE)
2037 return INVALID_FILE_SIZE;
2039 size = GetFileSize( file, NULL );
2040 CloseHandle( file );
2041 return size;
2044 static BOOL hash_matches( MSIFILE *file )
2046 UINT r;
2047 MSIFILEHASHINFO hash;
2049 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2050 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2051 if (r != ERROR_SUCCESS)
2052 return FALSE;
2054 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2057 static UINT set_file_install_states( MSIPACKAGE *package )
2059 VS_FIXEDFILEINFO *file_version;
2060 MSIFILE *file;
2062 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2064 MSICOMPONENT* comp = file->Component;
2065 DWORD file_size;
2066 LPWSTR p;
2068 if (!comp)
2069 continue;
2071 if (file->IsCompressed)
2072 comp->ForceLocalState = TRUE;
2074 /* calculate target */
2075 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2076 msi_free(file->TargetPath);
2078 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2080 file->TargetPath = build_directory_name(2, p, file->FileName);
2081 msi_free(p);
2083 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2085 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2087 file->state = msifs_missing;
2088 comp->Cost += file->FileSize;
2089 continue;
2091 if (file->Version && (file_version = msi_get_disk_file_version( file->TargetPath )))
2093 TRACE("new %s old %u.%u.%u.%u\n", debugstr_w(file->Version),
2094 HIWORD(file_version->dwFileVersionMS),
2095 LOWORD(file_version->dwFileVersionMS),
2096 HIWORD(file_version->dwFileVersionLS),
2097 LOWORD(file_version->dwFileVersionLS));
2099 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2101 file->state = msifs_overwrite;
2102 comp->Cost += file->FileSize;
2104 else
2106 TRACE("Destination file version equal or greater, not overwriting\n");
2107 file->state = msifs_present;
2109 msi_free( file_version );
2110 continue;
2112 if ((file_size = get_disk_file_size( file->TargetPath )) != file->FileSize)
2114 file->state = msifs_overwrite;
2115 comp->Cost += file->FileSize - file_size;
2116 continue;
2118 if (file->hash.dwFileHashInfoSize && hash_matches( file ))
2120 TRACE("File hashes match, not overwriting\n");
2121 file->state = msifs_present;
2122 continue;
2124 file->state = msifs_overwrite;
2125 comp->Cost += file->FileSize - file_size;
2128 return ERROR_SUCCESS;
2132 * A lot is done in this function aside from just the costing.
2133 * The costing needs to be implemented at some point but for now I am going
2134 * to focus on the directory building
2137 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2139 static const WCHAR ExecSeqQuery[] =
2140 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2141 '`','D','i','r','e','c','t','o','r','y','`',0};
2142 static const WCHAR ConditionQuery[] =
2143 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2144 '`','C','o','n','d','i','t','i','o','n','`',0};
2145 static const WCHAR szCosting[] =
2146 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2147 static const WCHAR szlevel[] =
2148 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2149 static const WCHAR szOutOfDiskSpace[] =
2150 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2151 MSICOMPONENT *comp;
2152 UINT rc = ERROR_SUCCESS;
2153 MSIQUERY * view;
2154 LPWSTR level;
2156 TRACE("Building Directory properties\n");
2158 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2159 if (rc == ERROR_SUCCESS)
2161 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2162 package);
2163 msiobj_release(&view->hdr);
2166 /* read components states from the registry */
2167 ACTION_GetComponentInstallStates(package);
2168 ACTION_GetFeatureInstallStates(package);
2170 TRACE("Calculating file install states\n");
2171 set_file_install_states( package );
2173 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2175 TRACE("Evaluating feature conditions\n");
2177 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2178 if (rc == ERROR_SUCCESS)
2180 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2181 msiobj_release( &view->hdr );
2184 TRACE("Evaluating component conditions\n");
2186 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2188 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2190 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2191 comp->Enabled = FALSE;
2193 else
2194 comp->Enabled = TRUE;
2197 msi_set_property( package->db, szCosting, szOne );
2198 /* set default run level if not set */
2199 level = msi_dup_property( package->db, szlevel );
2200 if (!level)
2201 msi_set_property( package->db, szlevel, szOne );
2202 msi_free(level);
2204 /* FIXME: check volume disk space */
2205 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2207 return MSI_SetFeatureStates(package);
2210 /* OK this value is "interpreted" and then formatted based on the
2211 first few characters */
2212 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2213 DWORD *size)
2215 LPSTR data = NULL;
2217 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2219 if (value[1]=='x')
2221 LPWSTR ptr;
2222 CHAR byte[5];
2223 LPWSTR deformated = NULL;
2224 int count;
2226 deformat_string(package, &value[2], &deformated);
2228 /* binary value type */
2229 ptr = deformated;
2230 *type = REG_BINARY;
2231 if (strlenW(ptr)%2)
2232 *size = (strlenW(ptr)/2)+1;
2233 else
2234 *size = strlenW(ptr)/2;
2236 data = msi_alloc(*size);
2238 byte[0] = '0';
2239 byte[1] = 'x';
2240 byte[4] = 0;
2241 count = 0;
2242 /* if uneven pad with a zero in front */
2243 if (strlenW(ptr)%2)
2245 byte[2]= '0';
2246 byte[3]= *ptr;
2247 ptr++;
2248 data[count] = (BYTE)strtol(byte,NULL,0);
2249 count ++;
2250 TRACE("Uneven byte count\n");
2252 while (*ptr)
2254 byte[2]= *ptr;
2255 ptr++;
2256 byte[3]= *ptr;
2257 ptr++;
2258 data[count] = (BYTE)strtol(byte,NULL,0);
2259 count ++;
2261 msi_free(deformated);
2263 TRACE("Data %i bytes(%i)\n",*size,count);
2265 else
2267 LPWSTR deformated;
2268 LPWSTR p;
2269 DWORD d = 0;
2270 deformat_string(package, &value[1], &deformated);
2272 *type=REG_DWORD;
2273 *size = sizeof(DWORD);
2274 data = msi_alloc(*size);
2275 p = deformated;
2276 if (*p == '-')
2277 p++;
2278 while (*p)
2280 if ( (*p < '0') || (*p > '9') )
2281 break;
2282 d *= 10;
2283 d += (*p - '0');
2284 p++;
2286 if (deformated[0] == '-')
2287 d = -d;
2288 *(LPDWORD)data = d;
2289 TRACE("DWORD %i\n",*(LPDWORD)data);
2291 msi_free(deformated);
2294 else
2296 static const WCHAR szMulti[] = {'[','~',']',0};
2297 LPCWSTR ptr;
2298 *type=REG_SZ;
2300 if (value[0]=='#')
2302 if (value[1]=='%')
2304 ptr = &value[2];
2305 *type=REG_EXPAND_SZ;
2307 else
2308 ptr = &value[1];
2310 else
2311 ptr=value;
2313 if (strstrW(value,szMulti))
2314 *type = REG_MULTI_SZ;
2316 /* remove initial delimiter */
2317 if (!strncmpW(value, szMulti, 3))
2318 ptr = value + 3;
2320 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2322 /* add double NULL terminator */
2323 if (*type == REG_MULTI_SZ)
2325 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2326 data = msi_realloc_zero(data, *size);
2329 return data;
2332 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2334 const WCHAR *ret;
2336 switch (root)
2338 case -1:
2339 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2341 *root_key = HKEY_LOCAL_MACHINE;
2342 ret = szHLM;
2344 else
2346 *root_key = HKEY_CURRENT_USER;
2347 ret = szHCU;
2349 break;
2350 case 0:
2351 *root_key = HKEY_CLASSES_ROOT;
2352 ret = szHCR;
2353 break;
2354 case 1:
2355 *root_key = HKEY_CURRENT_USER;
2356 ret = szHCU;
2357 break;
2358 case 2:
2359 *root_key = HKEY_LOCAL_MACHINE;
2360 ret = szHLM;
2361 break;
2362 case 3:
2363 *root_key = HKEY_USERS;
2364 ret = szHU;
2365 break;
2366 default:
2367 ERR("Unknown root %i\n", root);
2368 return NULL;
2371 return ret;
2374 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2376 MSIPACKAGE *package = param;
2377 LPSTR value_data = NULL;
2378 HKEY root_key, hkey;
2379 DWORD type,size;
2380 LPWSTR deformated;
2381 LPCWSTR szRoot, component, name, key, value;
2382 MSICOMPONENT *comp;
2383 MSIRECORD * uirow;
2384 LPWSTR uikey;
2385 INT root;
2386 BOOL check_first = FALSE;
2387 UINT rc;
2389 ui_progress(package,2,0,0,0);
2391 component = MSI_RecordGetString(row, 6);
2392 comp = get_loaded_component(package,component);
2393 if (!comp)
2394 return ERROR_SUCCESS;
2396 if (!comp->Enabled)
2398 TRACE("component is disabled\n");
2399 return ERROR_SUCCESS;
2402 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2404 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2405 comp->Action = comp->Installed;
2406 return ERROR_SUCCESS;
2408 comp->Action = INSTALLSTATE_LOCAL;
2410 name = MSI_RecordGetString(row, 4);
2411 if( MSI_RecordIsNull(row,5) && name )
2413 /* null values can have special meanings */
2414 if (name[0]=='-' && name[1] == 0)
2415 return ERROR_SUCCESS;
2416 else if ((name[0]=='+' && name[1] == 0) ||
2417 (name[0] == '*' && name[1] == 0))
2418 name = NULL;
2419 check_first = TRUE;
2422 root = MSI_RecordGetInteger(row,2);
2423 key = MSI_RecordGetString(row, 3);
2425 szRoot = get_root_key( package, root, &root_key );
2426 if (!szRoot)
2427 return ERROR_SUCCESS;
2429 deformat_string(package, key , &deformated);
2430 size = strlenW(deformated) + strlenW(szRoot) + 1;
2431 uikey = msi_alloc(size*sizeof(WCHAR));
2432 strcpyW(uikey,szRoot);
2433 strcatW(uikey,deformated);
2435 if (RegCreateKeyW( root_key, deformated, &hkey))
2437 ERR("Could not create key %s\n",debugstr_w(deformated));
2438 msi_free(deformated);
2439 msi_free(uikey);
2440 return ERROR_SUCCESS;
2442 msi_free(deformated);
2444 value = MSI_RecordGetString(row,5);
2445 if (value)
2446 value_data = parse_value(package, value, &type, &size);
2447 else
2449 value_data = (LPSTR)strdupW(szEmpty);
2450 size = sizeof(szEmpty);
2451 type = REG_SZ;
2454 deformat_string(package, name, &deformated);
2456 if (!check_first)
2458 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2459 debugstr_w(uikey));
2460 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2462 else
2464 DWORD sz = 0;
2465 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2466 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2468 TRACE("value %s of %s checked already exists\n",
2469 debugstr_w(deformated), debugstr_w(uikey));
2471 else
2473 TRACE("Checked and setting value %s of %s\n",
2474 debugstr_w(deformated), debugstr_w(uikey));
2475 if (deformated || size)
2476 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2479 RegCloseKey(hkey);
2481 uirow = MSI_CreateRecord(3);
2482 MSI_RecordSetStringW(uirow,2,deformated);
2483 MSI_RecordSetStringW(uirow,1,uikey);
2484 if (type == REG_SZ || type == REG_EXPAND_SZ)
2485 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2486 ui_actiondata(package,szWriteRegistryValues,uirow);
2487 msiobj_release( &uirow->hdr );
2489 msi_free(value_data);
2490 msi_free(deformated);
2491 msi_free(uikey);
2493 return ERROR_SUCCESS;
2496 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2498 UINT rc;
2499 MSIQUERY * view;
2500 static const WCHAR ExecSeqQuery[] =
2501 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2502 '`','R','e','g','i','s','t','r','y','`',0 };
2504 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2505 if (rc != ERROR_SUCCESS)
2506 return ERROR_SUCCESS;
2508 /* increment progress bar each time action data is sent */
2509 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2511 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2513 msiobj_release(&view->hdr);
2514 return rc;
2517 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2519 LONG res;
2520 HKEY hkey;
2521 DWORD num_subkeys, num_values;
2523 if (delete_key)
2525 if ((res = RegDeleteTreeW( hkey_root, key )))
2527 WARN("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2529 return;
2532 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2534 if ((res = RegDeleteValueW( hkey, value )))
2536 WARN("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2538 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2539 NULL, NULL, NULL, NULL );
2540 RegCloseKey( hkey );
2542 if (!res && !num_subkeys && !num_values)
2544 TRACE("Removing empty key %s\n", debugstr_w(key));
2545 RegDeleteKeyW( hkey_root, key );
2547 return;
2549 WARN("Failed to open key %s (%d)\n", debugstr_w(key), res);
2553 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2555 MSIPACKAGE *package = param;
2556 LPCWSTR component, name, key_str, root_key_str;
2557 LPWSTR deformated_key, deformated_name, ui_key_str;
2558 MSICOMPONENT *comp;
2559 MSIRECORD *uirow;
2560 BOOL delete_key = FALSE;
2561 HKEY hkey_root;
2562 UINT size;
2563 INT root;
2565 ui_progress( package, 2, 0, 0, 0 );
2567 component = MSI_RecordGetString( row, 6 );
2568 comp = get_loaded_component( package, component );
2569 if (!comp)
2570 return ERROR_SUCCESS;
2572 if (!comp->Enabled)
2574 TRACE("component is disabled\n");
2575 return ERROR_SUCCESS;
2578 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2580 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2581 comp->Action = comp->Installed;
2582 return ERROR_SUCCESS;
2584 comp->Action = INSTALLSTATE_ABSENT;
2586 name = MSI_RecordGetString( row, 4 );
2587 if (MSI_RecordIsNull( row, 5 ) && name )
2589 if (name[0] == '+' && !name[1])
2590 return ERROR_SUCCESS;
2591 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2593 delete_key = TRUE;
2594 name = NULL;
2598 root = MSI_RecordGetInteger( row, 2 );
2599 key_str = MSI_RecordGetString( row, 3 );
2601 root_key_str = get_root_key( package, root, &hkey_root );
2602 if (!root_key_str)
2603 return ERROR_SUCCESS;
2605 deformat_string( package, key_str, &deformated_key );
2606 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2607 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2608 strcpyW( ui_key_str, root_key_str );
2609 strcatW( ui_key_str, deformated_key );
2611 deformat_string( package, name, &deformated_name );
2613 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2614 msi_free( deformated_key );
2616 uirow = MSI_CreateRecord( 2 );
2617 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2618 MSI_RecordSetStringW( uirow, 2, deformated_name );
2620 ui_actiondata( package, szRemoveRegistryValues, uirow );
2621 msiobj_release( &uirow->hdr );
2623 msi_free( ui_key_str );
2624 msi_free( deformated_name );
2625 return ERROR_SUCCESS;
2628 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2630 MSIPACKAGE *package = param;
2631 LPCWSTR component, name, key_str, root_key_str;
2632 LPWSTR deformated_key, deformated_name, ui_key_str;
2633 MSICOMPONENT *comp;
2634 MSIRECORD *uirow;
2635 BOOL delete_key = FALSE;
2636 HKEY hkey_root;
2637 UINT size;
2638 INT root;
2640 ui_progress( package, 2, 0, 0, 0 );
2642 component = MSI_RecordGetString( row, 5 );
2643 comp = get_loaded_component( package, component );
2644 if (!comp)
2645 return ERROR_SUCCESS;
2647 if (!comp->Enabled)
2649 TRACE("component is disabled\n");
2650 return ERROR_SUCCESS;
2653 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2655 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2656 comp->Action = comp->Installed;
2657 return ERROR_SUCCESS;
2659 comp->Action = INSTALLSTATE_LOCAL;
2661 if ((name = MSI_RecordGetString( row, 4 )))
2663 if (name[0] == '-' && !name[1])
2665 delete_key = TRUE;
2666 name = NULL;
2670 root = MSI_RecordGetInteger( row, 2 );
2671 key_str = MSI_RecordGetString( row, 3 );
2673 root_key_str = get_root_key( package, root, &hkey_root );
2674 if (!root_key_str)
2675 return ERROR_SUCCESS;
2677 deformat_string( package, key_str, &deformated_key );
2678 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2679 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2680 strcpyW( ui_key_str, root_key_str );
2681 strcatW( ui_key_str, deformated_key );
2683 deformat_string( package, name, &deformated_name );
2685 delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key );
2686 msi_free( deformated_key );
2688 uirow = MSI_CreateRecord( 2 );
2689 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2690 MSI_RecordSetStringW( uirow, 2, deformated_name );
2692 ui_actiondata( package, szRemoveRegistryValues, uirow );
2693 msiobj_release( &uirow->hdr );
2695 msi_free( ui_key_str );
2696 msi_free( deformated_name );
2697 return ERROR_SUCCESS;
2700 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2702 UINT rc;
2703 MSIQUERY *view;
2704 static const WCHAR registry_query[] =
2705 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2706 '`','R','e','g','i','s','t','r','y','`',0 };
2707 static const WCHAR remove_registry_query[] =
2708 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2709 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2711 /* increment progress bar each time action data is sent */
2712 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2714 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2715 if (rc == ERROR_SUCCESS)
2717 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2718 msiobj_release( &view->hdr );
2719 if (rc != ERROR_SUCCESS)
2720 return rc;
2723 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2724 if (rc == ERROR_SUCCESS)
2726 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2727 msiobj_release( &view->hdr );
2728 if (rc != ERROR_SUCCESS)
2729 return rc;
2732 return ERROR_SUCCESS;
2735 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2737 package->script->CurrentlyScripting = TRUE;
2739 return ERROR_SUCCESS;
2743 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2745 MSICOMPONENT *comp;
2746 DWORD progress = 0;
2747 DWORD total = 0;
2748 static const WCHAR q1[]=
2749 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2750 '`','R','e','g','i','s','t','r','y','`',0};
2751 UINT rc;
2752 MSIQUERY * view;
2753 MSIFEATURE *feature;
2754 MSIFILE *file;
2756 TRACE("InstallValidate\n");
2758 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2759 if (rc == ERROR_SUCCESS)
2761 MSI_IterateRecords( view, &progress, NULL, package );
2762 msiobj_release( &view->hdr );
2763 total += progress * REG_PROGRESS_VALUE;
2766 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2767 total += COMPONENT_PROGRESS_VALUE;
2769 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2770 total += file->FileSize;
2772 ui_progress(package,0,total,0,0);
2774 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2776 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2777 debugstr_w(feature->Feature), feature->Installed,
2778 feature->ActionRequest, feature->Action);
2781 return ERROR_SUCCESS;
2784 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2786 MSIPACKAGE* package = param;
2787 LPCWSTR cond = NULL;
2788 LPCWSTR message = NULL;
2789 UINT r;
2791 static const WCHAR title[]=
2792 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2794 cond = MSI_RecordGetString(row,1);
2796 r = MSI_EvaluateConditionW(package,cond);
2797 if (r == MSICONDITION_FALSE)
2799 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2801 LPWSTR deformated;
2802 message = MSI_RecordGetString(row,2);
2803 deformat_string(package,message,&deformated);
2804 MessageBoxW(NULL,deformated,title,MB_OK);
2805 msi_free(deformated);
2808 return ERROR_INSTALL_FAILURE;
2811 return ERROR_SUCCESS;
2814 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2816 UINT rc;
2817 MSIQUERY * view = NULL;
2818 static const WCHAR ExecSeqQuery[] =
2819 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2820 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2822 TRACE("Checking launch conditions\n");
2824 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2825 if (rc != ERROR_SUCCESS)
2826 return ERROR_SUCCESS;
2828 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2829 msiobj_release(&view->hdr);
2831 return rc;
2834 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2837 if (!cmp->KeyPath)
2838 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2840 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2842 MSIRECORD * row = 0;
2843 UINT root,len;
2844 LPWSTR deformated,buffer,deformated_name;
2845 LPCWSTR key,name;
2846 static const WCHAR ExecSeqQuery[] =
2847 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2848 '`','R','e','g','i','s','t','r','y','`',' ',
2849 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2850 ' ','=',' ' ,'\'','%','s','\'',0 };
2851 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2852 static const WCHAR fmt2[]=
2853 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2855 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2856 if (!row)
2857 return NULL;
2859 root = MSI_RecordGetInteger(row,2);
2860 key = MSI_RecordGetString(row, 3);
2861 name = MSI_RecordGetString(row, 4);
2862 deformat_string(package, key , &deformated);
2863 deformat_string(package, name, &deformated_name);
2865 len = strlenW(deformated) + 6;
2866 if (deformated_name)
2867 len+=strlenW(deformated_name);
2869 buffer = msi_alloc( len *sizeof(WCHAR));
2871 if (deformated_name)
2872 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2873 else
2874 sprintfW(buffer,fmt,root,deformated);
2876 msi_free(deformated);
2877 msi_free(deformated_name);
2878 msiobj_release(&row->hdr);
2880 return buffer;
2882 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2884 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2885 return NULL;
2887 else
2889 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2891 if (file)
2892 return strdupW( file->TargetPath );
2894 return NULL;
2897 static HKEY openSharedDLLsKey(void)
2899 HKEY hkey=0;
2900 static const WCHAR path[] =
2901 {'S','o','f','t','w','a','r','e','\\',
2902 'M','i','c','r','o','s','o','f','t','\\',
2903 'W','i','n','d','o','w','s','\\',
2904 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2905 'S','h','a','r','e','d','D','L','L','s',0};
2907 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2908 return hkey;
2911 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2913 HKEY hkey;
2914 DWORD count=0;
2915 DWORD type;
2916 DWORD sz = sizeof(count);
2917 DWORD rc;
2919 hkey = openSharedDLLsKey();
2920 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2921 if (rc != ERROR_SUCCESS)
2922 count = 0;
2923 RegCloseKey(hkey);
2924 return count;
2927 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2929 HKEY hkey;
2931 hkey = openSharedDLLsKey();
2932 if (count > 0)
2933 msi_reg_set_val_dword( hkey, path, count );
2934 else
2935 RegDeleteValueW(hkey,path);
2936 RegCloseKey(hkey);
2937 return count;
2941 * Return TRUE if the count should be written out and FALSE if not
2943 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2945 MSIFEATURE *feature;
2946 INT count = 0;
2947 BOOL write = FALSE;
2949 /* only refcount DLLs */
2950 if (comp->KeyPath == NULL ||
2951 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2952 comp->Attributes & msidbComponentAttributesODBCDataSource)
2953 write = FALSE;
2954 else
2956 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2957 write = (count > 0);
2959 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2960 write = TRUE;
2963 /* increment counts */
2964 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2966 ComponentList *cl;
2968 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2969 continue;
2971 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2973 if ( cl->component == comp )
2974 count++;
2978 /* decrement counts */
2979 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2981 ComponentList *cl;
2983 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2984 continue;
2986 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2988 if ( cl->component == comp )
2989 count--;
2993 /* ref count all the files in the component */
2994 if (write)
2996 MSIFILE *file;
2998 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3000 if (file->Component == comp)
3001 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3005 /* add a count for permanent */
3006 if (comp->Attributes & msidbComponentAttributesPermanent)
3007 count ++;
3009 comp->RefCount = count;
3011 if (write)
3012 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3015 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3017 WCHAR squished_pc[GUID_SIZE];
3018 WCHAR squished_cc[GUID_SIZE];
3019 UINT rc;
3020 MSICOMPONENT *comp;
3021 HKEY hkey;
3023 TRACE("\n");
3025 squash_guid(package->ProductCode,squished_pc);
3026 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
3028 msi_set_sourcedir_props(package, FALSE);
3030 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3032 MSIRECORD * uirow;
3034 ui_progress(package,2,0,0,0);
3035 if (!comp->ComponentId)
3036 continue;
3038 squash_guid(comp->ComponentId,squished_cc);
3040 msi_free(comp->FullKeypath);
3041 comp->FullKeypath = resolve_keypath( package, comp );
3043 ACTION_RefCountComponent( package, comp );
3045 TRACE("Component %s (%s), Keypath=%s, RefCount=%i Request=%u\n",
3046 debugstr_w(comp->Component),
3047 debugstr_w(squished_cc),
3048 debugstr_w(comp->FullKeypath),
3049 comp->RefCount,
3050 comp->ActionRequest);
3052 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3053 comp->ActionRequest == INSTALLSTATE_SOURCE)
3055 if (!comp->FullKeypath)
3056 continue;
3058 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3059 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
3060 &hkey, TRUE);
3061 else
3062 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
3063 &hkey, TRUE);
3065 if (rc != ERROR_SUCCESS)
3066 continue;
3068 if (comp->Attributes & msidbComponentAttributesPermanent)
3070 static const WCHAR szPermKey[] =
3071 { '0','0','0','0','0','0','0','0','0','0','0','0',
3072 '0','0','0','0','0','0','0','0','0','0','0','0',
3073 '0','0','0','0','0','0','0','0',0 };
3075 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3078 if (comp->ActionRequest == INSTALLSTATE_LOCAL)
3079 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3080 else
3082 MSIFILE *file;
3083 MSIRECORD *row;
3084 LPWSTR ptr, ptr2;
3085 WCHAR source[MAX_PATH];
3086 WCHAR base[MAX_PATH];
3087 LPWSTR sourcepath;
3089 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3090 static const WCHAR query[] = {
3091 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3092 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3093 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3094 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3095 '`','D','i','s','k','I','d','`',0};
3097 file = get_loaded_file(package, comp->KeyPath);
3098 if (!file)
3099 continue;
3101 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3102 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3103 ptr2 = strrchrW(source, '\\') + 1;
3104 msiobj_release(&row->hdr);
3106 lstrcpyW(base, package->PackagePath);
3107 ptr = strrchrW(base, '\\');
3108 *(ptr + 1) = '\0';
3110 sourcepath = resolve_file_source(package, file);
3111 ptr = sourcepath + lstrlenW(base);
3112 lstrcpyW(ptr2, ptr);
3113 msi_free(sourcepath);
3115 msi_reg_set_val_str(hkey, squished_pc, source);
3117 RegCloseKey(hkey);
3119 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3121 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3122 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3123 else
3124 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3126 comp->Action = comp->ActionRequest;
3128 /* UI stuff */
3129 uirow = MSI_CreateRecord(3);
3130 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3131 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3132 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3133 ui_actiondata(package,szProcessComponents,uirow);
3134 msiobj_release( &uirow->hdr );
3137 return ERROR_SUCCESS;
3140 typedef struct {
3141 CLSID clsid;
3142 LPWSTR source;
3144 LPWSTR path;
3145 ITypeLib *ptLib;
3146 } typelib_struct;
3148 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3149 LPWSTR lpszName, LONG_PTR lParam)
3151 TLIBATTR *attr;
3152 typelib_struct *tl_struct = (typelib_struct*) lParam;
3153 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3154 int sz;
3155 HRESULT res;
3157 if (!IS_INTRESOURCE(lpszName))
3159 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3160 return TRUE;
3163 sz = strlenW(tl_struct->source)+4;
3164 sz *= sizeof(WCHAR);
3166 if ((INT_PTR)lpszName == 1)
3167 tl_struct->path = strdupW(tl_struct->source);
3168 else
3170 tl_struct->path = msi_alloc(sz);
3171 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3174 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3175 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3176 if (FAILED(res))
3178 msi_free(tl_struct->path);
3179 tl_struct->path = NULL;
3181 return TRUE;
3184 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3185 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3187 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3188 return FALSE;
3191 msi_free(tl_struct->path);
3192 tl_struct->path = NULL;
3194 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3195 ITypeLib_Release(tl_struct->ptLib);
3197 return TRUE;
3200 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3202 MSIPACKAGE* package = param;
3203 LPCWSTR component;
3204 MSICOMPONENT *comp;
3205 MSIFILE *file;
3206 typelib_struct tl_struct;
3207 ITypeLib *tlib;
3208 HMODULE module;
3209 HRESULT hr;
3211 component = MSI_RecordGetString(row,3);
3212 comp = get_loaded_component(package,component);
3213 if (!comp)
3214 return ERROR_SUCCESS;
3216 if (!comp->Enabled)
3218 TRACE("component is disabled\n");
3219 return ERROR_SUCCESS;
3222 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3224 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3225 comp->Action = comp->Installed;
3226 return ERROR_SUCCESS;
3228 comp->Action = INSTALLSTATE_LOCAL;
3230 file = get_loaded_file( package, comp->KeyPath );
3231 if (!file)
3232 return ERROR_SUCCESS;
3234 ui_actiondata( package, szRegisterTypeLibraries, row );
3236 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3237 if (module)
3239 LPCWSTR guid;
3240 guid = MSI_RecordGetString(row,1);
3241 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3242 tl_struct.source = strdupW( file->TargetPath );
3243 tl_struct.path = NULL;
3245 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3246 (LONG_PTR)&tl_struct);
3248 if (tl_struct.path)
3250 LPWSTR help = NULL;
3251 LPCWSTR helpid;
3252 HRESULT res;
3254 helpid = MSI_RecordGetString(row,6);
3256 if (helpid)
3257 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3258 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3259 msi_free(help);
3261 if (FAILED(res))
3262 ERR("Failed to register type library %s\n",
3263 debugstr_w(tl_struct.path));
3264 else
3265 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3267 ITypeLib_Release(tl_struct.ptLib);
3268 msi_free(tl_struct.path);
3270 else
3271 ERR("Failed to load type library %s\n",
3272 debugstr_w(tl_struct.source));
3274 FreeLibrary(module);
3275 msi_free(tl_struct.source);
3277 else
3279 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3280 if (FAILED(hr))
3282 ERR("Failed to load type library: %08x\n", hr);
3283 return ERROR_INSTALL_FAILURE;
3286 ITypeLib_Release(tlib);
3289 return ERROR_SUCCESS;
3292 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3295 * OK this is a bit confusing.. I am given a _Component key and I believe
3296 * that the file that is being registered as a type library is the "key file
3297 * of that component" which I interpret to mean "The file in the KeyPath of
3298 * that component".
3300 UINT rc;
3301 MSIQUERY * view;
3302 static const WCHAR Query[] =
3303 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3304 '`','T','y','p','e','L','i','b','`',0};
3306 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3307 if (rc != ERROR_SUCCESS)
3308 return ERROR_SUCCESS;
3310 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3311 msiobj_release(&view->hdr);
3312 return rc;
3315 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3317 MSIPACKAGE *package = param;
3318 LPCWSTR component, guid;
3319 MSICOMPONENT *comp;
3320 GUID libid;
3321 UINT version;
3322 LCID language;
3323 SYSKIND syskind;
3324 HRESULT hr;
3326 component = MSI_RecordGetString( row, 3 );
3327 comp = get_loaded_component( package, component );
3328 if (!comp)
3329 return ERROR_SUCCESS;
3331 if (!comp->Enabled)
3333 TRACE("component is disabled\n");
3334 return ERROR_SUCCESS;
3337 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3339 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3340 comp->Action = comp->Installed;
3341 return ERROR_SUCCESS;
3343 comp->Action = INSTALLSTATE_ABSENT;
3345 ui_actiondata( package, szUnregisterTypeLibraries, row );
3347 guid = MSI_RecordGetString( row, 1 );
3348 CLSIDFromString( (LPCWSTR)guid, &libid );
3349 version = MSI_RecordGetInteger( row, 4 );
3350 language = MSI_RecordGetInteger( row, 2 );
3352 #ifdef _WIN64
3353 syskind = SYS_WIN64;
3354 #else
3355 syskind = SYS_WIN32;
3356 #endif
3358 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3359 if (FAILED(hr))
3361 WARN("Failed to unregister typelib: %08x\n", hr);
3364 return ERROR_SUCCESS;
3367 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3369 UINT rc;
3370 MSIQUERY *view;
3371 static const WCHAR query[] =
3372 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3373 '`','T','y','p','e','L','i','b','`',0};
3375 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3376 if (rc != ERROR_SUCCESS)
3377 return ERROR_SUCCESS;
3379 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3380 msiobj_release( &view->hdr );
3381 return rc;
3384 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3386 static const WCHAR szlnk[] = {'.','l','n','k',0};
3387 LPCWSTR directory, extension;
3388 LPWSTR link_folder, link_file, filename;
3390 directory = MSI_RecordGetString( row, 2 );
3391 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3393 /* may be needed because of a bug somewhere else */
3394 create_full_pathW( link_folder );
3396 filename = msi_dup_record_field( row, 3 );
3397 reduce_to_longfilename( filename );
3399 extension = strchrW( filename, '.' );
3400 if (!extension || strcmpiW( extension, szlnk ))
3402 int len = strlenW( filename );
3403 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3404 memcpy( filename + len, szlnk, sizeof(szlnk) );
3406 link_file = build_directory_name( 2, link_folder, filename );
3407 msi_free( link_folder );
3408 msi_free( filename );
3410 return link_file;
3413 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3415 MSIPACKAGE *package = param;
3416 LPWSTR link_file, deformated, path;
3417 LPCWSTR component, target;
3418 MSICOMPONENT *comp;
3419 IShellLinkW *sl = NULL;
3420 IPersistFile *pf = NULL;
3421 HRESULT res;
3423 component = MSI_RecordGetString(row, 4);
3424 comp = get_loaded_component(package, component);
3425 if (!comp)
3426 return ERROR_SUCCESS;
3428 if (!comp->Enabled)
3430 TRACE("component is disabled\n");
3431 return ERROR_SUCCESS;
3434 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3436 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3437 comp->Action = comp->Installed;
3438 return ERROR_SUCCESS;
3440 comp->Action = INSTALLSTATE_LOCAL;
3442 ui_actiondata(package,szCreateShortcuts,row);
3444 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3445 &IID_IShellLinkW, (LPVOID *) &sl );
3447 if (FAILED( res ))
3449 ERR("CLSID_ShellLink not available\n");
3450 goto err;
3453 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3454 if (FAILED( res ))
3456 ERR("QueryInterface(IID_IPersistFile) failed\n");
3457 goto err;
3460 target = MSI_RecordGetString(row, 5);
3461 if (strchrW(target, '['))
3463 deformat_string(package, target, &deformated);
3464 IShellLinkW_SetPath(sl,deformated);
3465 msi_free(deformated);
3467 else
3469 FIXME("poorly handled shortcut format, advertised shortcut\n");
3470 IShellLinkW_SetPath(sl,comp->FullKeypath);
3473 if (!MSI_RecordIsNull(row,6))
3475 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3476 deformat_string(package, arguments, &deformated);
3477 IShellLinkW_SetArguments(sl,deformated);
3478 msi_free(deformated);
3481 if (!MSI_RecordIsNull(row,7))
3483 LPCWSTR description = MSI_RecordGetString(row, 7);
3484 IShellLinkW_SetDescription(sl, description);
3487 if (!MSI_RecordIsNull(row,8))
3488 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3490 if (!MSI_RecordIsNull(row,9))
3492 INT index;
3493 LPCWSTR icon = MSI_RecordGetString(row, 9);
3495 path = build_icon_path(package, icon);
3496 index = MSI_RecordGetInteger(row,10);
3498 /* no value means 0 */
3499 if (index == MSI_NULL_INTEGER)
3500 index = 0;
3502 IShellLinkW_SetIconLocation(sl, path, index);
3503 msi_free(path);
3506 if (!MSI_RecordIsNull(row,11))
3507 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3509 if (!MSI_RecordIsNull(row,12))
3511 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3512 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3513 if (path)
3514 IShellLinkW_SetWorkingDirectory(sl, path);
3515 msi_free(path);
3518 link_file = get_link_file(package, row);
3520 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3521 IPersistFile_Save(pf, link_file, FALSE);
3523 msi_free(link_file);
3525 err:
3526 if (pf)
3527 IPersistFile_Release( pf );
3528 if (sl)
3529 IShellLinkW_Release( sl );
3531 return ERROR_SUCCESS;
3534 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3536 UINT rc;
3537 HRESULT res;
3538 MSIQUERY * view;
3539 static const WCHAR Query[] =
3540 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3541 '`','S','h','o','r','t','c','u','t','`',0};
3543 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3544 if (rc != ERROR_SUCCESS)
3545 return ERROR_SUCCESS;
3547 res = CoInitialize( NULL );
3549 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3550 msiobj_release(&view->hdr);
3552 if (SUCCEEDED(res))
3553 CoUninitialize();
3555 return rc;
3558 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3560 MSIPACKAGE *package = param;
3561 LPWSTR link_file;
3562 LPCWSTR component;
3563 MSICOMPONENT *comp;
3565 component = MSI_RecordGetString( row, 4 );
3566 comp = get_loaded_component( package, component );
3567 if (!comp)
3568 return ERROR_SUCCESS;
3570 if (!comp->Enabled)
3572 TRACE("component is disabled\n");
3573 return ERROR_SUCCESS;
3576 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3578 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3579 comp->Action = comp->Installed;
3580 return ERROR_SUCCESS;
3582 comp->Action = INSTALLSTATE_ABSENT;
3584 ui_actiondata( package, szRemoveShortcuts, row );
3586 link_file = get_link_file( package, row );
3588 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3589 if (!DeleteFileW( link_file ))
3591 WARN("Failed to remove shortcut file %u\n", GetLastError());
3593 msi_free( link_file );
3595 return ERROR_SUCCESS;
3598 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3600 UINT rc;
3601 MSIQUERY *view;
3602 static const WCHAR query[] =
3603 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3604 '`','S','h','o','r','t','c','u','t','`',0};
3606 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3607 if (rc != ERROR_SUCCESS)
3608 return ERROR_SUCCESS;
3610 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3611 msiobj_release( &view->hdr );
3613 return rc;
3616 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3618 MSIPACKAGE* package = param;
3619 HANDLE the_file;
3620 LPWSTR FilePath;
3621 LPCWSTR FileName;
3622 CHAR buffer[1024];
3623 DWORD sz;
3624 UINT rc;
3626 FileName = MSI_RecordGetString(row,1);
3627 if (!FileName)
3629 ERR("Unable to get FileName\n");
3630 return ERROR_SUCCESS;
3633 FilePath = build_icon_path(package,FileName);
3635 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3637 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3638 FILE_ATTRIBUTE_NORMAL, NULL);
3640 if (the_file == INVALID_HANDLE_VALUE)
3642 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3643 msi_free(FilePath);
3644 return ERROR_SUCCESS;
3649 DWORD write;
3650 sz = 1024;
3651 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3652 if (rc != ERROR_SUCCESS)
3654 ERR("Failed to get stream\n");
3655 CloseHandle(the_file);
3656 DeleteFileW(FilePath);
3657 break;
3659 WriteFile(the_file,buffer,sz,&write,NULL);
3660 } while (sz == 1024);
3662 msi_free(FilePath);
3663 CloseHandle(the_file);
3665 return ERROR_SUCCESS;
3668 static UINT msi_publish_icons(MSIPACKAGE *package)
3670 UINT r;
3671 MSIQUERY *view;
3673 static const WCHAR query[]= {
3674 'S','E','L','E','C','T',' ','*',' ',
3675 'F','R','O','M',' ','`','I','c','o','n','`',0};
3677 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3678 if (r == ERROR_SUCCESS)
3680 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3681 msiobj_release(&view->hdr);
3684 return ERROR_SUCCESS;
3687 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3689 UINT r;
3690 HKEY source;
3691 LPWSTR buffer;
3692 MSIMEDIADISK *disk;
3693 MSISOURCELISTINFO *info;
3695 r = RegCreateKeyW(hkey, szSourceList, &source);
3696 if (r != ERROR_SUCCESS)
3697 return r;
3699 RegCloseKey(source);
3701 buffer = strrchrW(package->PackagePath, '\\') + 1;
3702 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3703 package->Context, MSICODE_PRODUCT,
3704 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3705 if (r != ERROR_SUCCESS)
3706 return r;
3708 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3709 package->Context, MSICODE_PRODUCT,
3710 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3711 if (r != ERROR_SUCCESS)
3712 return r;
3714 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3715 package->Context, MSICODE_PRODUCT,
3716 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3717 if (r != ERROR_SUCCESS)
3718 return r;
3720 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3722 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3723 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3724 info->options, info->value);
3725 else
3726 MsiSourceListSetInfoW(package->ProductCode, NULL,
3727 info->context, info->options,
3728 info->property, info->value);
3731 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3733 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3734 disk->context, disk->options,
3735 disk->disk_id, disk->volume_label, disk->disk_prompt);
3738 return ERROR_SUCCESS;
3741 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3743 MSIHANDLE hdb, suminfo;
3744 WCHAR guids[MAX_PATH];
3745 WCHAR packcode[SQUISH_GUID_SIZE];
3746 LPWSTR buffer;
3747 LPWSTR ptr;
3748 DWORD langid;
3749 DWORD size;
3750 UINT r;
3752 static const WCHAR szProductLanguage[] =
3753 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3754 static const WCHAR szARPProductIcon[] =
3755 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3756 static const WCHAR szProductVersion[] =
3757 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3758 static const WCHAR szAssignment[] =
3759 {'A','s','s','i','g','n','m','e','n','t',0};
3760 static const WCHAR szAdvertiseFlags[] =
3761 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3762 static const WCHAR szClients[] =
3763 {'C','l','i','e','n','t','s',0};
3764 static const WCHAR szColon[] = {':',0};
3766 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3767 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3768 msi_free(buffer);
3770 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3771 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3773 /* FIXME */
3774 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3776 buffer = msi_dup_property(package->db, szARPProductIcon);
3777 if (buffer)
3779 LPWSTR path = build_icon_path(package,buffer);
3780 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3781 msi_free(path);
3782 msi_free(buffer);
3785 buffer = msi_dup_property(package->db, szProductVersion);
3786 if (buffer)
3788 DWORD verdword = msi_version_str_to_dword(buffer);
3789 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3790 msi_free(buffer);
3793 msi_reg_set_val_dword(hkey, szAssignment, 0);
3794 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3795 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3796 msi_reg_set_val_str(hkey, szClients, szColon);
3798 hdb = alloc_msihandle(&package->db->hdr);
3799 if (!hdb)
3800 return ERROR_NOT_ENOUGH_MEMORY;
3802 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3803 MsiCloseHandle(hdb);
3804 if (r != ERROR_SUCCESS)
3805 goto done;
3807 size = MAX_PATH;
3808 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3809 NULL, guids, &size);
3810 if (r != ERROR_SUCCESS)
3811 goto done;
3813 ptr = strchrW(guids, ';');
3814 if (ptr) *ptr = 0;
3815 squash_guid(guids, packcode);
3816 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3818 done:
3819 MsiCloseHandle(suminfo);
3820 return ERROR_SUCCESS;
3823 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3825 UINT r;
3826 HKEY hkey;
3827 LPWSTR upgrade;
3828 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3830 upgrade = msi_dup_property(package->db, szUpgradeCode);
3831 if (!upgrade)
3832 return ERROR_SUCCESS;
3834 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3836 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3837 if (r != ERROR_SUCCESS)
3838 goto done;
3840 else
3842 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3843 if (r != ERROR_SUCCESS)
3844 goto done;
3847 squash_guid(package->ProductCode, squashed_pc);
3848 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3850 RegCloseKey(hkey);
3852 done:
3853 msi_free(upgrade);
3854 return r;
3857 static BOOL msi_check_publish(MSIPACKAGE *package)
3859 MSIFEATURE *feature;
3861 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3863 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3864 return TRUE;
3867 return FALSE;
3870 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3872 MSIFEATURE *feature;
3874 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3876 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3877 return FALSE;
3880 return TRUE;
3883 static UINT msi_publish_patches( MSIPACKAGE *package )
3885 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
3886 WCHAR patch_squashed[GUID_SIZE];
3887 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
3888 LONG res;
3889 MSIPATCHINFO *patch;
3890 UINT r;
3891 WCHAR *p, *all_patches = NULL;
3892 DWORD len = 0;
3894 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, FALSE );
3895 if (r != ERROR_SUCCESS)
3896 return ERROR_FUNCTION_FAILED;
3898 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
3899 if (res != ERROR_SUCCESS)
3901 r = ERROR_FUNCTION_FAILED;
3902 goto done;
3905 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
3906 if (r != ERROR_SUCCESS)
3907 goto done;
3909 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3911 squash_guid( patch->patchcode, patch_squashed );
3912 len += strlenW( patch_squashed ) + 1;
3915 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
3916 if (!all_patches)
3917 goto done;
3919 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
3921 HKEY patch_key;
3923 squash_guid( patch->patchcode, p );
3924 p += strlenW( p ) + 1;
3926 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
3927 (const BYTE *)patch->transforms,
3928 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
3929 if (res != ERROR_SUCCESS)
3930 goto done;
3932 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
3933 if (r != ERROR_SUCCESS)
3934 goto done;
3936 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
3937 (const BYTE *)patch->localfile,
3938 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
3939 RegCloseKey( patch_key );
3940 if (res != ERROR_SUCCESS)
3941 goto done;
3943 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
3944 if (res != ERROR_SUCCESS)
3945 goto done;
3947 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
3948 RegCloseKey( patch_key );
3949 if (res != ERROR_SUCCESS)
3950 goto done;
3953 all_patches[len] = 0;
3954 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
3955 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3956 if (res != ERROR_SUCCESS)
3957 goto done;
3959 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
3960 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
3961 if (res != ERROR_SUCCESS)
3962 r = ERROR_FUNCTION_FAILED;
3964 done:
3965 RegCloseKey( product_patches_key );
3966 RegCloseKey( patches_key );
3967 RegCloseKey( product_key );
3968 msi_free( all_patches );
3969 return r;
3973 * 99% of the work done here is only done for
3974 * advertised installs. However this is where the
3975 * Icon table is processed and written out
3976 * so that is what I am going to do here.
3978 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3980 UINT rc;
3981 HKEY hukey = NULL, hudkey = NULL;
3982 MSIRECORD *uirow;
3984 if (!list_empty(&package->patches))
3986 rc = msi_publish_patches(package);
3987 if (rc != ERROR_SUCCESS)
3988 goto end;
3991 /* FIXME: also need to publish if the product is in advertise mode */
3992 if (!msi_check_publish(package))
3993 return ERROR_SUCCESS;
3995 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3996 &hukey, TRUE);
3997 if (rc != ERROR_SUCCESS)
3998 goto end;
4000 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4001 NULL, &hudkey, TRUE);
4002 if (rc != ERROR_SUCCESS)
4003 goto end;
4005 rc = msi_publish_upgrade_code(package);
4006 if (rc != ERROR_SUCCESS)
4007 goto end;
4009 rc = msi_publish_product_properties(package, hukey);
4010 if (rc != ERROR_SUCCESS)
4011 goto end;
4013 rc = msi_publish_sourcelist(package, hukey);
4014 if (rc != ERROR_SUCCESS)
4015 goto end;
4017 rc = msi_publish_icons(package);
4019 end:
4020 uirow = MSI_CreateRecord( 1 );
4021 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4022 ui_actiondata( package, szPublishProduct, uirow );
4023 msiobj_release( &uirow->hdr );
4025 RegCloseKey(hukey);
4026 RegCloseKey(hudkey);
4028 return rc;
4031 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4033 WCHAR *filename, *ptr, *folder, *ret;
4034 const WCHAR *dirprop;
4036 filename = msi_dup_record_field( row, 2 );
4037 if (filename && (ptr = strchrW( filename, '|' )))
4038 ptr++;
4039 else
4040 ptr = filename;
4042 dirprop = MSI_RecordGetString( row, 3 );
4043 if (dirprop)
4045 folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
4046 if (!folder)
4047 folder = msi_dup_property( package->db, dirprop );
4049 else
4050 folder = msi_dup_property( package->db, szWindowsFolder );
4052 if (!folder)
4054 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4055 msi_free( filename );
4056 return NULL;
4059 ret = build_directory_name( 2, folder, ptr );
4061 msi_free( filename );
4062 msi_free( folder );
4063 return ret;
4066 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4068 MSIPACKAGE *package = param;
4069 LPCWSTR component, section, key, value, identifier;
4070 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4071 MSIRECORD * uirow;
4072 INT action;
4073 MSICOMPONENT *comp;
4075 component = MSI_RecordGetString(row, 8);
4076 comp = get_loaded_component(package,component);
4077 if (!comp)
4078 return ERROR_SUCCESS;
4080 if (!comp->Enabled)
4082 TRACE("component is disabled\n");
4083 return ERROR_SUCCESS;
4086 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4088 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4089 comp->Action = comp->Installed;
4090 return ERROR_SUCCESS;
4092 comp->Action = INSTALLSTATE_LOCAL;
4094 identifier = MSI_RecordGetString(row,1);
4095 section = MSI_RecordGetString(row,4);
4096 key = MSI_RecordGetString(row,5);
4097 value = MSI_RecordGetString(row,6);
4098 action = MSI_RecordGetInteger(row,7);
4100 deformat_string(package,section,&deformated_section);
4101 deformat_string(package,key,&deformated_key);
4102 deformat_string(package,value,&deformated_value);
4104 fullname = get_ini_file_name(package, row);
4106 if (action == 0)
4108 TRACE("Adding value %s to section %s in %s\n",
4109 debugstr_w(deformated_key), debugstr_w(deformated_section),
4110 debugstr_w(fullname));
4111 WritePrivateProfileStringW(deformated_section, deformated_key,
4112 deformated_value, fullname);
4114 else if (action == 1)
4116 WCHAR returned[10];
4117 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4118 returned, 10, fullname);
4119 if (returned[0] == 0)
4121 TRACE("Adding value %s to section %s in %s\n",
4122 debugstr_w(deformated_key), debugstr_w(deformated_section),
4123 debugstr_w(fullname));
4125 WritePrivateProfileStringW(deformated_section, deformated_key,
4126 deformated_value, fullname);
4129 else if (action == 3)
4130 FIXME("Append to existing section not yet implemented\n");
4132 uirow = MSI_CreateRecord(4);
4133 MSI_RecordSetStringW(uirow,1,identifier);
4134 MSI_RecordSetStringW(uirow,2,deformated_section);
4135 MSI_RecordSetStringW(uirow,3,deformated_key);
4136 MSI_RecordSetStringW(uirow,4,deformated_value);
4137 ui_actiondata(package,szWriteIniValues,uirow);
4138 msiobj_release( &uirow->hdr );
4140 msi_free(fullname);
4141 msi_free(deformated_key);
4142 msi_free(deformated_value);
4143 msi_free(deformated_section);
4144 return ERROR_SUCCESS;
4147 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4149 UINT rc;
4150 MSIQUERY * view;
4151 static const WCHAR ExecSeqQuery[] =
4152 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4153 '`','I','n','i','F','i','l','e','`',0};
4155 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4156 if (rc != ERROR_SUCCESS)
4158 TRACE("no IniFile table\n");
4159 return ERROR_SUCCESS;
4162 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4163 msiobj_release(&view->hdr);
4164 return rc;
4167 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4169 MSIPACKAGE *package = param;
4170 LPCWSTR component, section, key, value, identifier;
4171 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4172 MSICOMPONENT *comp;
4173 MSIRECORD *uirow;
4174 INT action;
4176 component = MSI_RecordGetString( row, 8 );
4177 comp = get_loaded_component( package, component );
4178 if (!comp)
4179 return ERROR_SUCCESS;
4181 if (!comp->Enabled)
4183 TRACE("component is disabled\n");
4184 return ERROR_SUCCESS;
4187 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4189 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4190 comp->Action = comp->Installed;
4191 return ERROR_SUCCESS;
4193 comp->Action = INSTALLSTATE_ABSENT;
4195 identifier = MSI_RecordGetString( row, 1 );
4196 section = MSI_RecordGetString( row, 4 );
4197 key = MSI_RecordGetString( row, 5 );
4198 value = MSI_RecordGetString( row, 6 );
4199 action = MSI_RecordGetInteger( row, 7 );
4201 deformat_string( package, section, &deformated_section );
4202 deformat_string( package, key, &deformated_key );
4203 deformat_string( package, value, &deformated_value );
4205 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4207 filename = get_ini_file_name( package, row );
4209 TRACE("Removing key %s from section %s in %s\n",
4210 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4212 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4214 WARN("Unable to remove key %u\n", GetLastError());
4216 msi_free( filename );
4218 else
4219 FIXME("Unsupported action %d\n", action);
4222 uirow = MSI_CreateRecord( 4 );
4223 MSI_RecordSetStringW( uirow, 1, identifier );
4224 MSI_RecordSetStringW( uirow, 2, deformated_section );
4225 MSI_RecordSetStringW( uirow, 3, deformated_key );
4226 MSI_RecordSetStringW( uirow, 4, deformated_value );
4227 ui_actiondata( package, szRemoveIniValues, uirow );
4228 msiobj_release( &uirow->hdr );
4230 msi_free( deformated_key );
4231 msi_free( deformated_value );
4232 msi_free( deformated_section );
4233 return ERROR_SUCCESS;
4236 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4238 MSIPACKAGE *package = param;
4239 LPCWSTR component, section, key, value, identifier;
4240 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4241 MSICOMPONENT *comp;
4242 MSIRECORD *uirow;
4243 INT action;
4245 component = MSI_RecordGetString( row, 8 );
4246 comp = get_loaded_component( package, component );
4247 if (!comp)
4248 return ERROR_SUCCESS;
4250 if (!comp->Enabled)
4252 TRACE("component is disabled\n");
4253 return ERROR_SUCCESS;
4256 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4258 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4259 comp->Action = comp->Installed;
4260 return ERROR_SUCCESS;
4262 comp->Action = INSTALLSTATE_LOCAL;
4264 identifier = MSI_RecordGetString( row, 1 );
4265 section = MSI_RecordGetString( row, 4 );
4266 key = MSI_RecordGetString( row, 5 );
4267 value = MSI_RecordGetString( row, 6 );
4268 action = MSI_RecordGetInteger( row, 7 );
4270 deformat_string( package, section, &deformated_section );
4271 deformat_string( package, key, &deformated_key );
4272 deformat_string( package, value, &deformated_value );
4274 if (action == msidbIniFileActionRemoveLine)
4276 filename = get_ini_file_name( package, row );
4278 TRACE("Removing key %s from section %s in %s\n",
4279 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4281 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4283 WARN("Unable to remove key %u\n", GetLastError());
4285 msi_free( filename );
4287 else
4288 FIXME("Unsupported action %d\n", action);
4290 uirow = MSI_CreateRecord( 4 );
4291 MSI_RecordSetStringW( uirow, 1, identifier );
4292 MSI_RecordSetStringW( uirow, 2, deformated_section );
4293 MSI_RecordSetStringW( uirow, 3, deformated_key );
4294 MSI_RecordSetStringW( uirow, 4, deformated_value );
4295 ui_actiondata( package, szRemoveIniValues, uirow );
4296 msiobj_release( &uirow->hdr );
4298 msi_free( deformated_key );
4299 msi_free( deformated_value );
4300 msi_free( deformated_section );
4301 return ERROR_SUCCESS;
4304 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4306 UINT rc;
4307 MSIQUERY *view;
4308 static const WCHAR query[] =
4309 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4310 '`','I','n','i','F','i','l','e','`',0};
4311 static const WCHAR remove_query[] =
4312 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4313 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4315 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4316 if (rc == ERROR_SUCCESS)
4318 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4319 msiobj_release( &view->hdr );
4320 if (rc != ERROR_SUCCESS)
4321 return rc;
4324 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4325 if (rc == ERROR_SUCCESS)
4327 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4328 msiobj_release( &view->hdr );
4329 if (rc != ERROR_SUCCESS)
4330 return rc;
4333 return ERROR_SUCCESS;
4336 static void register_dll( const WCHAR *dll, BOOL unregister )
4338 HMODULE hmod;
4340 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4341 if (hmod)
4343 HRESULT (WINAPI *func_ptr)( void );
4344 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4346 func_ptr = (void *)GetProcAddress( hmod, func );
4347 if (func_ptr)
4349 HRESULT hr = func_ptr();
4350 if (FAILED( hr ))
4351 WARN("failed to register dll 0x%08x\n", hr);
4353 else
4354 WARN("entry point %s not found\n", func);
4355 FreeLibrary( hmod );
4356 return;
4358 WARN("failed to load library %u\n", GetLastError());
4361 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4363 MSIPACKAGE *package = param;
4364 LPCWSTR filename;
4365 MSIFILE *file;
4366 MSIRECORD *uirow;
4368 filename = MSI_RecordGetString(row,1);
4369 file = get_loaded_file( package, filename );
4371 if (!file)
4373 ERR("Unable to find file id %s\n",debugstr_w(filename));
4374 return ERROR_SUCCESS;
4377 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4379 register_dll( file->TargetPath, FALSE );
4381 uirow = MSI_CreateRecord( 2 );
4382 MSI_RecordSetStringW( uirow, 1, filename );
4383 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4384 ui_actiondata( package, szSelfRegModules, uirow );
4385 msiobj_release( &uirow->hdr );
4387 return ERROR_SUCCESS;
4390 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4392 UINT rc;
4393 MSIQUERY * view;
4394 static const WCHAR ExecSeqQuery[] =
4395 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4396 '`','S','e','l','f','R','e','g','`',0};
4398 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4399 if (rc != ERROR_SUCCESS)
4401 TRACE("no SelfReg table\n");
4402 return ERROR_SUCCESS;
4405 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4406 msiobj_release(&view->hdr);
4408 return ERROR_SUCCESS;
4411 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4413 MSIPACKAGE *package = param;
4414 LPCWSTR filename;
4415 MSIFILE *file;
4416 MSIRECORD *uirow;
4418 filename = MSI_RecordGetString( row, 1 );
4419 file = get_loaded_file( package, filename );
4421 if (!file)
4423 ERR("Unable to find file id %s\n", debugstr_w(filename));
4424 return ERROR_SUCCESS;
4427 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4429 register_dll( file->TargetPath, TRUE );
4431 uirow = MSI_CreateRecord( 2 );
4432 MSI_RecordSetStringW( uirow, 1, filename );
4433 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4434 ui_actiondata( package, szSelfUnregModules, uirow );
4435 msiobj_release( &uirow->hdr );
4437 return ERROR_SUCCESS;
4440 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4442 UINT rc;
4443 MSIQUERY *view;
4444 static const WCHAR query[] =
4445 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4446 '`','S','e','l','f','R','e','g','`',0};
4448 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4449 if (rc != ERROR_SUCCESS)
4451 TRACE("no SelfReg table\n");
4452 return ERROR_SUCCESS;
4455 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4456 msiobj_release( &view->hdr );
4458 return ERROR_SUCCESS;
4461 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4463 MSIFEATURE *feature;
4464 UINT rc;
4465 HKEY hkey = NULL, userdata = NULL;
4467 if (!msi_check_publish(package))
4468 return ERROR_SUCCESS;
4470 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4471 &hkey, TRUE);
4472 if (rc != ERROR_SUCCESS)
4473 goto end;
4475 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4476 &userdata, TRUE);
4477 if (rc != ERROR_SUCCESS)
4478 goto end;
4480 /* here the guids are base 85 encoded */
4481 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4483 ComponentList *cl;
4484 LPWSTR data = NULL;
4485 GUID clsid;
4486 INT size;
4487 BOOL absent = FALSE;
4488 MSIRECORD *uirow;
4490 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4491 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4492 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4494 size = 1;
4495 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4497 size += 21;
4499 if (feature->Feature_Parent)
4500 size += strlenW( feature->Feature_Parent )+2;
4502 data = msi_alloc(size * sizeof(WCHAR));
4504 data[0] = 0;
4505 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4507 MSICOMPONENT* component = cl->component;
4508 WCHAR buf[21];
4510 buf[0] = 0;
4511 if (component->ComponentId)
4513 TRACE("From %s\n",debugstr_w(component->ComponentId));
4514 CLSIDFromString(component->ComponentId, &clsid);
4515 encode_base85_guid(&clsid,buf);
4516 TRACE("to %s\n",debugstr_w(buf));
4517 strcatW(data,buf);
4521 if (feature->Feature_Parent)
4523 static const WCHAR sep[] = {'\2',0};
4524 strcatW(data,sep);
4525 strcatW(data,feature->Feature_Parent);
4528 msi_reg_set_val_str( userdata, feature->Feature, data );
4529 msi_free(data);
4531 size = 0;
4532 if (feature->Feature_Parent)
4533 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4534 if (!absent)
4536 size += sizeof(WCHAR);
4537 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4538 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4540 else
4542 size += 2*sizeof(WCHAR);
4543 data = msi_alloc(size);
4544 data[0] = 0x6;
4545 data[1] = 0;
4546 if (feature->Feature_Parent)
4547 strcpyW( &data[1], feature->Feature_Parent );
4548 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4549 (LPBYTE)data,size);
4550 msi_free(data);
4553 /* the UI chunk */
4554 uirow = MSI_CreateRecord( 1 );
4555 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4556 ui_actiondata( package, szPublishFeatures, uirow);
4557 msiobj_release( &uirow->hdr );
4558 /* FIXME: call ui_progress? */
4561 end:
4562 RegCloseKey(hkey);
4563 RegCloseKey(userdata);
4564 return rc;
4567 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4569 UINT r;
4570 HKEY hkey;
4571 MSIRECORD *uirow;
4573 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4575 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4576 &hkey, FALSE);
4577 if (r == ERROR_SUCCESS)
4579 RegDeleteValueW(hkey, feature->Feature);
4580 RegCloseKey(hkey);
4583 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4584 &hkey, FALSE);
4585 if (r == ERROR_SUCCESS)
4587 RegDeleteValueW(hkey, feature->Feature);
4588 RegCloseKey(hkey);
4591 uirow = MSI_CreateRecord( 1 );
4592 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4593 ui_actiondata( package, szUnpublishFeatures, uirow );
4594 msiobj_release( &uirow->hdr );
4596 return ERROR_SUCCESS;
4599 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4601 MSIFEATURE *feature;
4603 if (!msi_check_unpublish(package))
4604 return ERROR_SUCCESS;
4606 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4608 msi_unpublish_feature(package, feature);
4611 return ERROR_SUCCESS;
4614 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4616 SYSTEMTIME systime;
4617 DWORD size, langid;
4618 WCHAR date[9], *val, *buffer;
4619 const WCHAR *prop, *key;
4621 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4622 static const WCHAR szWindowsInstaller[] =
4623 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4624 static const WCHAR modpath_fmt[] =
4625 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4626 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4627 static const WCHAR szModifyPath[] =
4628 {'M','o','d','i','f','y','P','a','t','h',0};
4629 static const WCHAR szUninstallString[] =
4630 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4631 static const WCHAR szEstimatedSize[] =
4632 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4633 static const WCHAR szProductLanguage[] =
4634 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4635 static const WCHAR szProductVersion[] =
4636 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4637 static const WCHAR szDisplayVersion[] =
4638 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4639 static const WCHAR szInstallSource[] =
4640 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4641 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4642 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4643 static const WCHAR szAuthorizedCDFPrefix[] =
4644 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4645 static const WCHAR szARPCONTACT[] =
4646 {'A','R','P','C','O','N','T','A','C','T',0};
4647 static const WCHAR szContact[] =
4648 {'C','o','n','t','a','c','t',0};
4649 static const WCHAR szARPCOMMENTS[] =
4650 {'A','R','P','C','O','M','M','E','N','T','S',0};
4651 static const WCHAR szComments[] =
4652 {'C','o','m','m','e','n','t','s',0};
4653 static const WCHAR szProductName[] =
4654 {'P','r','o','d','u','c','t','N','a','m','e',0};
4655 static const WCHAR szDisplayName[] =
4656 {'D','i','s','p','l','a','y','N','a','m','e',0};
4657 static const WCHAR szARPHELPLINK[] =
4658 {'A','R','P','H','E','L','P','L','I','N','K',0};
4659 static const WCHAR szHelpLink[] =
4660 {'H','e','l','p','L','i','n','k',0};
4661 static const WCHAR szARPHELPTELEPHONE[] =
4662 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4663 static const WCHAR szHelpTelephone[] =
4664 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4665 static const WCHAR szARPINSTALLLOCATION[] =
4666 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4667 static const WCHAR szInstallLocation[] =
4668 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4669 static const WCHAR szManufacturer[] =
4670 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4671 static const WCHAR szPublisher[] =
4672 {'P','u','b','l','i','s','h','e','r',0};
4673 static const WCHAR szARPREADME[] =
4674 {'A','R','P','R','E','A','D','M','E',0};
4675 static const WCHAR szReadme[] =
4676 {'R','e','a','d','M','e',0};
4677 static const WCHAR szARPSIZE[] =
4678 {'A','R','P','S','I','Z','E',0};
4679 static const WCHAR szSize[] =
4680 {'S','i','z','e',0};
4681 static const WCHAR szARPURLINFOABOUT[] =
4682 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4683 static const WCHAR szURLInfoAbout[] =
4684 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4685 static const WCHAR szARPURLUPDATEINFO[] =
4686 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4687 static const WCHAR szURLUpdateInfo[] =
4688 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4690 static const WCHAR *propval[] = {
4691 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4692 szARPCONTACT, szContact,
4693 szARPCOMMENTS, szComments,
4694 szProductName, szDisplayName,
4695 szARPHELPLINK, szHelpLink,
4696 szARPHELPTELEPHONE, szHelpTelephone,
4697 szARPINSTALLLOCATION, szInstallLocation,
4698 cszSourceDir, szInstallSource,
4699 szManufacturer, szPublisher,
4700 szARPREADME, szReadme,
4701 szARPSIZE, szSize,
4702 szARPURLINFOABOUT, szURLInfoAbout,
4703 szARPURLUPDATEINFO, szURLUpdateInfo,
4704 NULL
4706 const WCHAR **p = propval;
4708 while (*p)
4710 prop = *p++;
4711 key = *p++;
4712 val = msi_dup_property(package->db, prop);
4713 msi_reg_set_val_str(hkey, key, val);
4714 msi_free(val);
4717 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4719 size = deformat_string(package, modpath_fmt, &buffer);
4720 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4721 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4722 msi_free(buffer);
4724 /* FIXME: Write real Estimated Size when we have it */
4725 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4727 GetLocalTime(&systime);
4728 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4729 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4731 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4732 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4734 buffer = msi_dup_property(package->db, szProductVersion);
4735 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4736 if (buffer)
4738 DWORD verdword = msi_version_str_to_dword(buffer);
4740 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4741 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4742 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4743 msi_free(buffer);
4746 return ERROR_SUCCESS;
4749 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4751 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4752 MSIRECORD *uirow;
4753 LPWSTR upgrade_code;
4754 HKEY hkey, props;
4755 HKEY upgrade;
4756 UINT rc;
4758 /* FIXME: also need to publish if the product is in advertise mode */
4759 if (!msi_check_publish(package))
4760 return ERROR_SUCCESS;
4762 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4763 if (rc != ERROR_SUCCESS)
4764 return rc;
4766 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4767 NULL, &props, TRUE);
4768 if (rc != ERROR_SUCCESS)
4769 goto done;
4771 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4772 msi_free( package->db->localfile );
4773 package->db->localfile = NULL;
4775 rc = msi_publish_install_properties(package, hkey);
4776 if (rc != ERROR_SUCCESS)
4777 goto done;
4779 rc = msi_publish_install_properties(package, props);
4780 if (rc != ERROR_SUCCESS)
4781 goto done;
4783 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4784 if (upgrade_code)
4786 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4787 squash_guid(package->ProductCode, squashed_pc);
4788 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4789 RegCloseKey(upgrade);
4790 msi_free(upgrade_code);
4793 done:
4794 uirow = MSI_CreateRecord( 1 );
4795 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4796 ui_actiondata( package, szRegisterProduct, uirow );
4797 msiobj_release( &uirow->hdr );
4799 RegCloseKey(hkey);
4800 return ERROR_SUCCESS;
4803 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4805 return execute_script(package,INSTALL_SCRIPT);
4808 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
4810 WCHAR *upgrade, **features;
4811 BOOL full_uninstall = TRUE;
4812 MSIFEATURE *feature;
4813 MSIPATCHINFO *patch;
4815 static const WCHAR szUpgradeCode[] =
4816 {'U','p','g','r','a','d','e','C','o','d','e',0};
4818 features = msi_split_string(remove, ',');
4819 if (!features)
4821 ERR("REMOVE feature list is empty!\n");
4822 return ERROR_FUNCTION_FAILED;
4825 if (!lstrcmpW(features[0], szAll))
4826 full_uninstall = TRUE;
4827 else
4829 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4831 if (feature->Action != INSTALLSTATE_ABSENT)
4832 full_uninstall = FALSE;
4835 msi_free(features);
4837 if (!full_uninstall)
4838 return ERROR_SUCCESS;
4840 MSIREG_DeleteProductKey(package->ProductCode);
4841 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4842 MSIREG_DeleteUninstallKey(package->ProductCode);
4844 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4846 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4847 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4849 else
4851 MSIREG_DeleteUserProductKey(package->ProductCode);
4852 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4855 upgrade = msi_dup_property(package->db, szUpgradeCode);
4856 if (upgrade)
4858 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4859 msi_free(upgrade);
4862 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4864 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4867 return ERROR_SUCCESS;
4870 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4872 UINT rc;
4873 WCHAR *remove;
4875 /* turn off scheduling */
4876 package->script->CurrentlyScripting= FALSE;
4878 /* first do the same as an InstallExecute */
4879 rc = ACTION_InstallExecute(package);
4880 if (rc != ERROR_SUCCESS)
4881 return rc;
4883 /* then handle Commit Actions */
4884 rc = execute_script(package,COMMIT_SCRIPT);
4885 if (rc != ERROR_SUCCESS)
4886 return rc;
4888 remove = msi_dup_property(package->db, szRemove);
4889 if (remove)
4890 rc = msi_unpublish_product(package, remove);
4892 msi_free(remove);
4893 return rc;
4896 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4898 static const WCHAR RunOnce[] = {
4899 'S','o','f','t','w','a','r','e','\\',
4900 'M','i','c','r','o','s','o','f','t','\\',
4901 'W','i','n','d','o','w','s','\\',
4902 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4903 'R','u','n','O','n','c','e',0};
4904 static const WCHAR InstallRunOnce[] = {
4905 'S','o','f','t','w','a','r','e','\\',
4906 'M','i','c','r','o','s','o','f','t','\\',
4907 'W','i','n','d','o','w','s','\\',
4908 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4909 'I','n','s','t','a','l','l','e','r','\\',
4910 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4912 static const WCHAR msiexec_fmt[] = {
4913 '%','s',
4914 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4915 '\"','%','s','\"',0};
4916 static const WCHAR install_fmt[] = {
4917 '/','I',' ','\"','%','s','\"',' ',
4918 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4919 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4920 WCHAR buffer[256], sysdir[MAX_PATH];
4921 HKEY hkey;
4922 WCHAR squished_pc[100];
4924 squash_guid(package->ProductCode,squished_pc);
4926 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4927 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4928 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4929 squished_pc);
4931 msi_reg_set_val_str( hkey, squished_pc, buffer );
4932 RegCloseKey(hkey);
4934 TRACE("Reboot command %s\n",debugstr_w(buffer));
4936 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4937 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4939 msi_reg_set_val_str( hkey, squished_pc, buffer );
4940 RegCloseKey(hkey);
4942 return ERROR_INSTALL_SUSPEND;
4945 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4947 DWORD attrib;
4948 UINT rc;
4951 * We are currently doing what should be done here in the top level Install
4952 * however for Administrative and uninstalls this step will be needed
4954 if (!package->PackagePath)
4955 return ERROR_SUCCESS;
4957 msi_set_sourcedir_props(package, TRUE);
4959 attrib = GetFileAttributesW(package->db->path);
4960 if (attrib == INVALID_FILE_ATTRIBUTES)
4962 LPWSTR prompt;
4963 LPWSTR msg;
4964 DWORD size = 0;
4966 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4967 package->Context, MSICODE_PRODUCT,
4968 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4969 if (rc == ERROR_MORE_DATA)
4971 prompt = msi_alloc(size * sizeof(WCHAR));
4972 MsiSourceListGetInfoW(package->ProductCode, NULL,
4973 package->Context, MSICODE_PRODUCT,
4974 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4976 else
4977 prompt = strdupW(package->db->path);
4979 msg = generate_error_string(package,1302,1,prompt);
4980 while(attrib == INVALID_FILE_ATTRIBUTES)
4982 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4983 if (rc == IDCANCEL)
4985 rc = ERROR_INSTALL_USEREXIT;
4986 break;
4988 attrib = GetFileAttributesW(package->db->path);
4990 msi_free(prompt);
4991 rc = ERROR_SUCCESS;
4993 else
4994 return ERROR_SUCCESS;
4996 return rc;
4999 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5001 HKEY hkey = 0;
5002 LPWSTR buffer, productid = NULL;
5003 UINT i, rc = ERROR_SUCCESS;
5004 MSIRECORD *uirow;
5006 static const WCHAR szPropKeys[][80] =
5008 {'P','r','o','d','u','c','t','I','D',0},
5009 {'U','S','E','R','N','A','M','E',0},
5010 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5011 {0},
5014 static const WCHAR szRegKeys[][80] =
5016 {'P','r','o','d','u','c','t','I','D',0},
5017 {'R','e','g','O','w','n','e','r',0},
5018 {'R','e','g','C','o','m','p','a','n','y',0},
5019 {0},
5022 if (msi_check_unpublish(package))
5024 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5025 goto end;
5028 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5029 if (!productid)
5030 goto end;
5032 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5033 NULL, &hkey, TRUE);
5034 if (rc != ERROR_SUCCESS)
5035 goto end;
5037 for( i = 0; szPropKeys[i][0]; i++ )
5039 buffer = msi_dup_property( package->db, szPropKeys[i] );
5040 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5041 msi_free( buffer );
5044 end:
5045 uirow = MSI_CreateRecord( 1 );
5046 MSI_RecordSetStringW( uirow, 1, productid );
5047 ui_actiondata( package, szRegisterUser, uirow );
5048 msiobj_release( &uirow->hdr );
5050 msi_free(productid);
5051 RegCloseKey(hkey);
5052 return rc;
5056 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5058 UINT rc;
5060 package->script->InWhatSequence |= SEQUENCE_EXEC;
5061 rc = ACTION_ProcessExecSequence(package,FALSE);
5062 return rc;
5066 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5068 MSIPACKAGE *package = param;
5069 LPCWSTR compgroupid, component, feature, qualifier, text;
5070 LPWSTR advertise = NULL, output = NULL;
5071 HKEY hkey = NULL;
5072 UINT rc;
5073 MSICOMPONENT *comp;
5074 MSIFEATURE *feat;
5075 DWORD sz;
5076 MSIRECORD *uirow;
5078 feature = MSI_RecordGetString(rec, 5);
5079 feat = get_loaded_feature(package, feature);
5080 if (!feat)
5081 return ERROR_SUCCESS;
5083 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
5084 feat->ActionRequest != INSTALLSTATE_SOURCE &&
5085 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
5087 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
5088 feat->Action = feat->Installed;
5089 return ERROR_SUCCESS;
5092 component = MSI_RecordGetString(rec, 3);
5093 comp = get_loaded_component(package, component);
5094 if (!comp)
5095 return ERROR_SUCCESS;
5097 compgroupid = MSI_RecordGetString(rec,1);
5098 qualifier = MSI_RecordGetString(rec,2);
5100 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5101 if (rc != ERROR_SUCCESS)
5102 goto end;
5104 text = MSI_RecordGetString(rec,4);
5105 advertise = create_component_advertise_string(package, comp, feature);
5107 sz = strlenW(advertise);
5109 if (text)
5110 sz += lstrlenW(text);
5112 sz+=3;
5113 sz *= sizeof(WCHAR);
5115 output = msi_alloc_zero(sz);
5116 strcpyW(output,advertise);
5117 msi_free(advertise);
5119 if (text)
5120 strcatW(output,text);
5122 msi_reg_set_val_multi_str( hkey, qualifier, output );
5124 end:
5125 RegCloseKey(hkey);
5126 msi_free(output);
5128 /* the UI chunk */
5129 uirow = MSI_CreateRecord( 2 );
5130 MSI_RecordSetStringW( uirow, 1, compgroupid );
5131 MSI_RecordSetStringW( uirow, 2, qualifier);
5132 ui_actiondata( package, szPublishComponents, uirow);
5133 msiobj_release( &uirow->hdr );
5134 /* FIXME: call ui_progress? */
5136 return rc;
5140 * At present I am ignorning the advertised components part of this and only
5141 * focusing on the qualified component sets
5143 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5145 UINT rc;
5146 MSIQUERY * view;
5147 static const WCHAR ExecSeqQuery[] =
5148 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5149 '`','P','u','b','l','i','s','h',
5150 'C','o','m','p','o','n','e','n','t','`',0};
5152 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5153 if (rc != ERROR_SUCCESS)
5154 return ERROR_SUCCESS;
5156 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5157 msiobj_release(&view->hdr);
5159 return rc;
5162 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5164 static const WCHAR szInstallerComponents[] = {
5165 'S','o','f','t','w','a','r','e','\\',
5166 'M','i','c','r','o','s','o','f','t','\\',
5167 'I','n','s','t','a','l','l','e','r','\\',
5168 'C','o','m','p','o','n','e','n','t','s','\\',0};
5170 MSIPACKAGE *package = param;
5171 LPCWSTR compgroupid, component, feature, qualifier;
5172 MSICOMPONENT *comp;
5173 MSIFEATURE *feat;
5174 MSIRECORD *uirow;
5175 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5176 LONG res;
5178 feature = MSI_RecordGetString( rec, 5 );
5179 feat = get_loaded_feature( package, feature );
5180 if (!feat)
5181 return ERROR_SUCCESS;
5183 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5185 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5186 feat->Action = feat->Installed;
5187 return ERROR_SUCCESS;
5190 component = MSI_RecordGetString( rec, 3 );
5191 comp = get_loaded_component( package, component );
5192 if (!comp)
5193 return ERROR_SUCCESS;
5195 compgroupid = MSI_RecordGetString( rec, 1 );
5196 qualifier = MSI_RecordGetString( rec, 2 );
5198 squash_guid( compgroupid, squashed );
5199 strcpyW( keypath, szInstallerComponents );
5200 strcatW( keypath, squashed );
5202 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5203 if (res != ERROR_SUCCESS)
5205 WARN("Unable to delete component key %d\n", res);
5208 uirow = MSI_CreateRecord( 2 );
5209 MSI_RecordSetStringW( uirow, 1, compgroupid );
5210 MSI_RecordSetStringW( uirow, 2, qualifier );
5211 ui_actiondata( package, szUnpublishComponents, uirow );
5212 msiobj_release( &uirow->hdr );
5214 return ERROR_SUCCESS;
5217 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5219 UINT rc;
5220 MSIQUERY *view;
5221 static const WCHAR query[] =
5222 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5223 '`','P','u','b','l','i','s','h',
5224 'C','o','m','p','o','n','e','n','t','`',0};
5226 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5227 if (rc != ERROR_SUCCESS)
5228 return ERROR_SUCCESS;
5230 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5231 msiobj_release( &view->hdr );
5233 return rc;
5236 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5238 MSIPACKAGE *package = param;
5239 MSIRECORD *row;
5240 MSIFILE *file;
5241 SC_HANDLE hscm, service = NULL;
5242 LPCWSTR comp, depends, pass;
5243 LPWSTR name = NULL, disp = NULL;
5244 LPCWSTR load_order, serv_name, key;
5245 DWORD serv_type, start_type;
5246 DWORD err_control;
5248 static const WCHAR query[] =
5249 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5250 '`','C','o','m','p','o','n','e','n','t','`',' ',
5251 'W','H','E','R','E',' ',
5252 '`','C','o','m','p','o','n','e','n','t','`',' ',
5253 '=','\'','%','s','\'',0};
5255 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5256 if (!hscm)
5258 ERR("Failed to open the SC Manager!\n");
5259 goto done;
5262 start_type = MSI_RecordGetInteger(rec, 5);
5263 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5264 goto done;
5266 depends = MSI_RecordGetString(rec, 8);
5267 if (depends && *depends)
5268 FIXME("Dependency list unhandled!\n");
5270 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5271 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5272 serv_type = MSI_RecordGetInteger(rec, 4);
5273 err_control = MSI_RecordGetInteger(rec, 6);
5274 load_order = MSI_RecordGetString(rec, 7);
5275 serv_name = MSI_RecordGetString(rec, 9);
5276 pass = MSI_RecordGetString(rec, 10);
5277 comp = MSI_RecordGetString(rec, 12);
5279 /* fetch the service path */
5280 row = MSI_QueryGetRecord(package->db, query, comp);
5281 if (!row)
5283 ERR("Control query failed!\n");
5284 goto done;
5287 key = MSI_RecordGetString(row, 6);
5289 file = get_loaded_file(package, key);
5290 msiobj_release(&row->hdr);
5291 if (!file)
5293 ERR("Failed to load the service file\n");
5294 goto done;
5297 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5298 start_type, err_control, file->TargetPath,
5299 load_order, NULL, NULL, serv_name, pass);
5300 if (!service)
5302 if (GetLastError() != ERROR_SERVICE_EXISTS)
5303 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5306 done:
5307 CloseServiceHandle(service);
5308 CloseServiceHandle(hscm);
5309 msi_free(name);
5310 msi_free(disp);
5312 return ERROR_SUCCESS;
5315 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5317 UINT rc;
5318 MSIQUERY * view;
5319 static const WCHAR ExecSeqQuery[] =
5320 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5321 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5323 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5324 if (rc != ERROR_SUCCESS)
5325 return ERROR_SUCCESS;
5327 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5328 msiobj_release(&view->hdr);
5330 return rc;
5333 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5334 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5336 LPCWSTR *vector, *temp_vector;
5337 LPWSTR p, q;
5338 DWORD sep_len;
5340 static const WCHAR separator[] = {'[','~',']',0};
5342 *numargs = 0;
5343 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5345 if (!args)
5346 return NULL;
5348 vector = msi_alloc(sizeof(LPWSTR));
5349 if (!vector)
5350 return NULL;
5352 p = args;
5355 (*numargs)++;
5356 vector[*numargs - 1] = p;
5358 if ((q = strstrW(p, separator)))
5360 *q = '\0';
5362 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5363 if (!temp_vector)
5365 msi_free(vector);
5366 return NULL;
5368 vector = temp_vector;
5370 p = q + sep_len;
5372 } while (q);
5374 return vector;
5377 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5379 MSIPACKAGE *package = param;
5380 MSICOMPONENT *comp;
5381 MSIRECORD *uirow;
5382 SC_HANDLE scm = NULL, service = NULL;
5383 LPCWSTR component, *vector = NULL;
5384 LPWSTR name, args, display_name = NULL;
5385 DWORD event, numargs, len;
5386 UINT r = ERROR_FUNCTION_FAILED;
5388 component = MSI_RecordGetString(rec, 6);
5389 comp = get_loaded_component(package, component);
5390 if (!comp)
5391 return ERROR_SUCCESS;
5393 if (!comp->Enabled)
5395 TRACE("component is disabled\n");
5396 return ERROR_SUCCESS;
5399 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5401 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5402 comp->Action = comp->Installed;
5403 return ERROR_SUCCESS;
5405 comp->Action = INSTALLSTATE_LOCAL;
5407 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5408 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5409 event = MSI_RecordGetInteger(rec, 3);
5411 if (!(event & msidbServiceControlEventStart))
5413 r = ERROR_SUCCESS;
5414 goto done;
5417 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5418 if (!scm)
5420 ERR("Failed to open the service control manager\n");
5421 goto done;
5424 len = 0;
5425 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5426 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5428 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5429 GetServiceDisplayNameW( scm, name, display_name, &len );
5432 service = OpenServiceW(scm, name, SERVICE_START);
5433 if (!service)
5435 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5436 goto done;
5439 vector = msi_service_args_to_vector(args, &numargs);
5441 if (!StartServiceW(service, numargs, vector) &&
5442 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5444 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5445 goto done;
5448 r = ERROR_SUCCESS;
5450 done:
5451 uirow = MSI_CreateRecord( 2 );
5452 MSI_RecordSetStringW( uirow, 1, display_name );
5453 MSI_RecordSetStringW( uirow, 2, name );
5454 ui_actiondata( package, szStartServices, uirow );
5455 msiobj_release( &uirow->hdr );
5457 CloseServiceHandle(service);
5458 CloseServiceHandle(scm);
5460 msi_free(name);
5461 msi_free(args);
5462 msi_free(vector);
5463 msi_free(display_name);
5464 return r;
5467 static UINT ACTION_StartServices( MSIPACKAGE *package )
5469 UINT rc;
5470 MSIQUERY *view;
5472 static const WCHAR query[] = {
5473 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5474 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5476 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5477 if (rc != ERROR_SUCCESS)
5478 return ERROR_SUCCESS;
5480 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5481 msiobj_release(&view->hdr);
5483 return rc;
5486 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5488 DWORD i, needed, count;
5489 ENUM_SERVICE_STATUSW *dependencies;
5490 SERVICE_STATUS ss;
5491 SC_HANDLE depserv;
5493 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5494 0, &needed, &count))
5495 return TRUE;
5497 if (GetLastError() != ERROR_MORE_DATA)
5498 return FALSE;
5500 dependencies = msi_alloc(needed);
5501 if (!dependencies)
5502 return FALSE;
5504 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5505 needed, &needed, &count))
5506 goto error;
5508 for (i = 0; i < count; i++)
5510 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5511 SERVICE_STOP | SERVICE_QUERY_STATUS);
5512 if (!depserv)
5513 goto error;
5515 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5516 goto error;
5519 return TRUE;
5521 error:
5522 msi_free(dependencies);
5523 return FALSE;
5526 static UINT stop_service( LPCWSTR name )
5528 SC_HANDLE scm = NULL, service = NULL;
5529 SERVICE_STATUS status;
5530 SERVICE_STATUS_PROCESS ssp;
5531 DWORD needed;
5533 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5534 if (!scm)
5536 WARN("Failed to open the SCM: %d\n", GetLastError());
5537 goto done;
5540 service = OpenServiceW(scm, name,
5541 SERVICE_STOP |
5542 SERVICE_QUERY_STATUS |
5543 SERVICE_ENUMERATE_DEPENDENTS);
5544 if (!service)
5546 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5547 goto done;
5550 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5551 sizeof(SERVICE_STATUS_PROCESS), &needed))
5553 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5554 goto done;
5557 if (ssp.dwCurrentState == SERVICE_STOPPED)
5558 goto done;
5560 stop_service_dependents(scm, service);
5562 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5563 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5565 done:
5566 CloseServiceHandle(service);
5567 CloseServiceHandle(scm);
5569 return ERROR_SUCCESS;
5572 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5574 MSIPACKAGE *package = param;
5575 MSICOMPONENT *comp;
5576 MSIRECORD *uirow;
5577 LPCWSTR component;
5578 LPWSTR name = NULL, display_name = NULL;
5579 DWORD event, len;
5580 SC_HANDLE scm;
5582 event = MSI_RecordGetInteger( rec, 3 );
5583 if (!(event & msidbServiceControlEventStop))
5584 return ERROR_SUCCESS;
5586 component = MSI_RecordGetString( rec, 6 );
5587 comp = get_loaded_component( package, component );
5588 if (!comp)
5589 return ERROR_SUCCESS;
5591 if (!comp->Enabled)
5593 TRACE("component is disabled\n");
5594 return ERROR_SUCCESS;
5597 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5599 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5600 comp->Action = comp->Installed;
5601 return ERROR_SUCCESS;
5603 comp->Action = INSTALLSTATE_ABSENT;
5605 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5606 if (!scm)
5608 ERR("Failed to open the service control manager\n");
5609 goto done;
5612 len = 0;
5613 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5614 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5616 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5617 GetServiceDisplayNameW( scm, name, display_name, &len );
5619 CloseServiceHandle( scm );
5621 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5622 stop_service( name );
5624 done:
5625 uirow = MSI_CreateRecord( 2 );
5626 MSI_RecordSetStringW( uirow, 1, display_name );
5627 MSI_RecordSetStringW( uirow, 2, name );
5628 ui_actiondata( package, szStopServices, uirow );
5629 msiobj_release( &uirow->hdr );
5631 msi_free( name );
5632 msi_free( display_name );
5633 return ERROR_SUCCESS;
5636 static UINT ACTION_StopServices( MSIPACKAGE *package )
5638 UINT rc;
5639 MSIQUERY *view;
5641 static const WCHAR query[] = {
5642 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5643 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5645 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5646 if (rc != ERROR_SUCCESS)
5647 return ERROR_SUCCESS;
5649 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5650 msiobj_release(&view->hdr);
5652 return rc;
5655 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5657 MSIPACKAGE *package = param;
5658 MSICOMPONENT *comp;
5659 MSIRECORD *uirow;
5660 LPCWSTR component;
5661 LPWSTR name = NULL, display_name = NULL;
5662 DWORD event, len;
5663 SC_HANDLE scm = NULL, service = NULL;
5665 event = MSI_RecordGetInteger( rec, 3 );
5666 if (!(event & msidbServiceControlEventDelete))
5667 return ERROR_SUCCESS;
5669 component = MSI_RecordGetString(rec, 6);
5670 comp = get_loaded_component(package, component);
5671 if (!comp)
5672 return ERROR_SUCCESS;
5674 if (!comp->Enabled)
5676 TRACE("component is disabled\n");
5677 return ERROR_SUCCESS;
5680 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5682 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5683 comp->Action = comp->Installed;
5684 return ERROR_SUCCESS;
5686 comp->Action = INSTALLSTATE_ABSENT;
5688 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5689 stop_service( name );
5691 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5692 if (!scm)
5694 WARN("Failed to open the SCM: %d\n", GetLastError());
5695 goto done;
5698 len = 0;
5699 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5700 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5702 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5703 GetServiceDisplayNameW( scm, name, display_name, &len );
5706 service = OpenServiceW( scm, name, DELETE );
5707 if (!service)
5709 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5710 goto done;
5713 if (!DeleteService( service ))
5714 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5716 done:
5717 uirow = MSI_CreateRecord( 2 );
5718 MSI_RecordSetStringW( uirow, 1, display_name );
5719 MSI_RecordSetStringW( uirow, 2, name );
5720 ui_actiondata( package, szDeleteServices, uirow );
5721 msiobj_release( &uirow->hdr );
5723 CloseServiceHandle( service );
5724 CloseServiceHandle( scm );
5725 msi_free( name );
5726 msi_free( display_name );
5728 return ERROR_SUCCESS;
5731 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5733 UINT rc;
5734 MSIQUERY *view;
5736 static const WCHAR query[] = {
5737 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5738 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5740 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5741 if (rc != ERROR_SUCCESS)
5742 return ERROR_SUCCESS;
5744 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5745 msiobj_release( &view->hdr );
5747 return rc;
5750 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5752 MSIFILE *file;
5754 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5756 if (!lstrcmpW(file->File, filename))
5757 return file;
5760 return NULL;
5763 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5765 MSIPACKAGE *package = param;
5766 LPWSTR driver, driver_path, ptr;
5767 WCHAR outpath[MAX_PATH];
5768 MSIFILE *driver_file, *setup_file;
5769 MSIRECORD *uirow;
5770 LPCWSTR desc;
5771 DWORD len, usage;
5772 UINT r = ERROR_SUCCESS;
5774 static const WCHAR driver_fmt[] = {
5775 'D','r','i','v','e','r','=','%','s',0};
5776 static const WCHAR setup_fmt[] = {
5777 'S','e','t','u','p','=','%','s',0};
5778 static const WCHAR usage_fmt[] = {
5779 'F','i','l','e','U','s','a','g','e','=','1',0};
5781 desc = MSI_RecordGetString(rec, 3);
5783 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5784 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5786 if (!driver_file)
5788 ERR("ODBC Driver entry not found!\n");
5789 return ERROR_FUNCTION_FAILED;
5792 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5793 if (setup_file)
5794 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5795 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5797 driver = msi_alloc(len * sizeof(WCHAR));
5798 if (!driver)
5799 return ERROR_OUTOFMEMORY;
5801 ptr = driver;
5802 lstrcpyW(ptr, desc);
5803 ptr += lstrlenW(ptr) + 1;
5805 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5806 ptr += len + 1;
5808 if (setup_file)
5810 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5811 ptr += len + 1;
5814 lstrcpyW(ptr, usage_fmt);
5815 ptr += lstrlenW(ptr) + 1;
5816 *ptr = '\0';
5818 driver_path = strdupW(driver_file->TargetPath);
5819 ptr = strrchrW(driver_path, '\\');
5820 if (ptr) *ptr = '\0';
5822 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5823 NULL, ODBC_INSTALL_COMPLETE, &usage))
5825 ERR("Failed to install SQL driver!\n");
5826 r = ERROR_FUNCTION_FAILED;
5829 uirow = MSI_CreateRecord( 5 );
5830 MSI_RecordSetStringW( uirow, 1, desc );
5831 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5832 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
5833 ui_actiondata( package, szInstallODBC, uirow );
5834 msiobj_release( &uirow->hdr );
5836 msi_free(driver);
5837 msi_free(driver_path);
5839 return r;
5842 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5844 MSIPACKAGE *package = param;
5845 LPWSTR translator, translator_path, ptr;
5846 WCHAR outpath[MAX_PATH];
5847 MSIFILE *translator_file, *setup_file;
5848 MSIRECORD *uirow;
5849 LPCWSTR desc;
5850 DWORD len, usage;
5851 UINT r = ERROR_SUCCESS;
5853 static const WCHAR translator_fmt[] = {
5854 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5855 static const WCHAR setup_fmt[] = {
5856 'S','e','t','u','p','=','%','s',0};
5858 desc = MSI_RecordGetString(rec, 3);
5860 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5861 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5863 if (!translator_file)
5865 ERR("ODBC Translator entry not found!\n");
5866 return ERROR_FUNCTION_FAILED;
5869 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5870 if (setup_file)
5871 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5873 translator = msi_alloc(len * sizeof(WCHAR));
5874 if (!translator)
5875 return ERROR_OUTOFMEMORY;
5877 ptr = translator;
5878 lstrcpyW(ptr, desc);
5879 ptr += lstrlenW(ptr) + 1;
5881 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5882 ptr += len + 1;
5884 if (setup_file)
5886 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5887 ptr += len + 1;
5889 *ptr = '\0';
5891 translator_path = strdupW(translator_file->TargetPath);
5892 ptr = strrchrW(translator_path, '\\');
5893 if (ptr) *ptr = '\0';
5895 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5896 NULL, ODBC_INSTALL_COMPLETE, &usage))
5898 ERR("Failed to install SQL translator!\n");
5899 r = ERROR_FUNCTION_FAILED;
5902 uirow = MSI_CreateRecord( 5 );
5903 MSI_RecordSetStringW( uirow, 1, desc );
5904 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5905 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
5906 ui_actiondata( package, szInstallODBC, uirow );
5907 msiobj_release( &uirow->hdr );
5909 msi_free(translator);
5910 msi_free(translator_path);
5912 return r;
5915 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5917 MSIPACKAGE *package = param;
5918 LPWSTR attrs;
5919 LPCWSTR desc, driver;
5920 WORD request = ODBC_ADD_SYS_DSN;
5921 INT registration;
5922 DWORD len;
5923 UINT r = ERROR_SUCCESS;
5924 MSIRECORD *uirow;
5926 static const WCHAR attrs_fmt[] = {
5927 'D','S','N','=','%','s',0 };
5929 desc = MSI_RecordGetString(rec, 3);
5930 driver = MSI_RecordGetString(rec, 4);
5931 registration = MSI_RecordGetInteger(rec, 5);
5933 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5934 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5936 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5937 attrs = msi_alloc(len * sizeof(WCHAR));
5938 if (!attrs)
5939 return ERROR_OUTOFMEMORY;
5941 len = sprintfW(attrs, attrs_fmt, desc);
5942 attrs[len + 1] = 0;
5944 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5946 ERR("Failed to install SQL data source!\n");
5947 r = ERROR_FUNCTION_FAILED;
5950 uirow = MSI_CreateRecord( 5 );
5951 MSI_RecordSetStringW( uirow, 1, desc );
5952 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5953 MSI_RecordSetInteger( uirow, 3, request );
5954 ui_actiondata( package, szInstallODBC, uirow );
5955 msiobj_release( &uirow->hdr );
5957 msi_free(attrs);
5959 return r;
5962 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5964 UINT rc;
5965 MSIQUERY *view;
5967 static const WCHAR driver_query[] = {
5968 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5969 'O','D','B','C','D','r','i','v','e','r',0 };
5971 static const WCHAR translator_query[] = {
5972 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5973 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5975 static const WCHAR source_query[] = {
5976 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5977 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5979 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5980 if (rc != ERROR_SUCCESS)
5981 return ERROR_SUCCESS;
5983 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5984 msiobj_release(&view->hdr);
5986 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5987 if (rc != ERROR_SUCCESS)
5988 return ERROR_SUCCESS;
5990 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5991 msiobj_release(&view->hdr);
5993 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5994 if (rc != ERROR_SUCCESS)
5995 return ERROR_SUCCESS;
5997 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5998 msiobj_release(&view->hdr);
6000 return rc;
6003 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6005 MSIPACKAGE *package = param;
6006 MSIRECORD *uirow;
6007 DWORD usage;
6008 LPCWSTR desc;
6010 desc = MSI_RecordGetString( rec, 3 );
6011 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6013 WARN("Failed to remove ODBC driver\n");
6015 else if (!usage)
6017 FIXME("Usage count reached 0\n");
6020 uirow = MSI_CreateRecord( 2 );
6021 MSI_RecordSetStringW( uirow, 1, desc );
6022 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6023 ui_actiondata( package, szRemoveODBC, uirow );
6024 msiobj_release( &uirow->hdr );
6026 return ERROR_SUCCESS;
6029 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6031 MSIPACKAGE *package = param;
6032 MSIRECORD *uirow;
6033 DWORD usage;
6034 LPCWSTR desc;
6036 desc = MSI_RecordGetString( rec, 3 );
6037 if (!SQLRemoveTranslatorW( desc, &usage ))
6039 WARN("Failed to remove ODBC translator\n");
6041 else if (!usage)
6043 FIXME("Usage count reached 0\n");
6046 uirow = MSI_CreateRecord( 2 );
6047 MSI_RecordSetStringW( uirow, 1, desc );
6048 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6049 ui_actiondata( package, szRemoveODBC, uirow );
6050 msiobj_release( &uirow->hdr );
6052 return ERROR_SUCCESS;
6055 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6057 MSIPACKAGE *package = param;
6058 MSIRECORD *uirow;
6059 LPWSTR attrs;
6060 LPCWSTR desc, driver;
6061 WORD request = ODBC_REMOVE_SYS_DSN;
6062 INT registration;
6063 DWORD len;
6065 static const WCHAR attrs_fmt[] = {
6066 'D','S','N','=','%','s',0 };
6068 desc = MSI_RecordGetString( rec, 3 );
6069 driver = MSI_RecordGetString( rec, 4 );
6070 registration = MSI_RecordGetInteger( rec, 5 );
6072 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6073 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6075 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6076 attrs = msi_alloc( len * sizeof(WCHAR) );
6077 if (!attrs)
6078 return ERROR_OUTOFMEMORY;
6080 FIXME("Use ODBCSourceAttribute table\n");
6082 len = sprintfW( attrs, attrs_fmt, desc );
6083 attrs[len + 1] = 0;
6085 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6087 WARN("Failed to remove ODBC data source\n");
6089 msi_free( attrs );
6091 uirow = MSI_CreateRecord( 3 );
6092 MSI_RecordSetStringW( uirow, 1, desc );
6093 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6094 MSI_RecordSetInteger( uirow, 3, request );
6095 ui_actiondata( package, szRemoveODBC, uirow );
6096 msiobj_release( &uirow->hdr );
6098 return ERROR_SUCCESS;
6101 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6103 UINT rc;
6104 MSIQUERY *view;
6106 static const WCHAR driver_query[] = {
6107 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6108 'O','D','B','C','D','r','i','v','e','r',0 };
6110 static const WCHAR translator_query[] = {
6111 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6112 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6114 static const WCHAR source_query[] = {
6115 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6116 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6118 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6119 if (rc != ERROR_SUCCESS)
6120 return ERROR_SUCCESS;
6122 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6123 msiobj_release( &view->hdr );
6125 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6126 if (rc != ERROR_SUCCESS)
6127 return ERROR_SUCCESS;
6129 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6130 msiobj_release( &view->hdr );
6132 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6133 if (rc != ERROR_SUCCESS)
6134 return ERROR_SUCCESS;
6136 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6137 msiobj_release( &view->hdr );
6139 return rc;
6142 #define ENV_ACT_SETALWAYS 0x1
6143 #define ENV_ACT_SETABSENT 0x2
6144 #define ENV_ACT_REMOVE 0x4
6145 #define ENV_ACT_REMOVEMATCH 0x8
6147 #define ENV_MOD_MACHINE 0x20000000
6148 #define ENV_MOD_APPEND 0x40000000
6149 #define ENV_MOD_PREFIX 0x80000000
6150 #define ENV_MOD_MASK 0xC0000000
6152 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6154 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6156 LPCWSTR cptr = *name;
6158 static const WCHAR prefix[] = {'[','~',']',0};
6159 static const int prefix_len = 3;
6161 *flags = 0;
6162 while (*cptr)
6164 if (*cptr == '=')
6165 *flags |= ENV_ACT_SETALWAYS;
6166 else if (*cptr == '+')
6167 *flags |= ENV_ACT_SETABSENT;
6168 else if (*cptr == '-')
6169 *flags |= ENV_ACT_REMOVE;
6170 else if (*cptr == '!')
6171 *flags |= ENV_ACT_REMOVEMATCH;
6172 else if (*cptr == '*')
6173 *flags |= ENV_MOD_MACHINE;
6174 else
6175 break;
6177 cptr++;
6178 (*name)++;
6181 if (!*cptr)
6183 ERR("Missing environment variable\n");
6184 return ERROR_FUNCTION_FAILED;
6187 if (*value)
6189 LPCWSTR ptr = *value;
6190 if (!strncmpW(ptr, prefix, prefix_len))
6192 if (ptr[prefix_len] == szSemiColon[0])
6194 *flags |= ENV_MOD_APPEND;
6195 *value += lstrlenW(prefix);
6197 else
6199 *value = NULL;
6202 else if (lstrlenW(*value) >= prefix_len)
6204 ptr += lstrlenW(ptr) - prefix_len;
6205 if (!lstrcmpW(ptr, prefix))
6207 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6209 *flags |= ENV_MOD_PREFIX;
6210 /* the "[~]" will be removed by deformat_string */;
6212 else
6214 *value = NULL;
6220 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6221 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6222 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6223 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6225 ERR("Invalid flags: %08x\n", *flags);
6226 return ERROR_FUNCTION_FAILED;
6229 if (!*flags)
6230 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6232 return ERROR_SUCCESS;
6235 static UINT open_env_key( DWORD flags, HKEY *key )
6237 static const WCHAR user_env[] =
6238 {'E','n','v','i','r','o','n','m','e','n','t',0};
6239 static const WCHAR machine_env[] =
6240 {'S','y','s','t','e','m','\\',
6241 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6242 'C','o','n','t','r','o','l','\\',
6243 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6244 'E','n','v','i','r','o','n','m','e','n','t',0};
6245 const WCHAR *env;
6246 HKEY root;
6247 LONG res;
6249 if (flags & ENV_MOD_MACHINE)
6251 env = machine_env;
6252 root = HKEY_LOCAL_MACHINE;
6254 else
6256 env = user_env;
6257 root = HKEY_CURRENT_USER;
6260 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6261 if (res != ERROR_SUCCESS)
6263 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6264 return ERROR_FUNCTION_FAILED;
6267 return ERROR_SUCCESS;
6270 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6272 MSIPACKAGE *package = param;
6273 LPCWSTR name, value, component;
6274 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6275 DWORD flags, type, size;
6276 UINT res;
6277 HKEY env = NULL;
6278 MSICOMPONENT *comp;
6279 MSIRECORD *uirow;
6280 int action = 0;
6282 component = MSI_RecordGetString(rec, 4);
6283 comp = get_loaded_component(package, component);
6284 if (!comp)
6285 return ERROR_SUCCESS;
6287 if (!comp->Enabled)
6289 TRACE("component is disabled\n");
6290 return ERROR_SUCCESS;
6293 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6295 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6296 comp->Action = comp->Installed;
6297 return ERROR_SUCCESS;
6299 comp->Action = INSTALLSTATE_LOCAL;
6301 name = MSI_RecordGetString(rec, 2);
6302 value = MSI_RecordGetString(rec, 3);
6304 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6306 res = env_parse_flags(&name, &value, &flags);
6307 if (res != ERROR_SUCCESS || !value)
6308 goto done;
6310 if (value && !deformat_string(package, value, &deformatted))
6312 res = ERROR_OUTOFMEMORY;
6313 goto done;
6316 value = deformatted;
6318 res = open_env_key( flags, &env );
6319 if (res != ERROR_SUCCESS)
6320 goto done;
6322 if (flags & ENV_MOD_MACHINE)
6323 action |= 0x20000000;
6325 size = 0;
6326 type = REG_SZ;
6327 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6328 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6329 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6330 goto done;
6332 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6334 action = 0x2;
6336 /* Nothing to do. */
6337 if (!value)
6339 res = ERROR_SUCCESS;
6340 goto done;
6343 /* If we are appending but the string was empty, strip ; */
6344 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6346 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6347 newval = strdupW(value);
6348 if (!newval)
6350 res = ERROR_OUTOFMEMORY;
6351 goto done;
6354 else
6356 action = 0x1;
6358 /* Contrary to MSDN, +-variable to [~];path works */
6359 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6361 res = ERROR_SUCCESS;
6362 goto done;
6365 data = msi_alloc(size);
6366 if (!data)
6368 RegCloseKey(env);
6369 return ERROR_OUTOFMEMORY;
6372 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6373 if (res != ERROR_SUCCESS)
6374 goto done;
6376 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
6378 action = 0x4;
6379 res = RegDeleteValueW(env, name);
6380 if (res != ERROR_SUCCESS)
6381 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6382 goto done;
6385 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6386 if (flags & ENV_MOD_MASK)
6388 DWORD mod_size;
6389 int multiplier = 0;
6390 if (flags & ENV_MOD_APPEND) multiplier++;
6391 if (flags & ENV_MOD_PREFIX) multiplier++;
6392 mod_size = lstrlenW(value) * multiplier;
6393 size += mod_size * sizeof(WCHAR);
6396 newval = msi_alloc(size);
6397 ptr = newval;
6398 if (!newval)
6400 res = ERROR_OUTOFMEMORY;
6401 goto done;
6404 if (flags & ENV_MOD_PREFIX)
6406 lstrcpyW(newval, value);
6407 ptr = newval + lstrlenW(value);
6408 action |= 0x80000000;
6411 lstrcpyW(ptr, data);
6413 if (flags & ENV_MOD_APPEND)
6415 lstrcatW(newval, value);
6416 action |= 0x40000000;
6419 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6420 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6421 if (res)
6423 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6426 done:
6427 uirow = MSI_CreateRecord( 3 );
6428 MSI_RecordSetStringW( uirow, 1, name );
6429 MSI_RecordSetStringW( uirow, 2, newval );
6430 MSI_RecordSetInteger( uirow, 3, action );
6431 ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6432 msiobj_release( &uirow->hdr );
6434 if (env) RegCloseKey(env);
6435 msi_free(deformatted);
6436 msi_free(data);
6437 msi_free(newval);
6438 return res;
6441 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6443 UINT rc;
6444 MSIQUERY * view;
6445 static const WCHAR ExecSeqQuery[] =
6446 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6447 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6448 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6449 if (rc != ERROR_SUCCESS)
6450 return ERROR_SUCCESS;
6452 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6453 msiobj_release(&view->hdr);
6455 return rc;
6458 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6460 MSIPACKAGE *package = param;
6461 LPCWSTR name, value, component;
6462 LPWSTR deformatted = NULL;
6463 DWORD flags;
6464 HKEY env;
6465 MSICOMPONENT *comp;
6466 MSIRECORD *uirow;
6467 int action = 0;
6468 LONG res;
6469 UINT r;
6471 component = MSI_RecordGetString( rec, 4 );
6472 comp = get_loaded_component( package, component );
6473 if (!comp)
6474 return ERROR_SUCCESS;
6476 if (!comp->Enabled)
6478 TRACE("component is disabled\n");
6479 return ERROR_SUCCESS;
6482 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6484 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6485 comp->Action = comp->Installed;
6486 return ERROR_SUCCESS;
6488 comp->Action = INSTALLSTATE_ABSENT;
6490 name = MSI_RecordGetString( rec, 2 );
6491 value = MSI_RecordGetString( rec, 3 );
6493 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6495 r = env_parse_flags( &name, &value, &flags );
6496 if (r != ERROR_SUCCESS)
6497 return r;
6499 if (!(flags & ENV_ACT_REMOVE))
6501 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6502 return ERROR_SUCCESS;
6505 if (value && !deformat_string( package, value, &deformatted ))
6506 return ERROR_OUTOFMEMORY;
6508 value = deformatted;
6510 r = open_env_key( flags, &env );
6511 if (r != ERROR_SUCCESS)
6513 r = ERROR_SUCCESS;
6514 goto done;
6517 if (flags & ENV_MOD_MACHINE)
6518 action |= 0x20000000;
6520 TRACE("Removing %s\n", debugstr_w(name));
6522 res = RegDeleteValueW( env, name );
6523 if (res != ERROR_SUCCESS)
6525 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6526 r = ERROR_SUCCESS;
6529 done:
6530 uirow = MSI_CreateRecord( 3 );
6531 MSI_RecordSetStringW( uirow, 1, name );
6532 MSI_RecordSetStringW( uirow, 2, value );
6533 MSI_RecordSetInteger( uirow, 3, action );
6534 ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6535 msiobj_release( &uirow->hdr );
6537 if (env) RegCloseKey( env );
6538 msi_free( deformatted );
6539 return r;
6542 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6544 UINT rc;
6545 MSIQUERY *view;
6546 static const WCHAR query[] =
6547 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6548 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6550 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6551 if (rc != ERROR_SUCCESS)
6552 return ERROR_SUCCESS;
6554 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6555 msiobj_release( &view->hdr );
6557 return rc;
6560 typedef struct tagMSIASSEMBLY
6562 struct list entry;
6563 MSICOMPONENT *component;
6564 MSIFEATURE *feature;
6565 MSIFILE *file;
6566 LPWSTR manifest;
6567 LPWSTR application;
6568 LPWSTR display_name;
6569 DWORD attributes;
6570 BOOL installed;
6571 } MSIASSEMBLY;
6573 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6574 DWORD dwReserved);
6575 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6576 LPVOID pvReserved, HMODULE *phModDll);
6578 static BOOL init_functionpointers(void)
6580 HRESULT hr;
6581 HMODULE hfusion;
6582 HMODULE hmscoree;
6584 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6586 hmscoree = LoadLibraryA("mscoree.dll");
6587 if (!hmscoree)
6589 WARN("mscoree.dll not available\n");
6590 return FALSE;
6593 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6594 if (!pLoadLibraryShim)
6596 WARN("LoadLibraryShim not available\n");
6597 FreeLibrary(hmscoree);
6598 return FALSE;
6601 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6602 if (FAILED(hr))
6604 WARN("fusion.dll not available\n");
6605 FreeLibrary(hmscoree);
6606 return FALSE;
6609 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6611 FreeLibrary(hmscoree);
6612 return TRUE;
6615 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6616 LPWSTR path)
6618 IAssemblyCache *cache;
6619 MSIRECORD *uirow;
6620 HRESULT hr;
6621 UINT r = ERROR_FUNCTION_FAILED;
6623 TRACE("installing assembly: %s\n", debugstr_w(path));
6625 uirow = MSI_CreateRecord( 2 );
6626 MSI_RecordSetStringW( uirow, 2, assembly->display_name );
6627 ui_actiondata( package, szMsiPublishAssemblies, uirow );
6628 msiobj_release( &uirow->hdr );
6630 if (assembly->feature)
6631 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6633 if (assembly->manifest)
6634 FIXME("Manifest unhandled\n");
6636 if (assembly->application)
6638 FIXME("Assembly should be privately installed\n");
6639 return ERROR_SUCCESS;
6642 if (assembly->attributes == msidbAssemblyAttributesWin32)
6644 FIXME("Win32 assemblies not handled\n");
6645 return ERROR_SUCCESS;
6648 hr = pCreateAssemblyCache(&cache, 0);
6649 if (FAILED(hr))
6650 goto done;
6652 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6653 if (FAILED(hr))
6654 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6656 r = ERROR_SUCCESS;
6658 done:
6659 IAssemblyCache_Release(cache);
6660 return r;
6663 typedef struct tagASSEMBLY_LIST
6665 MSIPACKAGE *package;
6666 IAssemblyCache *cache;
6667 struct list *assemblies;
6668 } ASSEMBLY_LIST;
6670 typedef struct tagASSEMBLY_NAME
6672 LPWSTR name;
6673 LPWSTR version;
6674 LPWSTR culture;
6675 LPWSTR pubkeytoken;
6676 } ASSEMBLY_NAME;
6678 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6680 ASSEMBLY_NAME *asmname = param;
6681 LPCWSTR name = MSI_RecordGetString(rec, 2);
6682 LPWSTR val = msi_dup_record_field(rec, 3);
6684 static const WCHAR Name[] = {'N','a','m','e',0};
6685 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6686 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6687 static const WCHAR PublicKeyToken[] = {
6688 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6690 if (!strcmpiW(name, Name))
6691 asmname->name = val;
6692 else if (!strcmpiW(name, Version))
6693 asmname->version = val;
6694 else if (!strcmpiW(name, Culture))
6695 asmname->culture = val;
6696 else if (!strcmpiW(name, PublicKeyToken))
6697 asmname->pubkeytoken = val;
6698 else
6699 msi_free(val);
6701 return ERROR_SUCCESS;
6704 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6706 if (!*str)
6708 *size = lstrlenW(append) + 1;
6709 *str = msi_alloc((*size) * sizeof(WCHAR));
6710 lstrcpyW(*str, append);
6711 return;
6714 (*size) += lstrlenW(append);
6715 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6716 lstrcatW(*str, append);
6719 static WCHAR *get_assembly_display_name( MSIDATABASE *db, MSICOMPONENT *comp )
6721 static const WCHAR separator[] = {',',' ',0};
6722 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6723 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6724 static const WCHAR PublicKeyToken[] = {'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6725 static const WCHAR query[] = {
6726 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6727 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6728 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6729 '=','\'','%','s','\'',0};
6730 ASSEMBLY_NAME name;
6731 MSIQUERY *view;
6732 LPWSTR display_name;
6733 DWORD size;
6734 UINT r;
6736 display_name = NULL;
6737 memset( &name, 0, sizeof(ASSEMBLY_NAME) );
6739 r = MSI_OpenQuery( db, &view, query, comp->Component );
6740 if (r != ERROR_SUCCESS)
6741 return NULL;
6743 MSI_IterateRecords( view, NULL, parse_assembly_name, &name );
6744 msiobj_release( &view->hdr );
6746 if (!name.name)
6748 ERR("No assembly name specified!\n");
6749 return NULL;
6752 append_str( &display_name, &size, name.name );
6754 if (name.version)
6756 append_str( &display_name, &size, separator );
6757 append_str( &display_name, &size, Version );
6758 append_str( &display_name, &size, name.version );
6760 if (name.culture)
6762 append_str( &display_name, &size, separator );
6763 append_str( &display_name, &size, Culture );
6764 append_str( &display_name, &size, name.culture );
6766 if (name.pubkeytoken)
6768 append_str( &display_name, &size, separator );
6769 append_str( &display_name, &size, PublicKeyToken );
6770 append_str( &display_name, &size, name.pubkeytoken );
6773 msi_free( name.name );
6774 msi_free( name.version );
6775 msi_free( name.culture );
6776 msi_free( name.pubkeytoken );
6778 return display_name;
6781 static BOOL check_assembly_installed( MSIDATABASE *db, IAssemblyCache *cache, MSICOMPONENT *comp )
6783 ASSEMBLY_INFO asminfo;
6784 LPWSTR disp;
6785 BOOL found = FALSE;
6786 HRESULT hr;
6788 disp = get_assembly_display_name( db, comp );
6789 if (!disp)
6790 return FALSE;
6792 memset( &asminfo, 0, sizeof(ASSEMBLY_INFO) );
6793 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6795 hr = IAssemblyCache_QueryAssemblyInfo( cache, QUERYASMINFO_FLAG_VALIDATE, disp, &asminfo );
6796 if (SUCCEEDED(hr))
6797 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6799 msi_free( disp );
6800 return found;
6803 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6805 ASSEMBLY_LIST *list = param;
6806 MSIASSEMBLY *assembly;
6807 LPCWSTR component;
6809 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6810 if (!assembly)
6811 return ERROR_OUTOFMEMORY;
6813 component = MSI_RecordGetString(rec, 1);
6814 assembly->component = get_loaded_component(list->package, component);
6815 if (!assembly->component)
6816 return ERROR_SUCCESS;
6818 if (assembly->component->ActionRequest != INSTALLSTATE_LOCAL &&
6819 assembly->component->ActionRequest != INSTALLSTATE_SOURCE)
6821 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6822 assembly->component->Action = assembly->component->Installed;
6823 return ERROR_SUCCESS;
6825 assembly->component->Action = assembly->component->ActionRequest;
6827 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6828 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6830 if (!assembly->file)
6832 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6833 return ERROR_FUNCTION_FAILED;
6836 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6837 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6838 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6840 if (assembly->application)
6842 WCHAR version[24];
6843 DWORD size = sizeof(version)/sizeof(WCHAR);
6845 /* FIXME: we should probably check the manifest file here */
6847 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6848 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6850 assembly->installed = TRUE;
6853 else
6854 assembly->installed = check_assembly_installed(list->package->db,
6855 list->cache,
6856 assembly->component);
6858 list_add_head(list->assemblies, &assembly->entry);
6859 return ERROR_SUCCESS;
6862 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6864 IAssemblyCache *cache = NULL;
6865 ASSEMBLY_LIST list;
6866 MSIQUERY *view;
6867 HRESULT hr;
6868 UINT r;
6870 static const WCHAR query[] =
6871 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6872 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6874 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6875 if (r != ERROR_SUCCESS)
6876 return ERROR_SUCCESS;
6878 hr = pCreateAssemblyCache(&cache, 0);
6879 if (FAILED(hr))
6880 return ERROR_FUNCTION_FAILED;
6882 list.package = package;
6883 list.cache = cache;
6884 list.assemblies = assemblies;
6886 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6887 msiobj_release(&view->hdr);
6889 IAssemblyCache_Release(cache);
6891 return r;
6894 static void free_assemblies(struct list *assemblies)
6896 struct list *item, *cursor;
6898 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6900 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6902 list_remove(&assembly->entry);
6903 msi_free(assembly->application);
6904 msi_free(assembly->manifest);
6905 msi_free(assembly->display_name);
6906 msi_free(assembly);
6910 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6912 MSIASSEMBLY *assembly;
6914 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6916 if (!lstrcmpW(assembly->file->File, file))
6918 *out = assembly;
6919 return TRUE;
6923 return FALSE;
6926 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6927 LPWSTR *path, DWORD *attrs, PVOID user)
6929 MSIASSEMBLY *assembly;
6930 WCHAR temppath[MAX_PATH];
6931 struct list *assemblies = user;
6932 UINT r;
6934 if (!find_assembly(assemblies, file, &assembly))
6935 return FALSE;
6937 GetTempPathW(MAX_PATH, temppath);
6938 PathAddBackslashW(temppath);
6939 lstrcatW(temppath, assembly->file->FileName);
6941 if (action == MSICABEXTRACT_BEGINEXTRACT)
6943 if (assembly->installed)
6944 return FALSE;
6946 *path = strdupW(temppath);
6947 *attrs = assembly->file->Attributes;
6949 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6951 assembly->installed = TRUE;
6953 r = install_assembly(package, assembly, temppath);
6954 if (r != ERROR_SUCCESS)
6955 ERR("Failed to install assembly\n");
6958 return TRUE;
6961 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6963 UINT r;
6964 struct list assemblies = LIST_INIT(assemblies);
6965 MSIASSEMBLY *assembly;
6966 MSIMEDIAINFO *mi;
6968 if (!init_functionpointers() || !pCreateAssemblyCache)
6969 return ERROR_FUNCTION_FAILED;
6971 r = load_assemblies(package, &assemblies);
6972 if (r != ERROR_SUCCESS)
6973 goto done;
6975 if (list_empty(&assemblies))
6976 goto done;
6978 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6979 if (!mi)
6981 r = ERROR_OUTOFMEMORY;
6982 goto done;
6985 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6987 if (assembly->installed && !mi->is_continuous)
6988 continue;
6990 if (assembly->file->IsCompressed)
6992 if (assembly->file->disk_id != mi->disk_id || mi->is_continuous)
6994 MSICABDATA data;
6996 r = ready_media(package, assembly->file, mi);
6997 if (r != ERROR_SUCCESS)
6999 ERR("Failed to ready media\n");
7000 break;
7003 data.mi = mi;
7004 data.package = package;
7005 data.cb = installassembly_cb;
7006 data.user = &assemblies;
7008 if (!msi_cabextract(package, mi, &data))
7010 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
7011 r = ERROR_FUNCTION_FAILED;
7012 break;
7016 else
7018 LPWSTR source = resolve_file_source(package, assembly->file);
7020 r = install_assembly(package, assembly, source);
7021 if (r != ERROR_SUCCESS)
7022 ERR("Failed to install assembly\n");
7024 msi_free(source);
7027 /* FIXME: write Installer assembly reg values */
7030 done:
7031 free_assemblies(&assemblies);
7032 return r;
7035 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7037 LPWSTR key, template, id;
7038 UINT r = ERROR_SUCCESS;
7040 id = msi_dup_property( package->db, szProductID );
7041 if (id)
7043 msi_free( id );
7044 return ERROR_SUCCESS;
7046 template = msi_dup_property( package->db, szPIDTemplate );
7047 key = msi_dup_property( package->db, szPIDKEY );
7049 if (key && template)
7051 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7052 r = msi_set_property( package->db, szProductID, key );
7054 msi_free( template );
7055 msi_free( key );
7056 return r;
7059 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7061 TRACE("\n");
7062 package->need_reboot = 1;
7063 return ERROR_SUCCESS;
7066 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7068 static const WCHAR szAvailableFreeReg[] =
7069 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7070 MSIRECORD *uirow;
7071 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7073 TRACE("%p %d kilobytes\n", package, space);
7075 uirow = MSI_CreateRecord( 1 );
7076 MSI_RecordSetInteger( uirow, 1, space );
7077 ui_actiondata( package, szAllocateRegistrySpace, uirow );
7078 msiobj_release( &uirow->hdr );
7080 return ERROR_SUCCESS;
7083 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7085 FIXME("%p\n", package);
7086 return ERROR_SUCCESS;
7089 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7091 FIXME("%p\n", package);
7092 return ERROR_SUCCESS;
7095 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7097 UINT r, count;
7098 MSIQUERY *view;
7100 static const WCHAR driver_query[] = {
7101 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7102 'O','D','B','C','D','r','i','v','e','r',0 };
7104 static const WCHAR translator_query[] = {
7105 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7106 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7108 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7109 if (r == ERROR_SUCCESS)
7111 count = 0;
7112 r = MSI_IterateRecords( view, &count, NULL, package );
7113 msiobj_release( &view->hdr );
7114 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7117 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7118 if (r == ERROR_SUCCESS)
7120 count = 0;
7121 r = MSI_IterateRecords( view, &count, NULL, package );
7122 msiobj_release( &view->hdr );
7123 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7126 return ERROR_SUCCESS;
7129 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7130 LPCSTR action, LPCWSTR table )
7132 static const WCHAR query[] = {
7133 'S','E','L','E','C','T',' ','*',' ',
7134 'F','R','O','M',' ','`','%','s','`',0 };
7135 MSIQUERY *view = NULL;
7136 DWORD count = 0;
7137 UINT r;
7139 r = MSI_OpenQuery( package->db, &view, query, table );
7140 if (r == ERROR_SUCCESS)
7142 r = MSI_IterateRecords(view, &count, NULL, package);
7143 msiobj_release(&view->hdr);
7146 if (count)
7147 FIXME("%s -> %u ignored %s table values\n",
7148 action, count, debugstr_w(table));
7150 return ERROR_SUCCESS;
7153 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7155 static const WCHAR table[] = { 'P','a','t','c','h',0 };
7156 return msi_unimplemented_action_stub( package, "PatchFiles", table );
7159 static UINT ACTION_BindImage( MSIPACKAGE *package )
7161 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7162 return msi_unimplemented_action_stub( package, "BindImage", table );
7165 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7167 static const WCHAR table[] = {
7168 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7169 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7172 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7174 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7175 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
7178 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
7180 static const WCHAR table[] = {
7181 'M','s','i','A','s','s','e','m','b','l','y',0 };
7182 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
7185 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7187 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7188 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7191 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7193 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7194 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7197 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7199 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7200 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7203 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7205 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7206 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7209 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7211 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7212 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
7215 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7217 static const struct
7219 const WCHAR *action;
7220 UINT (*handler)(MSIPACKAGE *);
7222 StandardActions[] =
7224 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7225 { szAppSearch, ACTION_AppSearch },
7226 { szBindImage, ACTION_BindImage },
7227 { szCCPSearch, ACTION_CCPSearch },
7228 { szCostFinalize, ACTION_CostFinalize },
7229 { szCostInitialize, ACTION_CostInitialize },
7230 { szCreateFolders, ACTION_CreateFolders },
7231 { szCreateShortcuts, ACTION_CreateShortcuts },
7232 { szDeleteServices, ACTION_DeleteServices },
7233 { szDisableRollback, ACTION_DisableRollback },
7234 { szDuplicateFiles, ACTION_DuplicateFiles },
7235 { szExecuteAction, ACTION_ExecuteAction },
7236 { szFileCost, ACTION_FileCost },
7237 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7238 { szForceReboot, ACTION_ForceReboot },
7239 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7240 { szInstallExecute, ACTION_InstallExecute },
7241 { szInstallExecuteAgain, ACTION_InstallExecute },
7242 { szInstallFiles, ACTION_InstallFiles},
7243 { szInstallFinalize, ACTION_InstallFinalize },
7244 { szInstallInitialize, ACTION_InstallInitialize },
7245 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7246 { szInstallValidate, ACTION_InstallValidate },
7247 { szIsolateComponents, ACTION_IsolateComponents },
7248 { szLaunchConditions, ACTION_LaunchConditions },
7249 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7250 { szMoveFiles, ACTION_MoveFiles },
7251 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7252 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7253 { szInstallODBC, ACTION_InstallODBC },
7254 { szInstallServices, ACTION_InstallServices },
7255 { szPatchFiles, ACTION_PatchFiles },
7256 { szProcessComponents, ACTION_ProcessComponents },
7257 { szPublishComponents, ACTION_PublishComponents },
7258 { szPublishFeatures, ACTION_PublishFeatures },
7259 { szPublishProduct, ACTION_PublishProduct },
7260 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7261 { szRegisterComPlus, ACTION_RegisterComPlus},
7262 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7263 { szRegisterFonts, ACTION_RegisterFonts },
7264 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7265 { szRegisterProduct, ACTION_RegisterProduct },
7266 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7267 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7268 { szRegisterUser, ACTION_RegisterUser },
7269 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7270 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7271 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7272 { szRemoveFiles, ACTION_RemoveFiles },
7273 { szRemoveFolders, ACTION_RemoveFolders },
7274 { szRemoveIniValues, ACTION_RemoveIniValues },
7275 { szRemoveODBC, ACTION_RemoveODBC },
7276 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7277 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7278 { szResolveSource, ACTION_ResolveSource },
7279 { szRMCCPSearch, ACTION_RMCCPSearch },
7280 { szScheduleReboot, ACTION_ScheduleReboot },
7281 { szSelfRegModules, ACTION_SelfRegModules },
7282 { szSelfUnregModules, ACTION_SelfUnregModules },
7283 { szSetODBCFolders, ACTION_SetODBCFolders },
7284 { szStartServices, ACTION_StartServices },
7285 { szStopServices, ACTION_StopServices },
7286 { szUnpublishComponents, ACTION_UnpublishComponents },
7287 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7288 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7289 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7290 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7291 { szUnregisterFonts, ACTION_UnregisterFonts },
7292 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7293 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7294 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7295 { szValidateProductID, ACTION_ValidateProductID },
7296 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7297 { szWriteIniValues, ACTION_WriteIniValues },
7298 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7299 { NULL, NULL },
7302 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7304 BOOL ret = FALSE;
7305 UINT i;
7307 i = 0;
7308 while (StandardActions[i].action != NULL)
7310 if (!strcmpW( StandardActions[i].action, action ))
7312 ui_actionstart( package, action );
7313 if (StandardActions[i].handler)
7315 ui_actioninfo( package, action, TRUE, 0 );
7316 *rc = StandardActions[i].handler( package );
7317 ui_actioninfo( package, action, FALSE, *rc );
7319 else
7321 FIXME("unhandled standard action %s\n", debugstr_w(action));
7322 *rc = ERROR_SUCCESS;
7324 ret = TRUE;
7325 break;
7327 i++;
7329 return ret;
7332 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7334 UINT rc = ERROR_SUCCESS;
7335 BOOL handled;
7337 TRACE("Performing action (%s)\n", debugstr_w(action));
7339 handled = ACTION_HandleStandardAction(package, action, &rc);
7341 if (!handled)
7342 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7344 if (!handled)
7346 WARN("unhandled msi action %s\n", debugstr_w(action));
7347 rc = ERROR_FUNCTION_NOT_CALLED;
7350 return rc;
7353 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7355 UINT rc = ERROR_SUCCESS;
7356 BOOL handled = FALSE;
7358 TRACE("Performing action (%s)\n", debugstr_w(action));
7360 handled = ACTION_HandleStandardAction(package, action, &rc);
7362 if (!handled)
7363 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7365 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7366 handled = TRUE;
7368 if (!handled)
7370 WARN("unhandled msi action %s\n", debugstr_w(action));
7371 rc = ERROR_FUNCTION_NOT_CALLED;
7374 return rc;
7377 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7379 UINT rc = ERROR_SUCCESS;
7380 MSIRECORD *row;
7382 static const WCHAR ExecSeqQuery[] =
7383 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7384 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7385 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7386 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7387 static const WCHAR UISeqQuery[] =
7388 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7389 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7390 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7391 ' ', '=',' ','%','i',0};
7393 if (needs_ui_sequence(package))
7394 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7395 else
7396 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7398 if (row)
7400 LPCWSTR action, cond;
7402 TRACE("Running the actions\n");
7404 /* check conditions */
7405 cond = MSI_RecordGetString(row, 2);
7407 /* this is a hack to skip errors in the condition code */
7408 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7410 msiobj_release(&row->hdr);
7411 return ERROR_SUCCESS;
7414 action = MSI_RecordGetString(row, 1);
7415 if (!action)
7417 ERR("failed to fetch action\n");
7418 msiobj_release(&row->hdr);
7419 return ERROR_FUNCTION_FAILED;
7422 if (needs_ui_sequence(package))
7423 rc = ACTION_PerformUIAction(package, action, -1);
7424 else
7425 rc = ACTION_PerformAction(package, action, -1);
7427 msiobj_release(&row->hdr);
7430 return rc;
7433 /****************************************************
7434 * TOP level entry points
7435 *****************************************************/
7437 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7438 LPCWSTR szCommandLine )
7440 UINT rc;
7441 BOOL ui_exists;
7443 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7444 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7446 msi_set_property( package->db, szAction, szInstall );
7448 package->script->InWhatSequence = SEQUENCE_INSTALL;
7450 if (szPackagePath)
7452 LPWSTR p, dir;
7453 LPCWSTR file;
7455 dir = strdupW(szPackagePath);
7456 p = strrchrW(dir, '\\');
7457 if (p)
7459 *(++p) = 0;
7460 file = szPackagePath + (p - dir);
7462 else
7464 msi_free(dir);
7465 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7466 GetCurrentDirectoryW(MAX_PATH, dir);
7467 lstrcatW(dir, szBackSlash);
7468 file = szPackagePath;
7471 msi_free( package->PackagePath );
7472 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7473 if (!package->PackagePath)
7475 msi_free(dir);
7476 return ERROR_OUTOFMEMORY;
7479 lstrcpyW(package->PackagePath, dir);
7480 lstrcatW(package->PackagePath, file);
7481 msi_free(dir);
7483 msi_set_sourcedir_props(package, FALSE);
7486 msi_parse_command_line( package, szCommandLine, FALSE );
7488 msi_apply_transforms( package );
7489 msi_apply_patches( package );
7491 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7493 TRACE("setting reinstall property\n");
7494 msi_set_property( package->db, szReinstall, szAll );
7497 /* properties may have been added by a transform */
7498 msi_clone_properties( package );
7500 msi_parse_command_line( package, szCommandLine, FALSE );
7501 msi_adjust_allusers_property( package );
7502 msi_set_context( package );
7504 if (needs_ui_sequence( package))
7506 package->script->InWhatSequence |= SEQUENCE_UI;
7507 rc = ACTION_ProcessUISequence(package);
7508 ui_exists = ui_sequence_exists(package);
7509 if (rc == ERROR_SUCCESS || !ui_exists)
7511 package->script->InWhatSequence |= SEQUENCE_EXEC;
7512 rc = ACTION_ProcessExecSequence(package, ui_exists);
7515 else
7516 rc = ACTION_ProcessExecSequence(package, FALSE);
7518 package->script->CurrentlyScripting = FALSE;
7520 /* process the ending type action */
7521 if (rc == ERROR_SUCCESS)
7522 ACTION_PerformActionSequence(package, -1);
7523 else if (rc == ERROR_INSTALL_USEREXIT)
7524 ACTION_PerformActionSequence(package, -2);
7525 else if (rc == ERROR_INSTALL_SUSPEND)
7526 ACTION_PerformActionSequence(package, -4);
7527 else /* failed */
7528 ACTION_PerformActionSequence(package, -3);
7530 /* finish up running custom actions */
7531 ACTION_FinishCustomActions(package);
7533 if (rc == ERROR_SUCCESS && package->need_reboot)
7534 return ERROR_SUCCESS_REBOOT_REQUIRED;
7536 return rc;