mfmediaengine: Remove unnecessary import library.
[wine.git] / dlls / msi / action.c
blob8ee9420af55218b02beecc39880397c8df7171b6
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 "winuser.h"
34 #include "shlobj.h"
35 #include "objbase.h"
36 #include "mscoree.h"
37 #include "shlwapi.h"
38 #include "imagehlp.h"
39 #include "winver.h"
41 #include "msipriv.h"
42 #include "resource.h"
44 #define REG_PROGRESS_VALUE 13200
45 #define COMPONENT_PROGRESS_VALUE 24000
47 WINE_DEFAULT_DEBUG_CHANNEL(msi);
49 static INT ui_actionstart(MSIPACKAGE *package, LPCWSTR action, LPCWSTR description, LPCWSTR template)
51 MSIRECORD *row, *textrow;
52 INT rc;
54 textrow = MSI_QueryGetRecord(package->db, L"SELECT * FROM `ActionText` WHERE `Action` = '%s'", action);
55 if (textrow)
57 description = MSI_RecordGetString(textrow, 2);
58 template = MSI_RecordGetString(textrow, 3);
61 row = MSI_CreateRecord(3);
62 if (!row) return -1;
63 MSI_RecordSetStringW(row, 1, action);
64 MSI_RecordSetStringW(row, 2, description);
65 MSI_RecordSetStringW(row, 3, template);
66 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
67 if (textrow) msiobj_release(&textrow->hdr);
68 msiobj_release(&row->hdr);
69 return rc;
72 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
73 INT rc)
75 MSIRECORD *row;
76 WCHAR *template;
78 template = msi_get_error_message(package->db, start ? MSIERR_INFO_ACTIONSTART : MSIERR_INFO_ACTIONENDED);
80 row = MSI_CreateRecord(2);
81 if (!row)
83 msi_free(template);
84 return;
86 MSI_RecordSetStringW(row, 0, template);
87 MSI_RecordSetStringW(row, 1, action);
88 MSI_RecordSetInteger(row, 2, start ? package->LastActionResult : rc);
89 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
90 msiobj_release(&row->hdr);
91 msi_free(template);
92 if (!start) package->LastActionResult = rc;
95 enum parse_state
97 state_whitespace,
98 state_token,
99 state_quote
102 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
104 enum parse_state state = state_quote;
105 const WCHAR *p;
106 WCHAR *out = value;
107 BOOL ignore, in_quotes = FALSE;
108 int count = 0, len = 0;
110 for (p = str; *p; p++)
112 ignore = FALSE;
113 switch (state)
115 case state_whitespace:
116 switch (*p)
118 case ' ':
119 in_quotes = TRUE;
120 ignore = TRUE;
121 len++;
122 break;
123 case '"':
124 state = state_quote;
125 if (in_quotes && p[1] != '\"') count--;
126 else count++;
127 break;
128 default:
129 state = state_token;
130 in_quotes = TRUE;
131 len++;
132 break;
134 break;
136 case state_token:
137 switch (*p)
139 case '"':
140 state = state_quote;
141 if (in_quotes) count--;
142 else count++;
143 break;
144 case ' ':
145 state = state_whitespace;
146 if (!count) goto done;
147 in_quotes = TRUE;
148 len++;
149 break;
150 default:
151 if (count) in_quotes = TRUE;
152 len++;
153 break;
155 break;
157 case state_quote:
158 switch (*p)
160 case '"':
161 if (in_quotes && p[1] != '\"') count--;
162 else count++;
163 break;
164 case ' ':
165 state = state_whitespace;
166 if (!count || (count > 1 && !len)) goto done;
167 in_quotes = TRUE;
168 len++;
169 break;
170 default:
171 state = state_token;
172 if (count) in_quotes = TRUE;
173 len++;
174 break;
176 break;
178 default: break;
180 if (!ignore && value) *out++ = *p;
181 if (!count) in_quotes = FALSE;
184 done:
185 if (value)
187 if (!len) *value = 0;
188 else *out = 0;
191 if(quotes) *quotes = count;
192 return p - str;
195 static void remove_quotes( WCHAR *str )
197 WCHAR *p = str;
198 int len = lstrlenW( str );
200 while ((p = wcschr( p, '"' )))
202 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
203 p++;
207 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
208 BOOL preserve_case )
210 LPCWSTR ptr, ptr2;
211 int num_quotes;
212 DWORD len;
213 WCHAR *prop, *val;
214 UINT r;
216 if (!szCommandLine)
217 return ERROR_SUCCESS;
219 ptr = szCommandLine;
220 while (*ptr)
222 while (*ptr == ' ') ptr++;
223 if (!*ptr) break;
225 ptr2 = wcschr( ptr, '=' );
226 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
228 len = ptr2 - ptr;
229 if (!len) return ERROR_INVALID_COMMAND_LINE;
231 while (ptr[len - 1] == ' ') len--;
233 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
234 memcpy( prop, ptr, len * sizeof(WCHAR) );
235 prop[len] = 0;
236 if (!preserve_case) wcsupr( prop );
238 ptr2++;
239 while (*ptr2 == ' ') ptr2++;
241 num_quotes = 0;
242 val = msi_alloc( (lstrlenW( ptr2 ) + 1) * sizeof(WCHAR) );
243 len = parse_prop( ptr2, val, &num_quotes );
244 if (num_quotes % 2)
246 WARN("unbalanced quotes\n");
247 msi_free( val );
248 msi_free( prop );
249 return ERROR_INVALID_COMMAND_LINE;
251 remove_quotes( val );
252 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
254 r = msi_set_property( package->db, prop, val, -1 );
255 if (r == ERROR_SUCCESS && !wcscmp( prop, L"SourceDir" ))
256 msi_reset_source_folders( package );
258 msi_free( val );
259 msi_free( prop );
261 ptr = ptr2 + len;
264 return ERROR_SUCCESS;
267 const WCHAR *msi_get_command_line_option(const WCHAR *cmd, const WCHAR *option, UINT *len)
269 DWORD opt_len = lstrlenW(option);
271 if (!cmd)
272 return NULL;
274 while (*cmd)
276 BOOL found = FALSE;
278 while (*cmd == ' ') cmd++;
279 if (!*cmd) break;
281 if(!wcsnicmp(cmd, option, opt_len))
282 found = TRUE;
284 cmd = wcschr( cmd, '=' );
285 if(!cmd) break;
286 cmd++;
287 while (*cmd == ' ') cmd++;
288 if (!*cmd) break;
290 *len = parse_prop( cmd, NULL, NULL);
291 if (found) return cmd;
292 cmd += *len;
295 return NULL;
298 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
300 LPCWSTR pc;
301 LPWSTR p, *ret = NULL;
302 UINT count = 0;
304 if (!str)
305 return ret;
307 /* count the number of substrings */
308 for ( pc = str, count = 0; pc; count++ )
310 pc = wcschr( pc, sep );
311 if (pc)
312 pc++;
315 /* allocate space for an array of substring pointers and the substrings */
316 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
317 (lstrlenW(str)+1) * sizeof(WCHAR) );
318 if (!ret)
319 return ret;
321 /* copy the string and set the pointers */
322 p = (LPWSTR) &ret[count+1];
323 lstrcpyW( p, str );
324 for( count = 0; (ret[count] = p); count++ )
326 p = wcschr( p, sep );
327 if (p)
328 *p++ = 0;
331 return ret;
334 static BOOL ui_sequence_exists( MSIPACKAGE *package )
336 MSIQUERY *view;
337 DWORD count = 0;
339 if (!(MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `InstallUISequence` WHERE `Sequence` > 0", &view )))
341 MSI_IterateRecords( view, &count, NULL, package );
342 msiobj_release( &view->hdr );
344 return count != 0;
347 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
349 WCHAR *source, *check, *p, *db;
350 DWORD len;
352 if (!(db = msi_dup_property( package->db, L"OriginalDatabase" )))
353 return ERROR_OUTOFMEMORY;
355 if (!(p = wcsrchr( db, '\\' )) && !(p = wcsrchr( db, '/' )))
357 msi_free(db);
358 return ERROR_SUCCESS;
360 len = p - db + 2;
361 source = msi_alloc( len * sizeof(WCHAR) );
362 lstrcpynW( source, db, len );
363 msi_free( db );
365 check = msi_dup_property( package->db, L"SourceDir" );
366 if (!check || replace)
368 UINT r = msi_set_property( package->db, L"SourceDir", source, -1 );
369 if (r == ERROR_SUCCESS)
370 msi_reset_source_folders( package );
372 msi_free( check );
374 check = msi_dup_property( package->db, L"SOURCEDIR" );
375 if (!check || replace)
376 msi_set_property( package->db, L"SOURCEDIR", source, -1 );
378 msi_free( check );
379 msi_free( source );
381 return ERROR_SUCCESS;
384 static BOOL needs_ui_sequence(MSIPACKAGE *package)
386 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
389 UINT msi_set_context(MSIPACKAGE *package)
391 UINT r = msi_locate_product( package->ProductCode, &package->Context );
392 if (r != ERROR_SUCCESS)
394 int num = msi_get_property_int( package->db, L"ALLUSERS", 0 );
395 if (num == 1 || num == 2)
396 package->Context = MSIINSTALLCONTEXT_MACHINE;
397 else
398 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
400 return ERROR_SUCCESS;
403 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
405 UINT rc;
406 LPCWSTR cond, action;
407 MSIPACKAGE *package = param;
409 action = MSI_RecordGetString(row,1);
410 if (!action)
412 ERR("Error is retrieving action name\n");
413 return ERROR_FUNCTION_FAILED;
416 /* check conditions */
417 cond = MSI_RecordGetString(row,2);
419 /* this is a hack to skip errors in the condition code */
420 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
422 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
423 return ERROR_SUCCESS;
426 rc = ACTION_PerformAction(package, action);
428 msi_dialog_check_messages( NULL );
430 if (rc == ERROR_FUNCTION_NOT_CALLED)
431 rc = ERROR_SUCCESS;
433 if (rc != ERROR_SUCCESS)
434 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
436 if (package->need_reboot_now)
438 TRACE("action %s asked for immediate reboot, suspending installation\n",
439 debugstr_w(action));
440 rc = ACTION_ForceReboot( package );
442 return rc;
445 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
447 MSIQUERY *view;
448 UINT r;
450 TRACE("%p %s\n", package, debugstr_w(table));
452 r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `%s` WHERE `Sequence` > 0 ORDER BY `Sequence`", table );
453 if (r == ERROR_SUCCESS)
455 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
456 msiobj_release(&view->hdr);
458 return r;
461 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package)
463 MSIQUERY *view;
464 UINT rc;
466 if (package->ExecuteSequenceRun)
468 TRACE("Execute Sequence already Run\n");
469 return ERROR_SUCCESS;
472 package->ExecuteSequenceRun = TRUE;
474 rc = MSI_OpenQuery(package->db, &view,
475 L"SELECT * FROM `InstallExecuteSequence` WHERE `Sequence` > 0 ORDER BY `Sequence`");
476 if (rc == ERROR_SUCCESS)
478 TRACE("Running the actions\n");
480 msi_set_property( package->db, L"SourceDir", NULL, -1 );
481 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
482 msiobj_release(&view->hdr);
484 return rc;
487 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
489 MSIQUERY *view;
490 UINT rc;
492 rc = MSI_DatabaseOpenViewW(package->db,
493 L"SELECT * FROM `InstallUISequence` WHERE `Sequence` > 0 ORDER BY `Sequence`",
494 &view);
495 if (rc == ERROR_SUCCESS)
497 TRACE("Running the actions\n");
498 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
499 msiobj_release(&view->hdr);
501 return rc;
504 /********************************************************
505 * ACTION helper functions and functions that perform the actions
506 *******************************************************/
507 static UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action)
509 UINT arc;
510 INT uirc;
512 uirc = ui_actionstart(package, action, NULL, NULL);
513 if (uirc == IDCANCEL)
514 return ERROR_INSTALL_USEREXIT;
515 ui_actioninfo(package, action, TRUE, 0);
516 arc = ACTION_CustomAction(package, action);
517 uirc = !arc;
519 if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package))
521 uirc = ACTION_ShowDialog(package, action);
522 switch (uirc)
524 case -1:
525 return ERROR_SUCCESS; /* stop immediately */
526 case 0: arc = ERROR_FUNCTION_NOT_CALLED; break;
527 case 1: arc = ERROR_SUCCESS; break;
528 case 2: arc = ERROR_INSTALL_USEREXIT; break;
529 case 3: arc = ERROR_INSTALL_FAILURE; break;
530 case 4: arc = ERROR_INSTALL_SUSPEND; break;
531 case 5: arc = ERROR_MORE_DATA; break;
532 case 6: arc = ERROR_INVALID_HANDLE_STATE; break;
533 case 7: arc = ERROR_INVALID_DATA; break;
534 case 8: arc = ERROR_INSTALL_ALREADY_RUNNING; break;
535 case 9: arc = ERROR_INSTALL_PACKAGE_REJECTED; break;
536 default: arc = ERROR_FUNCTION_FAILED; break;
540 ui_actioninfo(package, action, FALSE, uirc);
542 return arc;
545 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
547 MSICOMPONENT *comp;
549 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
551 if (!wcscmp( Component, comp->Component )) return comp;
553 return NULL;
556 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
558 MSIFEATURE *feature;
560 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
562 if (!wcscmp( Feature, feature->Feature )) return feature;
564 return NULL;
567 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
569 MSIFILE *file;
571 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
573 if (!wcscmp( key, file->File )) return file;
575 return NULL;
578 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
580 MSIFOLDER *folder;
582 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
584 if (!wcscmp( dir, folder->Directory )) return folder;
586 return NULL;
589 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
591 MSIRECORD *row;
593 row = MSI_CreateRecord( 4 );
594 MSI_RecordSetInteger( row, 1, a );
595 MSI_RecordSetInteger( row, 2, b );
596 MSI_RecordSetInteger( row, 3, c );
597 MSI_RecordSetInteger( row, 4, d );
598 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
599 msiobj_release( &row->hdr );
601 msi_dialog_check_messages( NULL );
604 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
606 if (!comp->Enabled)
608 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
609 return INSTALLSTATE_UNKNOWN;
611 if (package->need_rollback) return comp->Installed;
612 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
614 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
615 return INSTALLSTATE_UNKNOWN;
617 return comp->ActionRequest;
620 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
622 if (package->need_rollback) return feature->Installed;
623 return feature->ActionRequest;
626 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
628 MSIPACKAGE *package = param;
629 LPCWSTR dir, component, full_path;
630 MSIRECORD *uirow;
631 MSIFOLDER *folder;
632 MSICOMPONENT *comp;
634 component = MSI_RecordGetString(row, 2);
635 if (!component)
636 return ERROR_SUCCESS;
638 comp = msi_get_loaded_component(package, component);
639 if (!comp)
640 return ERROR_SUCCESS;
642 comp->Action = msi_get_component_action( package, comp );
643 if (comp->Action != INSTALLSTATE_LOCAL)
645 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
646 return ERROR_SUCCESS;
649 dir = MSI_RecordGetString(row,1);
650 if (!dir)
652 ERR("Unable to get folder id\n");
653 return ERROR_SUCCESS;
656 uirow = MSI_CreateRecord(1);
657 MSI_RecordSetStringW(uirow, 1, dir);
658 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
659 msiobj_release(&uirow->hdr);
661 full_path = msi_get_target_folder( package, dir );
662 if (!full_path)
664 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
665 return ERROR_SUCCESS;
667 TRACE("folder is %s\n", debugstr_w(full_path));
669 folder = msi_get_loaded_folder( package, dir );
670 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( package, full_path );
671 folder->State = FOLDER_STATE_CREATED;
673 return ERROR_SUCCESS;
676 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
678 MSIQUERY *view;
679 UINT rc;
681 if (package->script == SCRIPT_NONE)
682 return msi_schedule_action(package, SCRIPT_INSTALL, L"CreateFolders");
684 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `CreateFolder`", &view );
685 if (rc != ERROR_SUCCESS)
686 return ERROR_SUCCESS;
688 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
689 msiobj_release(&view->hdr);
690 return rc;
693 static void remove_persistent_folder( MSIFOLDER *folder )
695 FolderList *fl;
697 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
699 remove_persistent_folder( fl->folder );
701 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
703 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
707 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
709 MSIPACKAGE *package = param;
710 LPCWSTR dir, component, full_path;
711 MSIRECORD *uirow;
712 MSIFOLDER *folder;
713 MSICOMPONENT *comp;
715 component = MSI_RecordGetString(row, 2);
716 if (!component)
717 return ERROR_SUCCESS;
719 comp = msi_get_loaded_component(package, component);
720 if (!comp)
721 return ERROR_SUCCESS;
723 comp->Action = msi_get_component_action( package, comp );
724 if (comp->Action != INSTALLSTATE_ABSENT)
726 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
727 return ERROR_SUCCESS;
730 dir = MSI_RecordGetString( row, 1 );
731 if (!dir)
733 ERR("Unable to get folder id\n");
734 return ERROR_SUCCESS;
737 full_path = msi_get_target_folder( package, dir );
738 if (!full_path)
740 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
741 return ERROR_SUCCESS;
743 TRACE("folder is %s\n", debugstr_w(full_path));
745 uirow = MSI_CreateRecord( 1 );
746 MSI_RecordSetStringW( uirow, 1, dir );
747 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
748 msiobj_release( &uirow->hdr );
750 folder = msi_get_loaded_folder( package, dir );
751 remove_persistent_folder( folder );
752 return ERROR_SUCCESS;
755 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
757 MSIQUERY *view;
758 UINT rc;
760 if (package->script == SCRIPT_NONE)
761 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveFolders");
763 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `CreateFolder`", &view );
764 if (rc != ERROR_SUCCESS)
765 return ERROR_SUCCESS;
767 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
768 msiobj_release( &view->hdr );
769 return rc;
772 static UINT load_component( MSIRECORD *row, LPVOID param )
774 MSIPACKAGE *package = param;
775 MSICOMPONENT *comp;
777 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
778 if (!comp)
779 return ERROR_FUNCTION_FAILED;
781 list_add_tail( &package->components, &comp->entry );
783 /* fill in the data */
784 comp->Component = msi_dup_record_field( row, 1 );
786 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
788 comp->ComponentId = msi_dup_record_field( row, 2 );
789 comp->Directory = msi_dup_record_field( row, 3 );
790 comp->Attributes = MSI_RecordGetInteger(row,4);
791 comp->Condition = msi_dup_record_field( row, 5 );
792 comp->KeyPath = msi_dup_record_field( row, 6 );
794 comp->Installed = INSTALLSTATE_UNKNOWN;
795 comp->Action = INSTALLSTATE_UNKNOWN;
796 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
798 comp->assembly = msi_load_assembly( package, comp );
799 return ERROR_SUCCESS;
802 UINT msi_load_all_components( MSIPACKAGE *package )
804 MSIQUERY *view;
805 UINT r;
807 if (!list_empty(&package->components))
808 return ERROR_SUCCESS;
810 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Component`", &view );
811 if (r != ERROR_SUCCESS)
812 return r;
814 r = MSI_IterateRecords(view, NULL, load_component, package);
815 msiobj_release(&view->hdr);
816 return r;
819 typedef struct {
820 MSIPACKAGE *package;
821 MSIFEATURE *feature;
822 } _ilfs;
824 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
826 ComponentList *cl;
828 cl = msi_alloc( sizeof (*cl) );
829 if ( !cl )
830 return ERROR_NOT_ENOUGH_MEMORY;
831 cl->component = comp;
832 list_add_tail( &feature->Components, &cl->entry );
834 return ERROR_SUCCESS;
837 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
839 FeatureList *fl;
841 fl = msi_alloc( sizeof(*fl) );
842 if ( !fl )
843 return ERROR_NOT_ENOUGH_MEMORY;
844 fl->feature = child;
845 list_add_tail( &parent->Children, &fl->entry );
847 return ERROR_SUCCESS;
850 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
852 _ilfs* ilfs = param;
853 LPCWSTR component;
854 MSICOMPONENT *comp;
856 component = MSI_RecordGetString(row,1);
858 /* check to see if the component is already loaded */
859 comp = msi_get_loaded_component( ilfs->package, component );
860 if (!comp)
862 WARN("ignoring unknown component %s\n", debugstr_w(component));
863 return ERROR_SUCCESS;
865 add_feature_component( ilfs->feature, comp );
866 comp->Enabled = TRUE;
868 return ERROR_SUCCESS;
871 static UINT load_feature(MSIRECORD *row, LPVOID param)
873 MSIPACKAGE *package = param;
874 MSIFEATURE *feature;
875 MSIQUERY *view;
876 _ilfs ilfs;
877 UINT rc;
879 /* fill in the data */
881 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
882 if (!feature)
883 return ERROR_NOT_ENOUGH_MEMORY;
885 list_init( &feature->Children );
886 list_init( &feature->Components );
888 feature->Feature = msi_dup_record_field( row, 1 );
890 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
892 feature->Feature_Parent = msi_dup_record_field( row, 2 );
893 feature->Title = msi_dup_record_field( row, 3 );
894 feature->Description = msi_dup_record_field( row, 4 );
896 if (!MSI_RecordIsNull(row,5))
897 feature->Display = MSI_RecordGetInteger(row,5);
899 feature->Level= MSI_RecordGetInteger(row,6);
900 feature->Directory = msi_dup_record_field( row, 7 );
901 feature->Attributes = MSI_RecordGetInteger(row,8);
903 feature->Installed = INSTALLSTATE_UNKNOWN;
904 feature->Action = INSTALLSTATE_UNKNOWN;
905 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
907 list_add_tail( &package->features, &feature->entry );
909 /* load feature components */
911 rc = MSI_OpenQuery( package->db, &view, L"SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = '%s'",
912 feature->Feature );
913 if (rc != ERROR_SUCCESS)
914 return ERROR_SUCCESS;
916 ilfs.package = package;
917 ilfs.feature = feature;
919 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
920 msiobj_release(&view->hdr);
921 return rc;
924 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
926 MSIPACKAGE *package = param;
927 MSIFEATURE *parent, *child;
929 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
930 if (!child)
931 return ERROR_FUNCTION_FAILED;
933 if (!child->Feature_Parent)
934 return ERROR_SUCCESS;
936 parent = msi_get_loaded_feature( package, child->Feature_Parent );
937 if (!parent)
938 return ERROR_FUNCTION_FAILED;
940 add_feature_child( parent, child );
941 return ERROR_SUCCESS;
944 UINT msi_load_all_features( MSIPACKAGE *package )
946 MSIQUERY *view;
947 UINT r;
949 if (!list_empty(&package->features))
950 return ERROR_SUCCESS;
952 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Feature` ORDER BY `Display`", &view );
953 if (r != ERROR_SUCCESS)
954 return r;
956 r = MSI_IterateRecords( view, NULL, load_feature, package );
957 if (r != ERROR_SUCCESS)
959 msiobj_release( &view->hdr );
960 return r;
962 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
963 msiobj_release( &view->hdr );
964 return r;
967 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
969 if (!p)
970 return p;
971 p = wcschr(p, ch);
972 if (!p)
973 return p;
974 *p = 0;
975 return p+1;
978 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
980 MSIQUERY *view = NULL;
981 MSIRECORD *row = NULL;
982 UINT r;
984 TRACE("%s\n", debugstr_w(file->File));
986 r = MSI_OpenQuery(package->db, &view, L"SELECT * FROM `MsiFileHash` WHERE `File_` = '%s'", file->File);
987 if (r != ERROR_SUCCESS)
988 goto done;
990 r = MSI_ViewExecute(view, NULL);
991 if (r != ERROR_SUCCESS)
992 goto done;
994 r = MSI_ViewFetch(view, &row);
995 if (r != ERROR_SUCCESS)
996 goto done;
998 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
999 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1000 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1001 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1002 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1004 done:
1005 if (view) msiobj_release(&view->hdr);
1006 if (row) msiobj_release(&row->hdr);
1007 return r;
1010 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1012 MSIRECORD *row = MSI_QueryGetRecord( package->db, L"SELECT `DiskId` FROM `Media` WHERE `LastSequence` >= %d",
1013 file->Sequence );
1014 if (!row)
1016 WARN("query failed\n");
1017 return ERROR_FUNCTION_FAILED;
1020 file->disk_id = MSI_RecordGetInteger( row, 1 );
1021 msiobj_release( &row->hdr );
1022 return ERROR_SUCCESS;
1025 static UINT load_file(MSIRECORD *row, LPVOID param)
1027 MSIPACKAGE* package = param;
1028 LPCWSTR component;
1029 MSIFILE *file;
1031 /* fill in the data */
1033 file = msi_alloc_zero( sizeof (MSIFILE) );
1034 if (!file)
1035 return ERROR_NOT_ENOUGH_MEMORY;
1037 file->File = msi_dup_record_field( row, 1 );
1039 component = MSI_RecordGetString( row, 2 );
1040 file->Component = msi_get_loaded_component( package, component );
1042 if (!file->Component)
1044 WARN("Component not found: %s\n", debugstr_w(component));
1045 msi_free(file->File);
1046 msi_free(file);
1047 return ERROR_SUCCESS;
1050 file->FileName = msi_dup_record_field( row, 3 );
1051 msi_reduce_to_long_filename( file->FileName );
1053 file->ShortName = msi_dup_record_field( row, 3 );
1054 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1056 file->FileSize = MSI_RecordGetInteger( row, 4 );
1057 file->Version = msi_dup_record_field( row, 5 );
1058 file->Language = msi_dup_record_field( row, 6 );
1059 file->Attributes = MSI_RecordGetInteger( row, 7 );
1060 file->Sequence = MSI_RecordGetInteger( row, 8 );
1062 file->state = msifs_invalid;
1064 /* if the compressed bits are not set in the file attributes,
1065 * then read the information from the package word count property
1067 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1069 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1071 else if (file->Attributes & (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1073 file->IsCompressed = TRUE;
1075 else if (file->Attributes & msidbFileAttributesNoncompressed)
1077 file->IsCompressed = FALSE;
1079 else file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1081 load_file_hash(package, file);
1082 load_file_disk_id(package, file);
1084 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1086 list_add_tail( &package->files, &file->entry );
1087 return ERROR_SUCCESS;
1090 static UINT load_all_files(MSIPACKAGE *package)
1092 MSIQUERY *view;
1093 UINT rc;
1095 if (!list_empty(&package->files))
1096 return ERROR_SUCCESS;
1098 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `File` ORDER BY `Sequence`", &view);
1099 if (rc != ERROR_SUCCESS)
1100 return ERROR_SUCCESS;
1102 rc = MSI_IterateRecords(view, NULL, load_file, package);
1103 msiobj_release(&view->hdr);
1104 return rc;
1107 static UINT load_media( MSIRECORD *row, LPVOID param )
1109 MSIPACKAGE *package = param;
1110 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1111 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1113 /* FIXME: load external cabinets and directory sources too */
1114 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1115 return ERROR_SUCCESS;
1117 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1120 static UINT load_all_media( MSIPACKAGE *package )
1122 MSIQUERY *view;
1123 UINT r;
1125 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Media` ORDER BY `DiskId`", &view );
1126 if (r != ERROR_SUCCESS)
1127 return ERROR_SUCCESS;
1129 r = MSI_IterateRecords( view, NULL, load_media, package );
1130 msiobj_release( &view->hdr );
1131 return r;
1134 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1136 MSIRECORD *rec = MSI_QueryGetRecord( package->db, L"SELECT `DiskId` FROM `Media` WHERE `LastSequence` >= %u",
1137 patch->Sequence );
1138 if (!rec)
1140 WARN("query failed\n");
1141 return ERROR_FUNCTION_FAILED;
1144 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1145 msiobj_release( &rec->hdr );
1146 return ERROR_SUCCESS;
1149 static UINT load_patch(MSIRECORD *row, LPVOID param)
1151 MSIPACKAGE *package = param;
1152 MSIFILEPATCH *patch;
1153 const WCHAR *file_key;
1155 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1156 if (!patch)
1157 return ERROR_NOT_ENOUGH_MEMORY;
1159 file_key = MSI_RecordGetString( row, 1 );
1160 patch->File = msi_get_loaded_file( package, file_key );
1161 if (!patch->File)
1163 ERR("Failed to find target for patch in File table\n");
1164 msi_free(patch);
1165 return ERROR_FUNCTION_FAILED;
1168 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1169 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1170 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1172 /* FIXME:
1173 * Header field - for patch validation.
1174 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1177 load_patch_disk_id( package, patch );
1179 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1181 list_add_tail( &package->filepatches, &patch->entry );
1183 return ERROR_SUCCESS;
1186 static UINT load_all_patches(MSIPACKAGE *package)
1188 MSIQUERY *view;
1189 UINT rc;
1191 if (!list_empty(&package->filepatches))
1192 return ERROR_SUCCESS;
1194 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Patch` ORDER BY `Sequence`", &view);
1195 if (rc != ERROR_SUCCESS)
1196 return ERROR_SUCCESS;
1198 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1199 msiobj_release(&view->hdr);
1200 return rc;
1203 static UINT iterate_patched_component( MSIRECORD *row, LPVOID param )
1205 MSIPACKAGE *package = param;
1206 const WCHAR *name;
1207 MSICOMPONENT *c;
1209 name = MSI_RecordGetString( row, 1 );
1210 TRACE( "found patched component: %s\n", wine_dbgstr_w(name) );
1211 c = msi_get_loaded_component( package, name );
1212 if (!c)
1213 return ERROR_SUCCESS;
1215 c->updated = 1;
1216 if (!wcscmp( MSI_RecordGetString( row, 2 ), L"INSERT" ))
1217 c->added = 1;
1218 return ERROR_SUCCESS;
1221 static void mark_patched_components( MSIPACKAGE *package )
1223 static const WCHAR select[] = L"SELECT `Row`, `Column` FROM `_TransformView` WHERE `Table`='Component'";
1224 MSIQUERY *q;
1225 UINT r;
1227 r = MSI_OpenQuery( package->db, &q, select );
1228 if (r != ERROR_SUCCESS)
1229 return;
1231 MSI_IterateRecords( q, NULL, iterate_patched_component, package );
1232 msiobj_release( &q->hdr );
1234 while (1)
1236 r = MSI_OpenQuery( package->db, &q, L"ALTER TABLE `_TransformView` FREE" );
1237 if (r != ERROR_SUCCESS)
1238 return;
1239 r = MSI_ViewExecute( q, NULL );
1240 msiobj_release( &q->hdr );
1241 if (r != ERROR_SUCCESS)
1242 return;
1246 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1248 MSIQUERY *view;
1250 folder->persistent = FALSE;
1251 if (!MSI_OpenQuery( package->db, &view, L"SELECT * FROM `CreateFolder` WHERE `Directory_` = '%s'",
1252 folder->Directory ))
1254 if (!MSI_ViewExecute( view, NULL ))
1256 MSIRECORD *rec;
1257 if (!MSI_ViewFetch( view, &rec ))
1259 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1260 folder->persistent = TRUE;
1261 msiobj_release( &rec->hdr );
1264 msiobj_release( &view->hdr );
1266 return ERROR_SUCCESS;
1269 static UINT load_folder( MSIRECORD *row, LPVOID param )
1271 MSIPACKAGE *package = param;
1272 static WCHAR szEmpty[] = L"";
1273 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1274 MSIFOLDER *folder;
1276 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1277 list_init( &folder->children );
1278 folder->Directory = msi_dup_record_field( row, 1 );
1279 folder->Parent = msi_dup_record_field( row, 2 );
1280 p = msi_dup_record_field(row, 3);
1282 TRACE("%s\n", debugstr_w(folder->Directory));
1284 /* split src and target dir */
1285 tgt_short = p;
1286 src_short = folder_split_path( p, ':' );
1288 /* split the long and short paths */
1289 tgt_long = folder_split_path( tgt_short, '|' );
1290 src_long = folder_split_path( src_short, '|' );
1292 /* check for no-op dirs */
1293 if (tgt_short && !wcscmp( L".", tgt_short ))
1294 tgt_short = szEmpty;
1295 if (src_short && !wcscmp( L".", src_short ))
1296 src_short = szEmpty;
1298 if (!tgt_long)
1299 tgt_long = tgt_short;
1301 if (!src_short) {
1302 src_short = tgt_short;
1303 src_long = tgt_long;
1306 if (!src_long)
1307 src_long = src_short;
1309 /* FIXME: use the target short path too */
1310 folder->TargetDefault = strdupW(tgt_long);
1311 folder->SourceShortPath = strdupW(src_short);
1312 folder->SourceLongPath = strdupW(src_long);
1313 msi_free(p);
1315 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1316 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1317 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1319 load_folder_persistence( package, folder );
1321 list_add_tail( &package->folders, &folder->entry );
1322 return ERROR_SUCCESS;
1325 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1327 FolderList *fl;
1329 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1330 fl->folder = child;
1331 list_add_tail( &parent->children, &fl->entry );
1332 return ERROR_SUCCESS;
1335 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1337 MSIPACKAGE *package = param;
1338 MSIFOLDER *parent, *child;
1340 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1341 return ERROR_FUNCTION_FAILED;
1343 if (!child->Parent) return ERROR_SUCCESS;
1345 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1346 return ERROR_FUNCTION_FAILED;
1348 return add_folder_child( parent, child );
1351 static UINT load_all_folders( MSIPACKAGE *package )
1353 MSIQUERY *view;
1354 UINT r;
1356 if (!list_empty(&package->folders))
1357 return ERROR_SUCCESS;
1359 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Directory`", &view );
1360 if (r != ERROR_SUCCESS)
1361 return r;
1363 r = MSI_IterateRecords( view, NULL, load_folder, package );
1364 if (r != ERROR_SUCCESS)
1366 msiobj_release( &view->hdr );
1367 return r;
1369 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1370 msiobj_release( &view->hdr );
1371 return r;
1374 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1376 msi_set_property( package->db, L"CostingComplete", L"0", -1 );
1377 msi_set_property( package->db, L"ROOTDRIVE", L"C:\\", -1 );
1379 load_all_folders( package );
1380 msi_load_all_components( package );
1381 msi_load_all_features( package );
1382 load_all_files( package );
1383 load_all_patches( package );
1384 mark_patched_components( package );
1385 load_all_media( package );
1387 return ERROR_SUCCESS;
1390 static UINT execute_script( MSIPACKAGE *package, UINT script )
1392 UINT i, rc = ERROR_SUCCESS;
1394 TRACE("executing script %u\n", script);
1396 package->script = script;
1398 if (script == SCRIPT_ROLLBACK)
1400 for (i = package->script_actions_count[script]; i > 0; i--)
1402 rc = ACTION_PerformAction(package, package->script_actions[script][i-1]);
1403 if (rc != ERROR_SUCCESS)
1405 ERR("Execution of script %i halted; action %s returned %u\n",
1406 script, debugstr_w(package->script_actions[script][i-1]), rc);
1407 break;
1411 else
1413 for (i = 0; i < package->script_actions_count[script]; i++)
1415 rc = ACTION_PerformAction(package, package->script_actions[script][i]);
1416 if (rc != ERROR_SUCCESS)
1418 ERR("Execution of script %i halted; action %s returned %u\n",
1419 script, debugstr_w(package->script_actions[script][i]), rc);
1420 break;
1425 package->script = SCRIPT_NONE;
1427 msi_free_action_script(package, script);
1428 return rc;
1431 static UINT ACTION_FileCost(MSIPACKAGE *package)
1433 return ERROR_SUCCESS;
1436 static void get_client_counts( MSIPACKAGE *package )
1438 MSICOMPONENT *comp;
1439 HKEY hkey;
1441 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1443 if (!comp->ComponentId) continue;
1445 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, L"S-1-5-18", &hkey, FALSE ) &&
1446 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1448 comp->num_clients = 0;
1449 continue;
1451 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1452 NULL, NULL, NULL, NULL );
1453 RegCloseKey( hkey );
1457 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1459 MSICOMPONENT *comp;
1460 UINT r;
1462 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1464 if (!comp->ComponentId) continue;
1466 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1467 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1468 &comp->Installed );
1469 if (r == ERROR_SUCCESS) continue;
1471 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1472 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1473 &comp->Installed );
1474 if (r == ERROR_SUCCESS) continue;
1476 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1477 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1478 &comp->Installed );
1479 if (r == ERROR_SUCCESS) continue;
1481 comp->Installed = INSTALLSTATE_ABSENT;
1485 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1487 MSIFEATURE *feature;
1489 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1491 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1493 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1494 feature->Installed = INSTALLSTATE_ABSENT;
1495 else
1496 feature->Installed = state;
1500 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1502 return (feature->Level > 0 && feature->Level <= level);
1505 static BOOL process_state_property(MSIPACKAGE* package, int level,
1506 LPCWSTR property, INSTALLSTATE state)
1508 LPWSTR override;
1509 MSIFEATURE *feature;
1510 BOOL remove = !wcscmp(property, L"REMOVE");
1511 BOOL reinstall = !wcscmp(property, L"REINSTALL");
1513 override = msi_dup_property( package->db, property );
1514 if (!override)
1515 return FALSE;
1517 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1519 if (feature->Level <= 0)
1520 continue;
1522 if (reinstall)
1523 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1524 else if (remove)
1525 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1527 if (!wcsicmp( override, L"ALL" ))
1529 feature->Action = state;
1530 feature->ActionRequest = state;
1532 else
1534 LPWSTR ptr = override;
1535 LPWSTR ptr2 = wcschr(override,',');
1537 while (ptr)
1539 int len = ptr2 - ptr;
1541 if ((ptr2 && lstrlenW(feature->Feature) == len && !wcsncmp(ptr, feature->Feature, len))
1542 || (!ptr2 && !wcscmp(ptr, feature->Feature)))
1544 feature->Action = state;
1545 feature->ActionRequest = state;
1546 break;
1548 if (ptr2)
1550 ptr=ptr2+1;
1551 ptr2 = wcschr(ptr,',');
1553 else
1554 break;
1558 msi_free(override);
1559 return TRUE;
1562 static BOOL process_overrides( MSIPACKAGE *package, int level )
1564 BOOL ret = FALSE;
1566 /* all these activation/deactivation things happen in order and things
1567 * later on the list override things earlier on the list.
1569 * 0 INSTALLLEVEL processing
1570 * 1 ADDLOCAL
1571 * 2 REMOVE
1572 * 3 ADDSOURCE
1573 * 4 ADDDEFAULT
1574 * 5 REINSTALL
1575 * 6 ADVERTISE
1576 * 7 COMPADDLOCAL
1577 * 8 COMPADDSOURCE
1578 * 9 FILEADDLOCAL
1579 * 10 FILEADDSOURCE
1580 * 11 FILEADDDEFAULT
1582 ret |= process_state_property( package, level, L"ADDLOCAL", INSTALLSTATE_LOCAL );
1583 ret |= process_state_property( package, level, L"REMOVE", INSTALLSTATE_ABSENT );
1584 ret |= process_state_property( package, level, L"ADDSOURCE", INSTALLSTATE_SOURCE );
1585 ret |= process_state_property( package, level, L"REINSTALL", INSTALLSTATE_UNKNOWN );
1586 ret |= process_state_property( package, level, L"ADVERTISE", INSTALLSTATE_ADVERTISED );
1588 if (ret)
1589 msi_set_property( package->db, L"Preselected", L"1", -1 );
1591 return ret;
1594 static void disable_children( MSIFEATURE *feature, int level )
1596 FeatureList *fl;
1598 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1600 if (!is_feature_selected( feature, level ))
1602 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1603 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1604 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1606 fl->feature->Level = feature->Level;
1607 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1608 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1610 disable_children( fl->feature, level );
1614 static void follow_parent( MSIFEATURE *feature )
1616 FeatureList *fl;
1618 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1620 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1622 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1623 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1624 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1626 fl->feature->Action = feature->Action;
1627 fl->feature->ActionRequest = feature->ActionRequest;
1629 follow_parent( fl->feature );
1633 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1635 int level;
1636 MSICOMPONENT* component;
1637 MSIFEATURE *feature;
1639 TRACE("Checking Install Level\n");
1641 level = msi_get_property_int(package->db, L"INSTALLLEVEL", 1);
1643 if (msi_get_property_int( package->db, L"Preselected", 0 ))
1645 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1647 if (!is_feature_selected( feature, level )) continue;
1649 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1651 if (feature->Installed == INSTALLSTATE_ABSENT)
1653 feature->Action = INSTALLSTATE_UNKNOWN;
1654 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1656 else
1658 feature->Action = feature->Installed;
1659 feature->ActionRequest = feature->Installed;
1664 else if (!msi_get_property_int( package->db, L"Installed", 0 ))
1666 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1668 if (!is_feature_selected( feature, level )) continue;
1670 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1672 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1674 feature->Action = INSTALLSTATE_SOURCE;
1675 feature->ActionRequest = INSTALLSTATE_SOURCE;
1677 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1679 feature->Action = INSTALLSTATE_ADVERTISED;
1680 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1682 else
1684 feature->Action = INSTALLSTATE_LOCAL;
1685 feature->ActionRequest = INSTALLSTATE_LOCAL;
1690 else
1692 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1694 ComponentList *cl;
1695 MSIFEATURE *cur;
1697 if (!is_feature_selected( feature, level )) continue;
1698 if (feature->ActionRequest != INSTALLSTATE_UNKNOWN) continue;
1700 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1702 if (!cl->component->updated && !cl->component->added)
1703 continue;
1705 cur = feature;
1706 while (cur)
1708 if (cur->ActionRequest != INSTALLSTATE_UNKNOWN)
1709 break;
1711 if (cur->Installed != INSTALLSTATE_ABSENT)
1713 cur->Action = cur->Installed;
1714 cur->ActionRequest = cur->Installed;
1716 else if (!cl->component->added)
1718 break;
1720 else if (cur->Attributes & msidbFeatureAttributesFavorSource)
1722 cur->Action = INSTALLSTATE_SOURCE;
1723 cur->ActionRequest = INSTALLSTATE_SOURCE;
1725 else if (cur->Attributes & msidbFeatureAttributesFavorAdvertise)
1727 cur->Action = INSTALLSTATE_ADVERTISED;
1728 cur->ActionRequest = INSTALLSTATE_ADVERTISED;
1730 else
1732 cur->Action = INSTALLSTATE_LOCAL;
1733 cur->ActionRequest = INSTALLSTATE_LOCAL;
1736 if (!cur->Feature_Parent)
1737 break;
1738 cur = msi_get_loaded_feature(package, cur->Feature_Parent);
1744 /* disable child features of unselected parent or follow parent */
1745 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1747 if (feature->Feature_Parent) continue;
1748 disable_children( feature, level );
1749 follow_parent( feature );
1752 /* now we want to set component state based based on feature state */
1753 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1755 ComponentList *cl;
1757 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1758 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1759 feature->ActionRequest, feature->Action);
1761 /* features with components that have compressed files are made local */
1762 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1764 if (cl->component->ForceLocalState &&
1765 feature->ActionRequest == INSTALLSTATE_SOURCE)
1767 feature->Action = INSTALLSTATE_LOCAL;
1768 feature->ActionRequest = INSTALLSTATE_LOCAL;
1769 break;
1773 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1775 component = cl->component;
1777 switch (feature->ActionRequest)
1779 case INSTALLSTATE_ABSENT:
1780 component->anyAbsent = 1;
1781 break;
1782 case INSTALLSTATE_ADVERTISED:
1783 component->hasAdvertisedFeature = 1;
1784 break;
1785 case INSTALLSTATE_SOURCE:
1786 component->hasSourceFeature = 1;
1787 break;
1788 case INSTALLSTATE_LOCAL:
1789 component->hasLocalFeature = 1;
1790 break;
1791 case INSTALLSTATE_DEFAULT:
1792 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1793 component->hasAdvertisedFeature = 1;
1794 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1795 component->hasSourceFeature = 1;
1796 else
1797 component->hasLocalFeature = 1;
1798 break;
1799 case INSTALLSTATE_UNKNOWN:
1800 if (feature->Installed == INSTALLSTATE_ADVERTISED)
1801 component->hasAdvertisedFeature = 1;
1802 if (feature->Installed == INSTALLSTATE_SOURCE)
1803 component->hasSourceFeature = 1;
1804 if (feature->Installed == INSTALLSTATE_LOCAL)
1805 component->hasLocalFeature = 1;
1806 break;
1807 default:
1808 break;
1813 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1815 /* check if it's local or source */
1816 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1817 (component->hasLocalFeature || component->hasSourceFeature))
1819 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1820 !component->ForceLocalState)
1822 component->Action = INSTALLSTATE_SOURCE;
1823 component->ActionRequest = INSTALLSTATE_SOURCE;
1825 else
1827 component->Action = INSTALLSTATE_LOCAL;
1828 component->ActionRequest = INSTALLSTATE_LOCAL;
1830 continue;
1833 /* if any feature is local, the component must be local too */
1834 if (component->hasLocalFeature)
1836 component->Action = INSTALLSTATE_LOCAL;
1837 component->ActionRequest = INSTALLSTATE_LOCAL;
1838 continue;
1840 if (component->hasSourceFeature)
1842 component->Action = INSTALLSTATE_SOURCE;
1843 component->ActionRequest = INSTALLSTATE_SOURCE;
1844 continue;
1846 if (component->hasAdvertisedFeature)
1848 component->Action = INSTALLSTATE_ADVERTISED;
1849 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1850 continue;
1852 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1853 if (component->anyAbsent && component->ComponentId)
1855 component->Action = INSTALLSTATE_ABSENT;
1856 component->ActionRequest = INSTALLSTATE_ABSENT;
1860 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1862 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1864 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1865 component->Action = INSTALLSTATE_LOCAL;
1866 component->ActionRequest = INSTALLSTATE_LOCAL;
1869 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1870 component->Installed == INSTALLSTATE_SOURCE &&
1871 component->hasSourceFeature)
1873 component->Action = INSTALLSTATE_UNKNOWN;
1874 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1877 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1878 component->num_clients++;
1879 else if (component->Action == INSTALLSTATE_ABSENT)
1881 component->num_clients--;
1883 if (component->num_clients > 0)
1885 TRACE("multiple clients uses %s - disallowing uninstallation\n", debugstr_w(component->Component));
1886 component->Action = INSTALLSTATE_UNKNOWN;
1890 TRACE("component %s (installed %d request %d action %d)\n",
1891 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1894 return ERROR_SUCCESS;
1897 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1899 MSIPACKAGE *package = param;
1900 LPCWSTR name;
1901 MSIFEATURE *feature;
1903 name = MSI_RecordGetString( row, 1 );
1905 feature = msi_get_loaded_feature( package, name );
1906 if (!feature)
1907 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1908 else
1910 LPCWSTR Condition;
1911 Condition = MSI_RecordGetString(row,3);
1913 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1915 int level = MSI_RecordGetInteger(row,2);
1916 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1917 feature->Level = level;
1920 return ERROR_SUCCESS;
1923 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
1925 DWORD ms, ls;
1927 msi_parse_version_string( version, &ms, &ls );
1929 if (fi->dwFileVersionMS > ms) return 1;
1930 else if (fi->dwFileVersionMS < ms) return -1;
1931 else if (fi->dwFileVersionLS > ls) return 1;
1932 else if (fi->dwFileVersionLS < ls) return -1;
1933 return 0;
1936 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
1938 DWORD ms1, ms2;
1940 msi_parse_version_string( ver1, &ms1, NULL );
1941 msi_parse_version_string( ver2, &ms2, NULL );
1943 if (ms1 > ms2) return 1;
1944 else if (ms1 < ms2) return -1;
1945 return 0;
1948 static WCHAR *create_temp_dir( MSIDATABASE *db )
1950 static UINT id;
1951 WCHAR *ret;
1953 if (!db->tempfolder)
1955 WCHAR tmp[MAX_PATH];
1956 UINT len = ARRAY_SIZE( tmp );
1958 if (msi_get_property( db, L"TempFolder", tmp, &len ) ||
1959 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
1961 GetTempPathW( MAX_PATH, tmp );
1963 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
1966 if ((ret = msi_alloc( (lstrlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
1968 for (;;)
1970 if (!GetTempFileNameW( db->tempfolder, L"msi", ++id, ret ))
1972 msi_free( ret );
1973 return NULL;
1975 if (CreateDirectoryW( ret, NULL )) break;
1979 return ret;
1983 * msi_build_directory_name()
1985 * This function is to save messing round with directory names
1986 * It handles adding backslashes between path segments,
1987 * and can add \ at the end of the directory name if told to.
1989 * It takes a variable number of arguments.
1990 * It always allocates a new string for the result, so make sure
1991 * to free the return value when finished with it.
1993 * The first arg is the number of path segments that follow.
1994 * The arguments following count are a list of path segments.
1995 * A path segment may be NULL.
1997 * Path segments will be added with a \ separating them.
1998 * A \ will not be added after the last segment, however if the
1999 * last segment is NULL, then the last character will be a \
2001 WCHAR * WINAPIV msi_build_directory_name( DWORD count, ... )
2003 DWORD sz = 1, i;
2004 WCHAR *dir;
2005 va_list va;
2007 va_start( va, count );
2008 for (i = 0; i < count; i++)
2010 const WCHAR *str = va_arg( va, const WCHAR * );
2011 if (str) sz += lstrlenW( str ) + 1;
2013 va_end( va );
2015 dir = msi_alloc( sz * sizeof(WCHAR) );
2016 dir[0] = 0;
2018 va_start( va, count );
2019 for (i = 0; i < count; i++)
2021 const WCHAR *str = va_arg( va, const WCHAR * );
2022 if (!str) continue;
2023 lstrcatW( dir, str );
2024 if ( i + 1 != count && dir[0] && dir[lstrlenW( dir ) - 1] != '\\') lstrcatW( dir, L"\\" );
2026 va_end( va );
2027 return dir;
2030 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2032 return comp->assembly && !comp->assembly->application;
2035 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2037 msi_free( file->TargetPath );
2038 if (msi_is_global_assembly( file->Component ))
2040 MSIASSEMBLY *assembly = file->Component->assembly;
2042 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2043 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2045 else
2047 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2048 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2051 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2054 static UINT calculate_file_cost( MSIPACKAGE *package )
2056 VS_FIXEDFILEINFO *file_version;
2057 WCHAR *font_version;
2058 MSIFILE *file;
2060 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2062 MSICOMPONENT *comp = file->Component;
2063 DWORD file_size;
2065 if (!comp->Enabled) continue;
2067 if (file->IsCompressed)
2068 comp->ForceLocalState = TRUE;
2070 set_target_path( package, file );
2072 if ((comp->assembly && !comp->assembly->installed) ||
2073 msi_get_file_attributes( package, file->TargetPath ) == INVALID_FILE_ATTRIBUTES)
2075 comp->Cost += file->FileSize;
2076 continue;
2078 file_size = msi_get_disk_file_size( package, file->TargetPath );
2079 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2081 if (file->Version)
2083 if ((file_version = msi_get_disk_file_version( package, file->TargetPath )))
2085 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2087 comp->Cost += file->FileSize - file_size;
2089 msi_free( file_version );
2090 continue;
2092 else if ((font_version = msi_get_font_file_version( package, file->TargetPath )))
2094 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2096 comp->Cost += file->FileSize - file_size;
2098 msi_free( font_version );
2099 continue;
2102 if (file_size != file->FileSize)
2104 comp->Cost += file->FileSize - file_size;
2108 return ERROR_SUCCESS;
2111 WCHAR *msi_normalize_path( const WCHAR *in )
2113 const WCHAR *p = in;
2114 WCHAR *q, *ret;
2115 int n, len = lstrlenW( in ) + 2;
2117 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2119 len = 0;
2120 while (1)
2122 /* copy until the end of the string or a space */
2123 while (*p != ' ' && (*q = *p))
2125 p++, len++;
2126 /* reduce many backslashes to one */
2127 if (*p != '\\' || *q != '\\')
2128 q++;
2131 /* quit at the end of the string */
2132 if (!*p)
2133 break;
2135 /* count the number of spaces */
2136 n = 0;
2137 while (p[n] == ' ')
2138 n++;
2140 /* if it's leading or trailing space, skip it */
2141 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2142 p += n;
2143 else /* copy n spaces */
2144 while (n && (*q++ = *p++)) n--;
2146 while (q - ret > 0 && q[-1] == ' ') q--;
2147 if (q - ret > 0 && q[-1] != '\\')
2149 q[0] = '\\';
2150 q[1] = 0;
2152 return ret;
2155 static WCHAR *get_install_location( MSIPACKAGE *package )
2157 HKEY hkey;
2158 WCHAR *path;
2160 if (!package->ProductCode) return NULL;
2161 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2162 if ((path = msi_reg_get_val_str( hkey, L"InstallLocation" )) && !path[0])
2164 msi_free( path );
2165 path = NULL;
2167 RegCloseKey( hkey );
2168 return path;
2171 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2173 FolderList *fl;
2174 MSIFOLDER *folder, *parent, *child;
2175 WCHAR *path, *normalized_path;
2177 TRACE("resolving %s\n", debugstr_w(name));
2179 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2181 if (!wcscmp( folder->Directory, L"TARGETDIR" )) /* special resolving for target root dir */
2183 if (!(path = get_install_location( package )) &&
2184 (!load_prop || !(path = msi_dup_property( package->db, L"TARGETDIR" ))))
2186 path = msi_dup_property( package->db, L"ROOTDRIVE" );
2189 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2191 if (folder->Parent && wcscmp( folder->Directory, folder->Parent ))
2193 parent = msi_get_loaded_folder( package, folder->Parent );
2194 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2196 else
2197 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2200 normalized_path = msi_normalize_path( path );
2201 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2202 msi_free( path );
2204 msi_free( folder->ResolvedTarget );
2205 folder->ResolvedTarget = normalized_path;
2207 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2209 child = fl->folder;
2210 msi_resolve_target_folder( package, child->Directory, load_prop );
2212 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2215 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2217 MSICOMPONENT *comp;
2218 ULONGLONG ret = 0;
2220 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2222 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2224 return ret;
2227 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2229 MSICOMPONENT *comp;
2230 MSIQUERY *view;
2231 WCHAR *level, *primary_key, *primary_folder;
2232 UINT rc;
2234 TRACE("Building directory properties\n");
2235 msi_resolve_target_folder( package, L"TARGETDIR", TRUE );
2237 TRACE("Evaluating component conditions\n");
2238 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2240 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2242 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2243 comp->Enabled = FALSE;
2245 else
2246 comp->Enabled = TRUE;
2248 get_client_counts( package );
2250 /* read components states from the registry */
2251 ACTION_GetComponentInstallStates(package);
2252 ACTION_GetFeatureInstallStates(package);
2254 if (!process_overrides( package, msi_get_property_int( package->db, L"INSTALLLEVEL", 1 ) ))
2256 TRACE("Evaluating feature conditions\n");
2258 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Condition`", &view );
2259 if (rc == ERROR_SUCCESS)
2261 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2262 msiobj_release( &view->hdr );
2263 if (rc != ERROR_SUCCESS)
2264 return rc;
2268 TRACE("Calculating file cost\n");
2269 calculate_file_cost( package );
2271 msi_set_property( package->db, L"CostingComplete", L"1", -1 );
2272 /* set default run level if not set */
2273 level = msi_dup_property( package->db, L"INSTALLLEVEL" );
2274 if (!level) msi_set_property( package->db, L"INSTALLLEVEL", L"1", -1 );
2275 msi_free(level);
2277 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2279 if ((primary_key = msi_dup_property( package->db, L"PRIMARYFOLDER" )))
2281 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2283 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2284 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2286 ULARGE_INTEGER free;
2287 ULONGLONG required;
2288 WCHAR buf[21];
2290 primary_folder[2] = 0;
2291 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2293 swprintf( buf, ARRAY_SIZE(buf), L"%lu", free.QuadPart / 512 );
2294 msi_set_property( package->db, L"PrimaryVolumeSpaceAvailable", buf, -1 );
2296 required = get_volume_space_required( package );
2297 swprintf( buf, ARRAY_SIZE(buf), L"%lu", required / 512 );
2298 msi_set_property( package->db, L"PrimaryVolumeSpaceRequired", buf, -1 );
2300 swprintf( buf, ARRAY_SIZE(buf), L"%lu", (free.QuadPart - required) / 512 );
2301 msi_set_property( package->db, L"PrimaryVolumeSpaceRemaining", buf, -1 );
2302 msi_set_property( package->db, L"PrimaryVolumePath", primary_folder, 2 );
2304 msi_free( primary_folder );
2306 msi_free( primary_key );
2309 /* FIXME: check volume disk space */
2310 msi_set_property( package->db, L"OutOfDiskSpace", L"0", -1 );
2311 msi_set_property( package->db, L"OutOfNoRbDiskSpace", L"0", -1 );
2313 return ERROR_SUCCESS;
2316 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2318 BYTE *data;
2320 if (!value)
2322 *size = sizeof(WCHAR);
2323 *type = REG_SZ;
2324 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2325 return data;
2327 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2329 if (value[1]=='x')
2331 LPWSTR ptr;
2332 CHAR byte[5];
2333 LPWSTR deformated = NULL;
2334 int count;
2336 deformat_string(package, &value[2], &deformated);
2338 /* binary value type */
2339 ptr = deformated;
2340 *type = REG_BINARY;
2341 if (lstrlenW(ptr)%2)
2342 *size = (lstrlenW(ptr)/2)+1;
2343 else
2344 *size = lstrlenW(ptr)/2;
2346 data = msi_alloc(*size);
2348 byte[0] = '0';
2349 byte[1] = 'x';
2350 byte[4] = 0;
2351 count = 0;
2352 /* if uneven pad with a zero in front */
2353 if (lstrlenW(ptr)%2)
2355 byte[2]= '0';
2356 byte[3]= *ptr;
2357 ptr++;
2358 data[count] = (BYTE)strtol(byte,NULL,0);
2359 count ++;
2360 TRACE("Uneven byte count\n");
2362 while (*ptr)
2364 byte[2]= *ptr;
2365 ptr++;
2366 byte[3]= *ptr;
2367 ptr++;
2368 data[count] = (BYTE)strtol(byte,NULL,0);
2369 count ++;
2371 msi_free(deformated);
2373 TRACE("Data %i bytes(%i)\n",*size,count);
2375 else
2377 LPWSTR deformated;
2378 LPWSTR p;
2379 DWORD d = 0;
2380 deformat_string(package, &value[1], &deformated);
2382 *type=REG_DWORD;
2383 *size = sizeof(DWORD);
2384 data = msi_alloc(*size);
2385 p = deformated;
2386 if (*p == '-')
2387 p++;
2388 while (*p)
2390 if ( (*p < '0') || (*p > '9') )
2391 break;
2392 d *= 10;
2393 d += (*p - '0');
2394 p++;
2396 if (deformated[0] == '-')
2397 d = -d;
2398 *(LPDWORD)data = d;
2399 TRACE("DWORD %i\n",*(LPDWORD)data);
2401 msi_free(deformated);
2404 else
2406 const WCHAR *ptr = value;
2408 *type = REG_SZ;
2409 if (value[0] == '#')
2411 ptr++; len--;
2412 if (value[1] == '%')
2414 ptr++; len--;
2415 *type = REG_EXPAND_SZ;
2418 data = (BYTE *)msi_strdupW( ptr, len );
2419 if (len > lstrlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2420 *size = (len + 1) * sizeof(WCHAR);
2422 return data;
2425 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2427 const WCHAR *ret;
2429 switch (root)
2431 case -1:
2432 if (msi_get_property_int( package->db, L"ALLUSERS", 0 ))
2434 *root_key = HKEY_LOCAL_MACHINE;
2435 ret = L"HKEY_LOCAL_MACHINE\\";
2437 else
2439 *root_key = HKEY_CURRENT_USER;
2440 ret = L"HKEY_CURRENT_USER\\";
2442 break;
2443 case 0:
2444 *root_key = HKEY_CLASSES_ROOT;
2445 ret = L"HKEY_CLASSES_ROOT\\";
2446 break;
2447 case 1:
2448 *root_key = HKEY_CURRENT_USER;
2449 ret = L"HKEY_CURRENT_USER\\";
2450 break;
2451 case 2:
2452 *root_key = HKEY_LOCAL_MACHINE;
2453 ret = L"HKEY_LOCAL_MACHINE\\";
2454 break;
2455 case 3:
2456 *root_key = HKEY_USERS;
2457 ret = L"HKEY_USERS\\";
2458 break;
2459 default:
2460 ERR("Unknown root %i\n", root);
2461 return NULL;
2464 return ret;
2467 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2469 REGSAM view = 0;
2470 if (is_wow64 || is_64bit)
2471 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2472 return view;
2475 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2477 WCHAR *subkey, *p, *q;
2478 HKEY hkey, ret = NULL;
2479 LONG res;
2481 access |= get_registry_view( comp );
2483 if (!(subkey = strdupW( path ))) return NULL;
2484 p = subkey;
2485 if ((q = wcschr( p, '\\' ))) *q = 0;
2486 if (create)
2487 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2488 else
2489 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2490 if (res)
2492 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2493 msi_free( subkey );
2494 return NULL;
2496 if (q && q[1])
2498 ret = open_key( comp, hkey, q + 1, create, access );
2499 RegCloseKey( hkey );
2501 else ret = hkey;
2502 msi_free( subkey );
2503 return ret;
2506 static BOOL is_special_entry( const WCHAR *name )
2508 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2511 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2513 const WCHAR *p = str;
2514 WCHAR **ret;
2515 int i = 0;
2517 *count = 0;
2518 if (!str) return NULL;
2519 while ((p - str) < len)
2521 p += lstrlenW( p ) + 1;
2522 (*count)++;
2524 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2525 p = str;
2526 while ((p - str) < len)
2528 if (!(ret[i] = strdupW( p )))
2530 for (; i >= 0; i--) msi_free( ret[i] );
2531 msi_free( ret );
2532 return NULL;
2534 p += lstrlenW( p ) + 1;
2535 i++;
2537 return ret;
2540 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2541 WCHAR **right, DWORD right_count, DWORD *size )
2543 WCHAR *ret, *p;
2544 unsigned int i;
2546 *size = sizeof(WCHAR);
2547 for (i = 0; i < left_count; i++) *size += (lstrlenW( left[i] ) + 1) * sizeof(WCHAR);
2548 for (i = 0; i < right_count; i++) *size += (lstrlenW( right[i] ) + 1) * sizeof(WCHAR);
2550 if (!(ret = p = msi_alloc( *size ))) return NULL;
2552 for (i = 0; i < left_count; i++)
2554 lstrcpyW( p, left[i] );
2555 p += lstrlenW( p ) + 1;
2557 for (i = 0; i < right_count; i++)
2559 lstrcpyW( p, right[i] );
2560 p += lstrlenW( p ) + 1;
2562 *p = 0;
2563 return ret;
2566 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2567 WCHAR **new, DWORD new_count )
2569 DWORD ret = old_count;
2570 unsigned int i, j, k;
2572 for (i = 0; i < new_count; i++)
2574 for (j = 0; j < old_count; j++)
2576 if (old[j] && !wcscmp( new[i], old[j] ))
2578 msi_free( old[j] );
2579 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2580 old[k] = NULL;
2581 ret--;
2585 return ret;
2588 enum join_op
2590 JOIN_OP_APPEND,
2591 JOIN_OP_PREPEND,
2592 JOIN_OP_REPLACE
2595 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2596 WCHAR **new, DWORD new_count, DWORD *size )
2598 switch (op)
2600 case JOIN_OP_APPEND:
2601 old_count = remove_duplicate_values( old, old_count, new, new_count );
2602 return flatten_multi_string_values( old, old_count, new, new_count, size );
2604 case JOIN_OP_PREPEND:
2605 old_count = remove_duplicate_values( old, old_count, new, new_count );
2606 return flatten_multi_string_values( new, new_count, old, old_count, size );
2608 case JOIN_OP_REPLACE:
2609 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2611 default:
2612 ERR("unhandled join op %u\n", op);
2613 return NULL;
2617 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2618 BYTE *new_value, DWORD new_size, DWORD *size )
2620 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2621 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2622 enum join_op op = JOIN_OP_REPLACE;
2623 WCHAR **old = NULL, **new = NULL;
2624 BYTE *ret;
2626 if (new_size / sizeof(WCHAR) - 1 > 1)
2628 new_ptr = (const WCHAR *)new_value;
2629 new_len = new_size / sizeof(WCHAR) - 1;
2631 if (!new_ptr[0] && new_ptr[new_len - 1])
2633 op = JOIN_OP_APPEND;
2634 new_len--;
2635 new_ptr++;
2637 else if (new_ptr[0] && !new_ptr[new_len - 1])
2639 op = JOIN_OP_PREPEND;
2640 new_len--;
2642 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2644 op = JOIN_OP_REPLACE;
2645 new_len -= 2;
2646 new_ptr++;
2648 new = split_multi_string_values( new_ptr, new_len, &new_count );
2650 if (old_size / sizeof(WCHAR) - 1 > 1)
2652 old_ptr = (const WCHAR *)old_value;
2653 old_len = old_size / sizeof(WCHAR) - 1;
2654 old = split_multi_string_values( old_ptr, old_len, &old_count );
2656 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2657 for (i = 0; i < old_count; i++) msi_free( old[i] );
2658 for (i = 0; i < new_count; i++) msi_free( new[i] );
2659 msi_free( old );
2660 msi_free( new );
2661 return ret;
2664 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2666 BYTE *ret;
2667 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2668 if (!(ret = msi_alloc( *size ))) return NULL;
2669 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2670 return ret;
2673 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2675 MSIPACKAGE *package = param;
2676 BYTE *new_value, *old_value = NULL;
2677 HKEY root_key, hkey;
2678 DWORD type, old_type, new_size, old_size = 0;
2679 LPWSTR deformated, uikey;
2680 const WCHAR *szRoot, *component, *name, *key, *str;
2681 MSICOMPONENT *comp;
2682 MSIRECORD * uirow;
2683 INT root;
2684 BOOL check_first = FALSE;
2685 int len;
2687 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2689 component = MSI_RecordGetString(row, 6);
2690 comp = msi_get_loaded_component(package,component);
2691 if (!comp)
2692 return ERROR_SUCCESS;
2694 comp->Action = msi_get_component_action( package, comp );
2695 if (comp->Action != INSTALLSTATE_LOCAL && comp->Action != INSTALLSTATE_SOURCE)
2697 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2698 return ERROR_SUCCESS;
2701 name = MSI_RecordGetString(row, 4);
2702 if( MSI_RecordIsNull(row,5) && name )
2704 /* null values can have special meanings */
2705 if (name[0]=='-' && name[1] == 0)
2706 return ERROR_SUCCESS;
2707 if ((name[0] == '+' || name[0] == '*') && !name[1])
2708 check_first = TRUE;
2711 root = MSI_RecordGetInteger(row,2);
2712 key = MSI_RecordGetString(row, 3);
2714 szRoot = get_root_key( package, root, &root_key );
2715 if (!szRoot)
2716 return ERROR_SUCCESS;
2718 deformat_string(package, key , &deformated);
2719 uikey = msi_alloc( (lstrlenW(deformated) + lstrlenW(szRoot) + 1) * sizeof(WCHAR) );
2720 lstrcpyW(uikey,szRoot);
2721 lstrcatW(uikey,deformated);
2723 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2725 ERR("Could not create key %s\n", debugstr_w(deformated));
2726 msi_free(uikey);
2727 msi_free(deformated);
2728 return ERROR_FUNCTION_FAILED;
2730 msi_free( deformated );
2731 str = msi_record_get_string( row, 5, NULL );
2732 len = deformat_string( package, str, &deformated );
2733 new_value = parse_value( package, deformated, len, &type, &new_size );
2735 msi_free( deformated );
2736 deformat_string(package, name, &deformated);
2738 if (!is_special_entry( name ))
2740 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2741 if (type == REG_MULTI_SZ)
2743 BYTE *new;
2744 if (old_value && old_type != REG_MULTI_SZ)
2746 msi_free( old_value );
2747 old_value = NULL;
2748 old_size = 0;
2750 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2751 msi_free( new_value );
2752 new_value = new;
2754 if (!check_first)
2756 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2757 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2759 else if (!old_value)
2761 if (deformated || new_size)
2763 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2764 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2767 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2769 RegCloseKey(hkey);
2771 uirow = MSI_CreateRecord(3);
2772 MSI_RecordSetStringW(uirow,2,deformated);
2773 MSI_RecordSetStringW(uirow,1,uikey);
2774 if (type == REG_SZ || type == REG_EXPAND_SZ)
2775 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2776 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2777 msiobj_release( &uirow->hdr );
2779 msi_free(new_value);
2780 msi_free(old_value);
2781 msi_free(deformated);
2782 msi_free(uikey);
2784 return ERROR_SUCCESS;
2787 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2789 MSIQUERY *view;
2790 UINT rc;
2792 if (package->script == SCRIPT_NONE)
2793 return msi_schedule_action(package, SCRIPT_INSTALL, L"WriteRegistryValues");
2795 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Registry`", &view);
2796 if (rc != ERROR_SUCCESS)
2797 return ERROR_SUCCESS;
2799 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2800 msiobj_release(&view->hdr);
2801 return rc;
2804 static int is_key_empty(const MSICOMPONENT *comp, HKEY root, const WCHAR *path)
2806 DWORD subkeys, values;
2807 HKEY key;
2808 LONG res;
2810 key = open_key(comp, root, path, FALSE, KEY_READ);
2811 if (!key) return 0;
2813 res = RegQueryInfoKeyW(key, 0, 0, 0, &subkeys, 0, 0, &values, 0, 0, 0, 0);
2814 RegCloseKey(key);
2816 return !res && !subkeys && !values;
2819 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2821 LONG res = ERROR_SUCCESS;
2822 REGSAM access = get_registry_view( comp );
2823 WCHAR *subkey, *p;
2824 HKEY hkey;
2826 if (!(subkey = strdupW( path ))) return;
2829 if ((p = wcsrchr( subkey, '\\' )))
2831 *p = 0;
2832 if (!p[1]) continue; /* trailing backslash */
2833 hkey = open_key( comp, root, subkey, FALSE, READ_CONTROL );
2834 if (!hkey) break;
2835 if (!is_key_empty(comp, hkey, p + 1))
2837 RegCloseKey(hkey);
2838 break;
2840 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2841 RegCloseKey( hkey );
2843 else if (is_key_empty(comp, root, subkey))
2844 res = RegDeleteKeyExW( root, subkey, access, 0 );
2845 if (res)
2847 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2848 break;
2850 } while (p);
2851 msi_free( subkey );
2854 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
2856 LONG res;
2857 HKEY hkey;
2859 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
2861 if ((res = RegDeleteValueW( hkey, value )))
2862 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2864 RegCloseKey( hkey );
2865 if (is_key_empty(comp, root, path))
2867 TRACE("removing empty key %s\n", debugstr_w(path));
2868 delete_key( comp, root, path );
2873 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2875 LONG res;
2876 HKEY hkey;
2878 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
2879 res = RegDeleteTreeW( hkey, NULL );
2880 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
2881 delete_key( comp, root, path );
2882 RegCloseKey( hkey );
2885 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2887 MSIPACKAGE *package = param;
2888 LPCWSTR component, name, key_str, root_key_str;
2889 LPWSTR deformated_key, deformated_name, ui_key_str;
2890 MSICOMPONENT *comp;
2891 MSIRECORD *uirow;
2892 BOOL delete_key = FALSE;
2893 HKEY hkey_root;
2894 UINT size;
2895 INT root;
2897 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2899 component = MSI_RecordGetString( row, 6 );
2900 comp = msi_get_loaded_component( package, component );
2901 if (!comp)
2902 return ERROR_SUCCESS;
2904 comp->Action = msi_get_component_action( package, comp );
2905 if (comp->Action != INSTALLSTATE_ABSENT)
2907 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2908 return ERROR_SUCCESS;
2911 name = MSI_RecordGetString( row, 4 );
2912 if (MSI_RecordIsNull( row, 5 ) && name )
2914 if (name[0] == '+' && !name[1])
2915 return ERROR_SUCCESS;
2916 if ((name[0] == '-' || name[0] == '*') && !name[1])
2918 delete_key = TRUE;
2919 name = NULL;
2923 root = MSI_RecordGetInteger( row, 2 );
2924 key_str = MSI_RecordGetString( row, 3 );
2926 root_key_str = get_root_key( package, root, &hkey_root );
2927 if (!root_key_str)
2928 return ERROR_SUCCESS;
2930 deformat_string( package, key_str, &deformated_key );
2931 size = lstrlenW( deformated_key ) + lstrlenW( root_key_str ) + 1;
2932 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2933 lstrcpyW( ui_key_str, root_key_str );
2934 lstrcatW( ui_key_str, deformated_key );
2936 deformat_string( package, name, &deformated_name );
2938 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
2939 else delete_value( comp, hkey_root, deformated_key, deformated_name );
2940 msi_free( deformated_key );
2942 uirow = MSI_CreateRecord( 2 );
2943 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2944 MSI_RecordSetStringW( uirow, 2, deformated_name );
2945 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2946 msiobj_release( &uirow->hdr );
2948 msi_free( ui_key_str );
2949 msi_free( deformated_name );
2950 return ERROR_SUCCESS;
2953 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2955 MSIPACKAGE *package = param;
2956 LPCWSTR component, name, key_str, root_key_str;
2957 LPWSTR deformated_key, deformated_name, ui_key_str;
2958 MSICOMPONENT *comp;
2959 MSIRECORD *uirow;
2960 BOOL delete_key = FALSE;
2961 HKEY hkey_root;
2962 UINT size;
2963 INT root;
2965 component = MSI_RecordGetString( row, 5 );
2966 comp = msi_get_loaded_component( package, component );
2967 if (!comp)
2968 return ERROR_SUCCESS;
2970 comp->Action = msi_get_component_action( package, comp );
2971 if (comp->Action != INSTALLSTATE_LOCAL)
2973 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2974 return ERROR_SUCCESS;
2977 if ((name = MSI_RecordGetString( row, 4 )))
2979 if (name[0] == '-' && !name[1])
2981 delete_key = TRUE;
2982 name = NULL;
2986 root = MSI_RecordGetInteger( row, 2 );
2987 key_str = MSI_RecordGetString( row, 3 );
2989 root_key_str = get_root_key( package, root, &hkey_root );
2990 if (!root_key_str)
2991 return ERROR_SUCCESS;
2993 deformat_string( package, key_str, &deformated_key );
2994 size = lstrlenW( deformated_key ) + lstrlenW( root_key_str ) + 1;
2995 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2996 lstrcpyW( ui_key_str, root_key_str );
2997 lstrcatW( ui_key_str, deformated_key );
2999 deformat_string( package, name, &deformated_name );
3001 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3002 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3003 msi_free( deformated_key );
3005 uirow = MSI_CreateRecord( 2 );
3006 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3007 MSI_RecordSetStringW( uirow, 2, deformated_name );
3008 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3009 msiobj_release( &uirow->hdr );
3011 msi_free( ui_key_str );
3012 msi_free( deformated_name );
3013 return ERROR_SUCCESS;
3016 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3018 MSIQUERY *view;
3019 UINT rc;
3021 if (package->script == SCRIPT_NONE)
3022 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveRegistryValues");
3024 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Registry`", &view );
3025 if (rc == ERROR_SUCCESS)
3027 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3028 msiobj_release( &view->hdr );
3029 if (rc != ERROR_SUCCESS)
3030 return rc;
3032 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `RemoveRegistry`", &view );
3033 if (rc == ERROR_SUCCESS)
3035 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3036 msiobj_release( &view->hdr );
3037 if (rc != ERROR_SUCCESS)
3038 return rc;
3040 return ERROR_SUCCESS;
3043 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3045 return ERROR_SUCCESS;
3049 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3051 MSICOMPONENT *comp;
3052 DWORD total = 0, count = 0;
3053 MSIQUERY *view;
3054 MSIFEATURE *feature;
3055 MSIFILE *file;
3056 UINT rc;
3058 TRACE("InstallValidate\n");
3060 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Registry`", &view );
3061 if (rc == ERROR_SUCCESS)
3063 rc = MSI_IterateRecords( view, &count, NULL, package );
3064 msiobj_release( &view->hdr );
3065 if (rc != ERROR_SUCCESS)
3066 return rc;
3067 total += count * REG_PROGRESS_VALUE;
3069 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3070 total += COMPONENT_PROGRESS_VALUE;
3072 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3073 total += file->FileSize;
3075 msi_ui_progress( package, 0, total, 0, 0 );
3077 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3079 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3080 debugstr_w(feature->Feature), feature->Installed,
3081 feature->ActionRequest, feature->Action);
3083 return ERROR_SUCCESS;
3086 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3088 MSIPACKAGE* package = param;
3089 const WCHAR *cond, *message;
3090 UINT r;
3092 cond = MSI_RecordGetString(row, 1);
3093 r = MSI_EvaluateConditionW(package, cond);
3094 if (r == MSICONDITION_FALSE)
3096 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3098 WCHAR *deformated;
3099 message = MSI_RecordGetString(row, 2);
3100 deformat_string(package, message, &deformated);
3101 MessageBoxW(NULL, deformated, L"Install Failed", MB_OK);
3102 msi_free(deformated);
3105 return ERROR_INSTALL_FAILURE;
3108 return ERROR_SUCCESS;
3111 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3113 MSIQUERY *view;
3114 UINT rc;
3116 TRACE("Checking launch conditions\n");
3118 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `LaunchCondition`", &view);
3119 if (rc != ERROR_SUCCESS)
3120 return ERROR_SUCCESS;
3122 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3123 msiobj_release(&view->hdr);
3124 return rc;
3127 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3130 if (!cmp->KeyPath)
3131 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3133 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3135 MSIRECORD *row;
3136 UINT root, len;
3137 LPWSTR deformated, buffer, deformated_name;
3138 LPCWSTR key, name;
3140 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `Registry` WHERE `Registry` = '%s'", cmp->KeyPath);
3141 if (!row)
3142 return NULL;
3144 root = MSI_RecordGetInteger(row,2);
3145 key = MSI_RecordGetString(row, 3);
3146 name = MSI_RecordGetString(row, 4);
3147 deformat_string(package, key , &deformated);
3148 deformat_string(package, name, &deformated_name);
3150 len = lstrlenW(deformated) + 6;
3151 if (deformated_name)
3152 len+=lstrlenW(deformated_name);
3154 buffer = msi_alloc( len *sizeof(WCHAR));
3156 if (deformated_name)
3157 swprintf(buffer, len, L"%02d:\\%s\\%s", root, deformated, deformated_name);
3158 else
3159 swprintf(buffer, len, L"%02d:\\%s\\", root, deformated);
3161 msi_free(deformated);
3162 msi_free(deformated_name);
3163 msiobj_release(&row->hdr);
3165 return buffer;
3167 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3169 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3170 return NULL;
3172 else
3174 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3176 if (file)
3177 return strdupW( file->TargetPath );
3179 return NULL;
3182 static HKEY open_shared_dlls_key( MSICOMPONENT *comp, BOOL create, REGSAM access )
3184 return open_key( comp, HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs",
3185 create, access );
3188 static UINT get_shared_dlls_count( MSICOMPONENT *comp )
3190 DWORD count, type, sz = sizeof(count);
3191 HKEY hkey = open_shared_dlls_key( comp, FALSE, KEY_READ );
3192 if (RegQueryValueExW( hkey, comp->FullKeypath, NULL, &type, (BYTE *)&count, &sz )) count = 0;
3193 RegCloseKey( hkey );
3194 return count;
3197 static void write_shared_dlls_count( MSICOMPONENT *comp, const WCHAR *path, INT count )
3199 HKEY hkey = open_shared_dlls_key( comp, TRUE, KEY_SET_VALUE );
3200 if (count > 0)
3201 msi_reg_set_val_dword( hkey, path, count );
3202 else
3203 RegDeleteValueW( hkey, path );
3204 RegCloseKey(hkey);
3207 static void refcount_component( MSIPACKAGE *package, MSICOMPONENT *comp )
3209 MSIFEATURE *feature;
3210 INT count = 0;
3211 BOOL write = FALSE;
3213 /* only refcount DLLs */
3214 if (!comp->KeyPath || comp->assembly || comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3215 comp->Attributes & msidbComponentAttributesODBCDataSource)
3216 write = FALSE;
3217 else
3219 count = get_shared_dlls_count( comp );
3220 write = (count > 0);
3221 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3222 write = TRUE;
3225 /* increment counts */
3226 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3228 ComponentList *cl;
3230 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3231 continue;
3233 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3235 if ( cl->component == comp )
3236 count++;
3240 /* decrement counts */
3241 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3243 ComponentList *cl;
3245 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3246 continue;
3248 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3250 if ( cl->component == comp )
3251 count--;
3255 /* ref count all the files in the component */
3256 if (write)
3258 MSIFILE *file;
3260 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3262 if (file->Component == comp)
3263 write_shared_dlls_count( comp, file->TargetPath, count );
3267 /* add a count for permanent */
3268 if (comp->Attributes & msidbComponentAttributesPermanent)
3269 count ++;
3271 comp->RefCount = count;
3273 if (write)
3274 write_shared_dlls_count( comp, comp->FullKeypath, comp->RefCount );
3277 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3279 if (comp->assembly)
3281 DWORD len = lstrlenW( L"<\\" ) + lstrlenW( comp->assembly->display_name );
3282 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3284 if (keypath)
3286 lstrcpyW( keypath, L"<\\" );
3287 lstrcatW( keypath, comp->assembly->display_name );
3289 return keypath;
3291 return resolve_keypath( package, comp );
3294 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3296 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3297 UINT rc;
3298 MSICOMPONENT *comp;
3299 HKEY hkey;
3301 TRACE("\n");
3303 msi_set_sourcedir_props(package, FALSE);
3305 if (package->script == SCRIPT_NONE)
3306 return msi_schedule_action(package, SCRIPT_INSTALL, L"ProcessComponents");
3308 squash_guid( package->ProductCode, squashed_pc );
3310 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3312 MSIRECORD *uirow;
3313 INSTALLSTATE action;
3315 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3316 if (!comp->ComponentId)
3317 continue;
3319 squash_guid( comp->ComponentId, squashed_cc );
3320 msi_free( comp->FullKeypath );
3321 comp->FullKeypath = build_full_keypath( package, comp );
3323 refcount_component( package, comp );
3325 if (package->need_rollback) action = comp->Installed;
3326 else action = comp->ActionRequest;
3328 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3329 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3330 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3332 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3334 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3335 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, L"S-1-5-18", &hkey, TRUE);
3336 else
3337 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3339 if (rc != ERROR_SUCCESS)
3340 continue;
3342 if (comp->Attributes & msidbComponentAttributesPermanent)
3344 msi_reg_set_val_str(hkey, L"00000000000000000000000000000000", comp->FullKeypath);
3346 if (action == INSTALLSTATE_LOCAL)
3347 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3348 else
3350 MSIFILE *file;
3351 MSIRECORD *row;
3352 LPWSTR ptr, ptr2;
3353 WCHAR source[MAX_PATH];
3354 WCHAR base[MAX_PATH];
3355 LPWSTR sourcepath;
3357 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3358 continue;
3360 if (!(row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `Media` WHERE `LastSequence` >= %d "
3361 "ORDER BY `DiskId`", file->Sequence)))
3362 return ERROR_FUNCTION_FAILED;
3364 swprintf(source, ARRAY_SIZE(source), L"%02d\\", MSI_RecordGetInteger(row, 1));
3365 ptr2 = wcsrchr(source, '\\') + 1;
3366 msiobj_release(&row->hdr);
3368 lstrcpyW(base, package->PackagePath);
3369 ptr = wcsrchr(base, '\\');
3370 *(ptr + 1) = '\0';
3372 sourcepath = msi_resolve_file_source(package, file);
3373 ptr = sourcepath + lstrlenW(base);
3374 lstrcpyW(ptr2, ptr);
3375 msi_free(sourcepath);
3377 msi_reg_set_val_str( hkey, squashed_pc, source );
3379 RegCloseKey(hkey);
3381 else if (action == INSTALLSTATE_ABSENT)
3383 if (comp->num_clients <= 0)
3385 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3386 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, L"S-1-5-18" );
3387 else
3388 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3390 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3392 else
3394 LONG res;
3396 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3397 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, L"S-1-5-18", &hkey, FALSE );
3398 else
3399 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3401 if (rc != ERROR_SUCCESS)
3403 WARN( "failed to open component key %u\n", rc );
3404 continue;
3406 res = RegDeleteValueW( hkey, squashed_pc );
3407 RegCloseKey(hkey);
3408 if (res) WARN( "failed to delete component value %d\n", res );
3412 /* UI stuff */
3413 uirow = MSI_CreateRecord(3);
3414 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3415 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3416 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3417 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3418 msiobj_release( &uirow->hdr );
3420 return ERROR_SUCCESS;
3423 typedef struct {
3424 CLSID clsid;
3425 LPWSTR source;
3427 LPWSTR path;
3428 ITypeLib *ptLib;
3429 } typelib_struct;
3431 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3432 LPWSTR lpszName, LONG_PTR lParam)
3434 TLIBATTR *attr;
3435 typelib_struct *tl_struct = (typelib_struct*) lParam;
3436 int sz;
3437 HRESULT res;
3439 if (!IS_INTRESOURCE(lpszName))
3441 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3442 return TRUE;
3445 sz = lstrlenW(tl_struct->source)+4;
3447 if ((INT_PTR)lpszName == 1)
3448 tl_struct->path = strdupW(tl_struct->source);
3449 else
3451 tl_struct->path = msi_alloc(sz * sizeof(WCHAR));
3452 swprintf(tl_struct->path, sz, L"%s\\%d", tl_struct->source, lpszName);
3455 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3456 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3457 if (FAILED(res))
3459 msi_free(tl_struct->path);
3460 tl_struct->path = NULL;
3462 return TRUE;
3465 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3466 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3468 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3469 return FALSE;
3472 msi_free(tl_struct->path);
3473 tl_struct->path = NULL;
3475 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3476 ITypeLib_Release(tl_struct->ptLib);
3478 return TRUE;
3481 static HMODULE msi_load_library( MSIPACKAGE *package, const WCHAR *filename, DWORD flags )
3483 HMODULE module;
3484 msi_disable_fs_redirection( package );
3485 module = LoadLibraryExW( filename, NULL, flags );
3486 msi_revert_fs_redirection( package );
3487 return module;
3490 static HRESULT msi_load_typelib( MSIPACKAGE *package, const WCHAR *filename, REGKIND kind, ITypeLib **lib )
3492 HRESULT hr;
3493 msi_disable_fs_redirection( package );
3494 hr = LoadTypeLibEx( filename, kind, lib );
3495 msi_revert_fs_redirection( package );
3496 return hr;
3499 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3501 MSIPACKAGE* package = param;
3502 LPCWSTR component;
3503 MSICOMPONENT *comp;
3504 MSIFILE *file;
3505 typelib_struct tl_struct;
3506 ITypeLib *tlib;
3507 HMODULE module;
3508 HRESULT hr;
3510 component = MSI_RecordGetString(row,3);
3511 comp = msi_get_loaded_component(package,component);
3512 if (!comp)
3513 return ERROR_SUCCESS;
3515 comp->Action = msi_get_component_action( package, comp );
3516 if (comp->Action != INSTALLSTATE_LOCAL)
3518 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3519 return ERROR_SUCCESS;
3522 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3524 TRACE("component has no key path\n");
3525 return ERROR_SUCCESS;
3527 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3529 module = msi_load_library( package, file->TargetPath, LOAD_LIBRARY_AS_DATAFILE );
3530 if (module)
3532 LPCWSTR guid;
3533 guid = MSI_RecordGetString(row,1);
3534 CLSIDFromString( guid, &tl_struct.clsid);
3535 tl_struct.source = strdupW( file->TargetPath );
3536 tl_struct.path = NULL;
3538 EnumResourceNamesW(module, L"TYPELIB", Typelib_EnumResNameProc,
3539 (LONG_PTR)&tl_struct);
3541 if (tl_struct.path)
3543 LPCWSTR helpid, help_path = NULL;
3544 HRESULT res;
3546 helpid = MSI_RecordGetString(row,6);
3548 if (helpid) help_path = msi_get_target_folder( package, helpid );
3549 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3551 if (FAILED(res))
3552 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3553 else
3554 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3556 ITypeLib_Release(tl_struct.ptLib);
3557 msi_free(tl_struct.path);
3559 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3561 FreeLibrary(module);
3562 msi_free(tl_struct.source);
3564 else
3566 hr = msi_load_typelib( package, file->TargetPath, REGKIND_REGISTER, &tlib );
3567 if (FAILED(hr))
3569 ERR("Failed to load type library: %08x\n", hr);
3570 return ERROR_INSTALL_FAILURE;
3573 ITypeLib_Release(tlib);
3576 return ERROR_SUCCESS;
3579 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3581 MSIQUERY *view;
3582 UINT rc;
3584 if (package->script == SCRIPT_NONE)
3585 return msi_schedule_action(package, SCRIPT_INSTALL, L"RegisterTypeLibraries");
3587 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `TypeLib`", &view);
3588 if (rc != ERROR_SUCCESS)
3589 return ERROR_SUCCESS;
3591 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3592 msiobj_release(&view->hdr);
3593 return rc;
3596 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3598 MSIPACKAGE *package = param;
3599 LPCWSTR component, guid;
3600 MSICOMPONENT *comp;
3601 GUID libid;
3602 UINT version;
3603 LCID language;
3604 SYSKIND syskind;
3605 HRESULT hr;
3607 component = MSI_RecordGetString( row, 3 );
3608 comp = msi_get_loaded_component( package, component );
3609 if (!comp)
3610 return ERROR_SUCCESS;
3612 comp->Action = msi_get_component_action( package, comp );
3613 if (comp->Action != INSTALLSTATE_ABSENT)
3615 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3616 return ERROR_SUCCESS;
3618 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3620 guid = MSI_RecordGetString( row, 1 );
3621 CLSIDFromString( guid, &libid );
3622 version = MSI_RecordGetInteger( row, 4 );
3623 language = MSI_RecordGetInteger( row, 2 );
3625 #ifdef _WIN64
3626 syskind = SYS_WIN64;
3627 #else
3628 syskind = SYS_WIN32;
3629 #endif
3631 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3632 if (FAILED(hr))
3634 WARN("Failed to unregister typelib: %08x\n", hr);
3637 return ERROR_SUCCESS;
3640 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3642 MSIQUERY *view;
3643 UINT rc;
3645 if (package->script == SCRIPT_NONE)
3646 return msi_schedule_action(package, SCRIPT_INSTALL, L"UnregisterTypeLibraries");
3648 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `TypeLib`", &view );
3649 if (rc != ERROR_SUCCESS)
3650 return ERROR_SUCCESS;
3652 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3653 msiobj_release( &view->hdr );
3654 return rc;
3657 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3659 LPCWSTR directory, extension, link_folder;
3660 LPWSTR link_file, filename;
3662 directory = MSI_RecordGetString( row, 2 );
3663 link_folder = msi_get_target_folder( package, directory );
3664 if (!link_folder)
3666 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3667 return NULL;
3669 /* may be needed because of a bug somewhere else */
3670 msi_create_full_path( package, link_folder );
3672 filename = msi_dup_record_field( row, 3 );
3673 msi_reduce_to_long_filename( filename );
3675 extension = wcsrchr( filename, '.' );
3676 if (!extension || wcsicmp( extension, L".lnk" ))
3678 int len = lstrlenW( filename );
3679 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(L".lnk") );
3680 memcpy( filename + len, L".lnk", sizeof(L".lnk") );
3682 link_file = msi_build_directory_name( 2, link_folder, filename );
3683 msi_free( filename );
3685 return link_file;
3688 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3690 WCHAR *folder, *dest, *path;
3692 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3693 folder = msi_dup_property( package->db, L"WindowsFolder" );
3694 else
3696 WCHAR *appdata = msi_dup_property( package->db, L"AppDataFolder" );
3697 folder = msi_build_directory_name( 2, appdata, L"Microsoft\\" );
3698 msi_free( appdata );
3700 dest = msi_build_directory_name( 3, folder, L"Installer\\", package->ProductCode );
3701 msi_create_full_path( package, dest );
3702 path = msi_build_directory_name( 2, dest, icon_name );
3703 msi_free( folder );
3704 msi_free( dest );
3705 return path;
3708 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3710 MSIPACKAGE *package = param;
3711 LPWSTR link_file, deformated, path;
3712 LPCWSTR component, target;
3713 MSICOMPONENT *comp;
3714 IShellLinkW *sl = NULL;
3715 IPersistFile *pf = NULL;
3716 HRESULT res;
3718 component = MSI_RecordGetString(row, 4);
3719 comp = msi_get_loaded_component(package, component);
3720 if (!comp)
3721 return ERROR_SUCCESS;
3723 comp->Action = msi_get_component_action( package, comp );
3724 if (comp->Action != INSTALLSTATE_LOCAL)
3726 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3727 return ERROR_SUCCESS;
3729 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3731 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3732 &IID_IShellLinkW, (LPVOID *) &sl );
3734 if (FAILED( res ))
3736 ERR("CLSID_ShellLink not available\n");
3737 goto err;
3740 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3741 if (FAILED( res ))
3743 ERR("QueryInterface(IID_IPersistFile) failed\n");
3744 goto err;
3747 target = MSI_RecordGetString(row, 5);
3748 if (wcschr(target, '['))
3750 deformat_string( package, target, &path );
3751 TRACE("target path is %s\n", debugstr_w(path));
3752 IShellLinkW_SetPath( sl, path );
3753 msi_free( path );
3755 else
3757 FIXME("poorly handled shortcut format, advertised shortcut\n");
3758 path = resolve_keypath( package, comp );
3759 IShellLinkW_SetPath( sl, path );
3760 msi_free( path );
3763 if (!MSI_RecordIsNull(row,6))
3765 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3766 deformat_string(package, arguments, &deformated);
3767 IShellLinkW_SetArguments(sl,deformated);
3768 msi_free(deformated);
3771 if (!MSI_RecordIsNull(row,7))
3773 LPCWSTR description = MSI_RecordGetString(row, 7);
3774 IShellLinkW_SetDescription(sl, description);
3777 if (!MSI_RecordIsNull(row,8))
3778 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3780 if (!MSI_RecordIsNull(row,9))
3782 INT index;
3783 LPCWSTR icon = MSI_RecordGetString(row, 9);
3785 path = msi_build_icon_path(package, icon);
3786 index = MSI_RecordGetInteger(row,10);
3788 /* no value means 0 */
3789 if (index == MSI_NULL_INTEGER)
3790 index = 0;
3792 IShellLinkW_SetIconLocation(sl, path, index);
3793 msi_free(path);
3796 if (!MSI_RecordIsNull(row,11))
3797 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3799 if (!MSI_RecordIsNull(row,12))
3801 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3802 full_path = msi_get_target_folder( package, wkdir );
3803 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3806 link_file = get_link_file(package, row);
3807 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3809 msi_disable_fs_redirection( package );
3810 IPersistFile_Save(pf, link_file, FALSE);
3811 msi_revert_fs_redirection( package );
3813 msi_free(link_file);
3815 err:
3816 if (pf)
3817 IPersistFile_Release( pf );
3818 if (sl)
3819 IShellLinkW_Release( sl );
3821 return ERROR_SUCCESS;
3824 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3826 MSIQUERY *view;
3827 HRESULT res;
3828 UINT rc;
3830 if (package->script == SCRIPT_NONE)
3831 return msi_schedule_action(package, SCRIPT_INSTALL, L"CreateShortcuts");
3833 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Shortcut`", &view);
3834 if (rc != ERROR_SUCCESS)
3835 return ERROR_SUCCESS;
3837 res = CoInitialize( NULL );
3839 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3840 msiobj_release(&view->hdr);
3842 if (SUCCEEDED(res)) CoUninitialize();
3843 return rc;
3846 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3848 MSIPACKAGE *package = param;
3849 LPWSTR link_file;
3850 LPCWSTR component;
3851 MSICOMPONENT *comp;
3853 component = MSI_RecordGetString( row, 4 );
3854 comp = msi_get_loaded_component( package, component );
3855 if (!comp)
3856 return ERROR_SUCCESS;
3858 comp->Action = msi_get_component_action( package, comp );
3859 if (comp->Action != INSTALLSTATE_ABSENT)
3861 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3862 return ERROR_SUCCESS;
3864 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3866 link_file = get_link_file( package, row );
3867 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3868 if (!msi_delete_file( package, link_file )) WARN("Failed to remove shortcut file %u\n", GetLastError());
3869 msi_free( link_file );
3871 return ERROR_SUCCESS;
3874 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3876 MSIQUERY *view;
3877 UINT rc;
3879 if (package->script == SCRIPT_NONE)
3880 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveShortcuts");
3882 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Shortcut`", &view );
3883 if (rc != ERROR_SUCCESS)
3884 return ERROR_SUCCESS;
3886 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3887 msiobj_release( &view->hdr );
3888 return rc;
3891 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3893 MSIPACKAGE *package = param;
3894 HANDLE handle;
3895 WCHAR *icon_path;
3896 const WCHAR *filename;
3897 char buffer[1024];
3898 DWORD sz;
3899 UINT rc;
3901 filename = MSI_RecordGetString( row, 1 );
3902 if (!filename)
3904 ERR("Unable to get filename\n");
3905 return ERROR_SUCCESS;
3908 icon_path = msi_build_icon_path( package, filename );
3910 TRACE("Creating icon file at %s\n", debugstr_w(icon_path));
3912 handle = msi_create_file( package, icon_path, GENERIC_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL );
3913 if (handle == INVALID_HANDLE_VALUE)
3915 ERR("Unable to create file %s\n", debugstr_w(icon_path));
3916 msi_free( icon_path );
3917 return ERROR_SUCCESS;
3922 DWORD count;
3923 sz = 1024;
3924 rc = MSI_RecordReadStream( row, 2, buffer, &sz );
3925 if (rc != ERROR_SUCCESS)
3927 ERR("Failed to get stream\n");
3928 msi_delete_file( package, icon_path );
3929 break;
3931 WriteFile( handle, buffer, sz, &count, NULL );
3932 } while (sz == 1024);
3934 msi_free( icon_path );
3935 CloseHandle( handle );
3937 return ERROR_SUCCESS;
3940 static UINT msi_publish_icons(MSIPACKAGE *package)
3942 MSIQUERY *view;
3943 UINT r;
3945 r = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Icon`", &view);
3946 if (r == ERROR_SUCCESS)
3948 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3949 msiobj_release(&view->hdr);
3950 if (r != ERROR_SUCCESS)
3951 return r;
3953 return ERROR_SUCCESS;
3956 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3958 UINT r;
3959 HKEY source;
3960 LPWSTR buffer;
3961 MSIMEDIADISK *disk;
3962 MSISOURCELISTINFO *info;
3964 r = RegCreateKeyW(hkey, L"SourceList", &source);
3965 if (r != ERROR_SUCCESS)
3966 return r;
3968 RegCloseKey(source);
3970 buffer = wcsrchr(package->PackagePath, '\\') + 1;
3971 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3972 package->Context, MSICODE_PRODUCT,
3973 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3974 if (r != ERROR_SUCCESS)
3975 return r;
3977 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3978 package->Context, MSICODE_PRODUCT,
3979 INSTALLPROPERTY_MEDIAPACKAGEPATHW, L"");
3980 if (r != ERROR_SUCCESS)
3981 return r;
3983 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3984 package->Context, MSICODE_PRODUCT,
3985 INSTALLPROPERTY_DISKPROMPTW, L"");
3986 if (r != ERROR_SUCCESS)
3987 return r;
3989 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3991 if (!wcscmp( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3992 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3993 info->options, info->value);
3994 else
3995 MsiSourceListSetInfoW(package->ProductCode, NULL,
3996 info->context, info->options,
3997 info->property, info->value);
4000 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4002 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4003 disk->context, disk->options,
4004 disk->disk_id, disk->volume_label, disk->disk_prompt);
4007 return ERROR_SUCCESS;
4010 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4012 WCHAR *buffer, *ptr, *guids, packcode[SQUASHED_GUID_SIZE];
4013 DWORD langid;
4015 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4016 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4017 msi_free(buffer);
4019 langid = msi_get_property_int(package->db, L"ProductLanguage", 0);
4020 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4022 /* FIXME */
4023 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4025 buffer = msi_dup_property(package->db, L"ARPPRODUCTICON");
4026 if (buffer)
4028 LPWSTR path = msi_build_icon_path(package, buffer);
4029 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4030 msi_free(path);
4031 msi_free(buffer);
4034 buffer = msi_dup_property(package->db, L"ProductVersion");
4035 if (buffer)
4037 DWORD verdword = msi_version_str_to_dword(buffer);
4038 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4039 msi_free(buffer);
4042 msi_reg_set_val_dword(hkey, L"Assignment", 0);
4043 msi_reg_set_val_dword(hkey, L"AdvertiseFlags", 0x184);
4044 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4045 msi_reg_set_val_multi_str(hkey, L"Clients", L":\0");
4047 if (!(guids = msi_get_package_code(package->db))) return ERROR_OUTOFMEMORY;
4048 if ((ptr = wcschr(guids, ';'))) *ptr = 0;
4049 squash_guid(guids, packcode);
4050 msi_free( guids);
4051 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4053 return ERROR_SUCCESS;
4056 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4058 UINT r;
4059 HKEY hkey;
4060 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4062 upgrade = msi_dup_property(package->db, L"UpgradeCode");
4063 if (!upgrade)
4064 return ERROR_SUCCESS;
4066 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4067 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4068 else
4069 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4071 if (r != ERROR_SUCCESS)
4073 WARN("failed to open upgrade code key\n");
4074 msi_free(upgrade);
4075 return ERROR_SUCCESS;
4077 squash_guid(package->ProductCode, squashed_pc);
4078 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4079 RegCloseKey(hkey);
4080 msi_free(upgrade);
4081 return ERROR_SUCCESS;
4084 static BOOL msi_check_publish(MSIPACKAGE *package)
4086 MSIFEATURE *feature;
4088 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4090 feature->Action = msi_get_feature_action( package, feature );
4091 if (feature->Action == INSTALLSTATE_LOCAL || feature->Action == INSTALLSTATE_SOURCE)
4092 return TRUE;
4095 return FALSE;
4098 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4100 MSIFEATURE *feature;
4102 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4104 feature->Action = msi_get_feature_action( package, feature );
4105 if (feature->Action != INSTALLSTATE_ABSENT)
4106 return FALSE;
4109 return TRUE;
4112 static UINT msi_publish_patches( MSIPACKAGE *package )
4114 WCHAR patch_squashed[GUID_SIZE];
4115 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4116 LONG res;
4117 MSIPATCHINFO *patch;
4118 UINT r;
4119 WCHAR *p, *all_patches = NULL;
4120 DWORD len = 0;
4122 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4123 if (r != ERROR_SUCCESS)
4124 return ERROR_FUNCTION_FAILED;
4126 res = RegCreateKeyExW( product_key, L"Patches", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4127 if (res != ERROR_SUCCESS)
4129 r = ERROR_FUNCTION_FAILED;
4130 goto done;
4133 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4134 if (r != ERROR_SUCCESS)
4135 goto done;
4137 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4139 squash_guid( patch->patchcode, patch_squashed );
4140 len += lstrlenW( patch_squashed ) + 1;
4143 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4144 if (!all_patches)
4145 goto done;
4147 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4149 HKEY patch_key;
4151 squash_guid( patch->patchcode, p );
4152 p += lstrlenW( p ) + 1;
4154 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4155 (const BYTE *)patch->transforms,
4156 (lstrlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4157 if (res != ERROR_SUCCESS)
4158 goto done;
4160 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4161 if (r != ERROR_SUCCESS)
4162 goto done;
4164 res = RegSetValueExW( patch_key, L"LocalPackage", 0, REG_SZ, (const BYTE *)patch->localfile,
4165 (lstrlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4166 RegCloseKey( patch_key );
4167 if (res != ERROR_SUCCESS)
4168 goto done;
4170 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4172 res = GetLastError();
4173 ERR("Unable to copy patch package %d\n", res);
4174 goto done;
4176 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4177 if (res != ERROR_SUCCESS)
4178 goto done;
4180 res = RegSetValueExW( patch_key, L"State", 0, REG_DWORD, (const BYTE *)&patch->state,
4181 sizeof(patch->state) );
4182 if (res != ERROR_SUCCESS)
4184 RegCloseKey( patch_key );
4185 goto done;
4188 res = RegSetValueExW( patch_key, L"Uninstallable", 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
4189 sizeof(patch->uninstallable) );
4190 RegCloseKey( patch_key );
4191 if (res != ERROR_SUCCESS)
4192 goto done;
4195 all_patches[len] = 0;
4196 res = RegSetValueExW( patches_key, L"Patches", 0, REG_MULTI_SZ,
4197 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4198 if (res != ERROR_SUCCESS)
4199 goto done;
4201 res = RegSetValueExW( product_patches_key, L"AllPatches", 0, REG_MULTI_SZ,
4202 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4203 if (res != ERROR_SUCCESS)
4204 r = ERROR_FUNCTION_FAILED;
4206 done:
4207 RegCloseKey( product_patches_key );
4208 RegCloseKey( patches_key );
4209 RegCloseKey( product_key );
4210 msi_free( all_patches );
4211 return r;
4214 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4216 UINT rc;
4217 HKEY hukey = NULL, hudkey = NULL;
4218 MSIRECORD *uirow;
4219 BOOL republish = FALSE;
4221 if (package->script == SCRIPT_NONE)
4222 return msi_schedule_action(package, SCRIPT_INSTALL, L"PublishProduct");
4224 if (!list_empty(&package->patches))
4226 rc = msi_publish_patches(package);
4227 if (rc != ERROR_SUCCESS)
4228 goto end;
4231 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4232 &hukey, FALSE);
4233 if (rc == ERROR_SUCCESS)
4235 WCHAR *package_code;
4237 package_code = msi_reg_get_val_str(hukey, INSTALLPROPERTY_PACKAGECODEW);
4238 if (package_code)
4240 WCHAR *guid;
4242 guid = msi_get_package_code(package->db);
4243 if (guid)
4245 WCHAR packed[SQUASHED_GUID_SIZE];
4247 squash_guid(guid, packed);
4248 msi_free(guid);
4249 if (!wcscmp(packed, package_code))
4251 TRACE("re-publishing product - new package\n");
4252 republish = TRUE;
4255 msi_free(package_code);
4259 /* FIXME: also need to publish if the product is in advertise mode */
4260 if (!republish && !msi_check_publish(package))
4262 if (hukey)
4263 RegCloseKey(hukey);
4264 return ERROR_SUCCESS;
4267 if (!hukey)
4269 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4270 &hukey, TRUE);
4271 if (rc != ERROR_SUCCESS)
4272 goto end;
4275 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4276 NULL, &hudkey, TRUE);
4277 if (rc != ERROR_SUCCESS)
4278 goto end;
4280 rc = msi_publish_upgrade_code(package);
4281 if (rc != ERROR_SUCCESS)
4282 goto end;
4284 rc = msi_publish_product_properties(package, hukey);
4285 if (rc != ERROR_SUCCESS)
4286 goto end;
4288 rc = msi_publish_sourcelist(package, hukey);
4289 if (rc != ERROR_SUCCESS)
4290 goto end;
4292 rc = msi_publish_icons(package);
4294 end:
4295 uirow = MSI_CreateRecord( 1 );
4296 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4297 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4298 msiobj_release( &uirow->hdr );
4300 RegCloseKey(hukey);
4301 RegCloseKey(hudkey);
4302 return rc;
4305 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4307 WCHAR *filename, *ptr, *folder, *ret;
4308 const WCHAR *dirprop;
4310 filename = msi_dup_record_field( row, 2 );
4311 if (filename && (ptr = wcschr( filename, '|' )))
4312 ptr++;
4313 else
4314 ptr = filename;
4316 dirprop = MSI_RecordGetString( row, 3 );
4317 if (dirprop)
4319 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4320 if (!folder) folder = msi_dup_property( package->db, dirprop );
4322 else
4323 folder = msi_dup_property( package->db, L"WindowsFolder" );
4325 if (!folder)
4327 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4328 msi_free( filename );
4329 return NULL;
4332 ret = msi_build_directory_name( 2, folder, ptr );
4334 msi_free( filename );
4335 msi_free( folder );
4336 return ret;
4339 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4341 MSIPACKAGE *package = param;
4342 LPCWSTR component, section, key, value, identifier;
4343 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4344 MSIRECORD * uirow;
4345 INT action;
4346 MSICOMPONENT *comp;
4348 component = MSI_RecordGetString(row, 8);
4349 comp = msi_get_loaded_component(package,component);
4350 if (!comp)
4351 return ERROR_SUCCESS;
4353 comp->Action = msi_get_component_action( package, comp );
4354 if (comp->Action != INSTALLSTATE_LOCAL)
4356 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4357 return ERROR_SUCCESS;
4360 identifier = MSI_RecordGetString(row,1);
4361 section = MSI_RecordGetString(row,4);
4362 key = MSI_RecordGetString(row,5);
4363 value = MSI_RecordGetString(row,6);
4364 action = MSI_RecordGetInteger(row,7);
4366 deformat_string(package,section,&deformated_section);
4367 deformat_string(package,key,&deformated_key);
4368 deformat_string(package,value,&deformated_value);
4370 fullname = get_ini_file_name(package, row);
4372 if (action == 0)
4374 TRACE("Adding value %s to section %s in %s\n",
4375 debugstr_w(deformated_key), debugstr_w(deformated_section),
4376 debugstr_w(fullname));
4377 WritePrivateProfileStringW(deformated_section, deformated_key,
4378 deformated_value, fullname);
4380 else if (action == 1)
4382 WCHAR returned[10];
4383 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4384 returned, 10, fullname);
4385 if (returned[0] == 0)
4387 TRACE("Adding value %s to section %s in %s\n",
4388 debugstr_w(deformated_key), debugstr_w(deformated_section),
4389 debugstr_w(fullname));
4391 WritePrivateProfileStringW(deformated_section, deformated_key,
4392 deformated_value, fullname);
4395 else if (action == 3)
4396 FIXME("Append to existing section not yet implemented\n");
4398 uirow = MSI_CreateRecord(4);
4399 MSI_RecordSetStringW(uirow,1,identifier);
4400 MSI_RecordSetStringW(uirow,2,deformated_section);
4401 MSI_RecordSetStringW(uirow,3,deformated_key);
4402 MSI_RecordSetStringW(uirow,4,deformated_value);
4403 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4404 msiobj_release( &uirow->hdr );
4406 msi_free(fullname);
4407 msi_free(deformated_key);
4408 msi_free(deformated_value);
4409 msi_free(deformated_section);
4410 return ERROR_SUCCESS;
4413 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4415 MSIQUERY *view;
4416 UINT rc;
4418 if (package->script == SCRIPT_NONE)
4419 return msi_schedule_action(package, SCRIPT_INSTALL, L"WriteIniValues");
4421 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `IniFile`", &view);
4422 if (rc != ERROR_SUCCESS)
4423 return ERROR_SUCCESS;
4425 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4426 msiobj_release(&view->hdr);
4427 return rc;
4430 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4432 MSIPACKAGE *package = param;
4433 LPCWSTR component, section, key, value, identifier;
4434 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4435 MSICOMPONENT *comp;
4436 MSIRECORD *uirow;
4437 INT action;
4439 component = MSI_RecordGetString( row, 8 );
4440 comp = msi_get_loaded_component( package, component );
4441 if (!comp)
4442 return ERROR_SUCCESS;
4444 comp->Action = msi_get_component_action( package, comp );
4445 if (comp->Action != INSTALLSTATE_ABSENT)
4447 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4448 return ERROR_SUCCESS;
4451 identifier = MSI_RecordGetString( row, 1 );
4452 section = MSI_RecordGetString( row, 4 );
4453 key = MSI_RecordGetString( row, 5 );
4454 value = MSI_RecordGetString( row, 6 );
4455 action = MSI_RecordGetInteger( row, 7 );
4457 deformat_string( package, section, &deformated_section );
4458 deformat_string( package, key, &deformated_key );
4459 deformat_string( package, value, &deformated_value );
4461 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4463 filename = get_ini_file_name( package, row );
4465 TRACE("Removing key %s from section %s in %s\n",
4466 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4468 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4470 WARN("Unable to remove key %u\n", GetLastError());
4472 msi_free( filename );
4474 else
4475 FIXME("Unsupported action %d\n", action);
4478 uirow = MSI_CreateRecord( 4 );
4479 MSI_RecordSetStringW( uirow, 1, identifier );
4480 MSI_RecordSetStringW( uirow, 2, deformated_section );
4481 MSI_RecordSetStringW( uirow, 3, deformated_key );
4482 MSI_RecordSetStringW( uirow, 4, deformated_value );
4483 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4484 msiobj_release( &uirow->hdr );
4486 msi_free( deformated_key );
4487 msi_free( deformated_value );
4488 msi_free( deformated_section );
4489 return ERROR_SUCCESS;
4492 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4494 MSIPACKAGE *package = param;
4495 LPCWSTR component, section, key, value, identifier;
4496 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4497 MSICOMPONENT *comp;
4498 MSIRECORD *uirow;
4499 INT action;
4501 component = MSI_RecordGetString( row, 8 );
4502 comp = msi_get_loaded_component( package, component );
4503 if (!comp)
4504 return ERROR_SUCCESS;
4506 comp->Action = msi_get_component_action( package, comp );
4507 if (comp->Action != INSTALLSTATE_LOCAL)
4509 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4510 return ERROR_SUCCESS;
4513 identifier = MSI_RecordGetString( row, 1 );
4514 section = MSI_RecordGetString( row, 4 );
4515 key = MSI_RecordGetString( row, 5 );
4516 value = MSI_RecordGetString( row, 6 );
4517 action = MSI_RecordGetInteger( row, 7 );
4519 deformat_string( package, section, &deformated_section );
4520 deformat_string( package, key, &deformated_key );
4521 deformat_string( package, value, &deformated_value );
4523 if (action == msidbIniFileActionRemoveLine)
4525 filename = get_ini_file_name( package, row );
4527 TRACE("Removing key %s from section %s in %s\n",
4528 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4530 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4532 WARN("Unable to remove key %u\n", GetLastError());
4534 msi_free( filename );
4536 else
4537 FIXME("Unsupported action %d\n", action);
4539 uirow = MSI_CreateRecord( 4 );
4540 MSI_RecordSetStringW( uirow, 1, identifier );
4541 MSI_RecordSetStringW( uirow, 2, deformated_section );
4542 MSI_RecordSetStringW( uirow, 3, deformated_key );
4543 MSI_RecordSetStringW( uirow, 4, deformated_value );
4544 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4545 msiobj_release( &uirow->hdr );
4547 msi_free( deformated_key );
4548 msi_free( deformated_value );
4549 msi_free( deformated_section );
4550 return ERROR_SUCCESS;
4553 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4555 MSIQUERY *view;
4556 UINT rc;
4558 if (package->script == SCRIPT_NONE)
4559 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveIniValues");
4561 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `IniFile`", &view );
4562 if (rc == ERROR_SUCCESS)
4564 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4565 msiobj_release( &view->hdr );
4566 if (rc != ERROR_SUCCESS)
4567 return rc;
4569 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `RemoveIniFile`", &view );
4570 if (rc == ERROR_SUCCESS)
4572 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4573 msiobj_release( &view->hdr );
4574 if (rc != ERROR_SUCCESS)
4575 return rc;
4577 return ERROR_SUCCESS;
4580 static void register_dll( const WCHAR *dll, BOOL unregister )
4582 static const WCHAR regW[] = L"regsvr32.exe \"%s\"";
4583 static const WCHAR unregW[] = L"regsvr32.exe /u \"%s\"";
4584 PROCESS_INFORMATION pi;
4585 STARTUPINFOW si;
4586 WCHAR *cmd;
4588 if (!(cmd = msi_alloc( lstrlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4590 if (unregister) swprintf( cmd, lstrlenW(dll) + ARRAY_SIZE(unregW), unregW, dll );
4591 else swprintf( cmd, lstrlenW(dll) + ARRAY_SIZE(unregW), regW, dll );
4593 memset( &si, 0, sizeof(STARTUPINFOW) );
4594 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4596 CloseHandle( pi.hThread );
4597 msi_dialog_check_messages( pi.hProcess );
4598 CloseHandle( pi.hProcess );
4600 msi_free( cmd );
4603 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4605 MSIPACKAGE *package = param;
4606 LPCWSTR filename;
4607 MSIFILE *file;
4608 MSIRECORD *uirow;
4610 filename = MSI_RecordGetString( row, 1 );
4611 file = msi_get_loaded_file( package, filename );
4612 if (!file)
4614 WARN("unable to find file %s\n", debugstr_w(filename));
4615 return ERROR_SUCCESS;
4617 file->Component->Action = msi_get_component_action( package, file->Component );
4618 if (file->Component->Action != INSTALLSTATE_LOCAL)
4620 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4621 return ERROR_SUCCESS;
4624 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4625 register_dll( file->TargetPath, FALSE );
4627 uirow = MSI_CreateRecord( 2 );
4628 MSI_RecordSetStringW( uirow, 1, file->File );
4629 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4630 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4631 msiobj_release( &uirow->hdr );
4633 return ERROR_SUCCESS;
4636 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4638 MSIQUERY *view;
4639 UINT rc;
4641 if (package->script == SCRIPT_NONE)
4642 return msi_schedule_action(package, SCRIPT_INSTALL, L"SelfRegModules");
4644 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `SelfReg`", &view);
4645 if (rc != ERROR_SUCCESS)
4646 return ERROR_SUCCESS;
4648 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4649 msiobj_release(&view->hdr);
4650 return rc;
4653 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4655 MSIPACKAGE *package = param;
4656 LPCWSTR filename;
4657 MSIFILE *file;
4658 MSIRECORD *uirow;
4660 filename = MSI_RecordGetString( row, 1 );
4661 file = msi_get_loaded_file( package, filename );
4662 if (!file)
4664 WARN("unable to find file %s\n", debugstr_w(filename));
4665 return ERROR_SUCCESS;
4667 file->Component->Action = msi_get_component_action( package, file->Component );
4668 if (file->Component->Action != INSTALLSTATE_ABSENT)
4670 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4671 return ERROR_SUCCESS;
4674 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4675 register_dll( file->TargetPath, TRUE );
4677 uirow = MSI_CreateRecord( 2 );
4678 MSI_RecordSetStringW( uirow, 1, file->File );
4679 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4680 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4681 msiobj_release( &uirow->hdr );
4683 return ERROR_SUCCESS;
4686 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4688 MSIQUERY *view;
4689 UINT rc;
4691 if (package->script == SCRIPT_NONE)
4692 return msi_schedule_action(package, SCRIPT_INSTALL, L"SelfUnregModules");
4694 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `SelfReg`", &view );
4695 if (rc != ERROR_SUCCESS)
4696 return ERROR_SUCCESS;
4698 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4699 msiobj_release( &view->hdr );
4700 return rc;
4703 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4705 MSIFEATURE *feature;
4706 UINT rc;
4707 HKEY hkey = NULL, userdata = NULL;
4709 if (package->script == SCRIPT_NONE)
4710 return msi_schedule_action(package, SCRIPT_INSTALL, L"PublishFeatures");
4712 if (!msi_check_publish(package))
4713 return ERROR_SUCCESS;
4715 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4716 &hkey, TRUE);
4717 if (rc != ERROR_SUCCESS)
4718 goto end;
4720 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4721 &userdata, TRUE);
4722 if (rc != ERROR_SUCCESS)
4723 goto end;
4725 /* here the guids are base 85 encoded */
4726 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4728 ComponentList *cl;
4729 LPWSTR data = NULL;
4730 GUID clsid;
4731 INT size;
4732 BOOL absent = FALSE;
4733 MSIRECORD *uirow;
4735 if (feature->Level <= 0) continue;
4736 if (feature->Action == INSTALLSTATE_UNKNOWN &&
4737 feature->Installed != INSTALLSTATE_ABSENT) continue;
4739 if (feature->Action != INSTALLSTATE_LOCAL &&
4740 feature->Action != INSTALLSTATE_SOURCE &&
4741 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4743 size = 1;
4744 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4746 size += 21;
4748 if (feature->Feature_Parent)
4749 size += lstrlenW( feature->Feature_Parent )+2;
4751 data = msi_alloc(size * sizeof(WCHAR));
4753 data[0] = 0;
4754 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4756 MSICOMPONENT* component = cl->component;
4757 WCHAR buf[21];
4759 buf[0] = 0;
4760 if (component->ComponentId)
4762 TRACE("From %s\n",debugstr_w(component->ComponentId));
4763 CLSIDFromString(component->ComponentId, &clsid);
4764 encode_base85_guid(&clsid,buf);
4765 TRACE("to %s\n",debugstr_w(buf));
4766 lstrcatW(data,buf);
4770 if (feature->Feature_Parent)
4772 lstrcatW(data, L"\2");
4773 lstrcatW(data, feature->Feature_Parent);
4776 msi_reg_set_val_str( userdata, feature->Feature, data );
4777 msi_free(data);
4779 size = 0;
4780 if (feature->Feature_Parent)
4781 size = lstrlenW(feature->Feature_Parent)*sizeof(WCHAR);
4782 if (!absent)
4784 size += sizeof(WCHAR);
4785 RegSetValueExW(hkey, feature->Feature, 0 ,REG_SZ,
4786 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : L""), size);
4788 else
4790 size += 2*sizeof(WCHAR);
4791 data = msi_alloc(size);
4792 data[0] = 0x6;
4793 data[1] = 0;
4794 if (feature->Feature_Parent)
4795 lstrcpyW( &data[1], feature->Feature_Parent );
4796 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4797 (LPBYTE)data,size);
4798 msi_free(data);
4801 /* the UI chunk */
4802 uirow = MSI_CreateRecord( 1 );
4803 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4804 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4805 msiobj_release( &uirow->hdr );
4806 /* FIXME: call msi_ui_progress? */
4809 end:
4810 RegCloseKey(hkey);
4811 RegCloseKey(userdata);
4812 return rc;
4815 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4817 UINT r;
4818 HKEY hkey;
4819 MSIRECORD *uirow;
4821 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4823 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4824 &hkey, FALSE);
4825 if (r == ERROR_SUCCESS)
4827 RegDeleteValueW(hkey, feature->Feature);
4828 RegCloseKey(hkey);
4831 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4832 &hkey, FALSE);
4833 if (r == ERROR_SUCCESS)
4835 RegDeleteValueW(hkey, feature->Feature);
4836 RegCloseKey(hkey);
4839 uirow = MSI_CreateRecord( 1 );
4840 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4841 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4842 msiobj_release( &uirow->hdr );
4844 return ERROR_SUCCESS;
4847 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4849 MSIFEATURE *feature;
4851 if (package->script == SCRIPT_NONE)
4852 return msi_schedule_action(package, SCRIPT_INSTALL, L"UnpublishFeatures");
4854 if (!msi_check_unpublish(package))
4855 return ERROR_SUCCESS;
4857 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4859 msi_unpublish_feature(package, feature);
4862 return ERROR_SUCCESS;
4865 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4867 static const WCHAR *propval[] =
4869 L"ARPAUTHORIZEDCDFPREFIX", L"AuthorizedCDFPrefix",
4870 L"ARPCONTACT", L"Contact",
4871 L"ARPCOMMENTS", L"Comments",
4872 L"ProductName", L"DisplayName",
4873 L"ARPHELPLINK", L"HelpLink",
4874 L"ARPHELPTELEPHONE", L"HelpTelephone",
4875 L"ARPINSTALLLOCATION", L"InstallLocation",
4876 L"SourceDir", L"InstallSource",
4877 L"Manufacturer", L"Publisher",
4878 L"ARPREADME", L"ReadMe",
4879 L"ARPSIZE", L"Size",
4880 L"ARPURLINFOABOUT", L"URLInfoAbout",
4881 L"ARPURLUPDATEINFO", L"URLUpdateInfo",
4882 NULL
4884 const WCHAR **p = propval;
4885 SYSTEMTIME systime;
4886 DWORD size, langid;
4887 WCHAR date[9], *val, *buffer;
4888 const WCHAR *prop, *key;
4890 while (*p)
4892 prop = *p++;
4893 key = *p++;
4894 val = msi_dup_property(package->db, prop);
4895 msi_reg_set_val_str(hkey, key, val);
4896 msi_free(val);
4899 msi_reg_set_val_dword(hkey, L"WindowsInstaller", 1);
4900 if (msi_get_property_int( package->db, L"ARPSYSTEMCOMPONENT", 0 ))
4902 msi_reg_set_val_dword( hkey, L"SystemComponent", 1 );
4905 if (msi_get_property_int( package->db, L"ARPNOREMOVE", 0 ))
4906 msi_reg_set_val_dword( hkey, L"NoRemove", 1 );
4907 else
4909 static const WCHAR fmt_install[] = L"MsiExec.exe /I[ProductCode]";
4910 static const WCHAR fmt_uninstall[] = L"MsiExec.exe /X[ProductCode]";
4911 const WCHAR *fmt = fmt_install;
4913 if (msi_get_property_int( package->db, L"ARPNOREPAIR", 0 ))
4914 msi_reg_set_val_dword( hkey, L"NoRepair", 1 );
4916 if (msi_get_property_int( package->db, L"ARPNOMODIFY", 0 ))
4918 msi_reg_set_val_dword( hkey, L"NoModify", 1 );
4919 fmt = fmt_uninstall;
4922 size = deformat_string(package, fmt, &buffer) * sizeof(WCHAR);
4923 RegSetValueExW(hkey, L"ModifyPath", 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4924 RegSetValueExW(hkey, L"UninstallString", 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4925 msi_free(buffer);
4928 /* FIXME: Write real Estimated Size when we have it */
4929 msi_reg_set_val_dword(hkey, L"EstimatedSize", 0);
4931 GetLocalTime(&systime);
4932 swprintf(date, ARRAY_SIZE(date), L"%d%02d%02d", systime.wYear, systime.wMonth, systime.wDay);
4933 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4935 langid = msi_get_property_int(package->db, L"ProductLanguage", 0);
4936 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4938 buffer = msi_dup_property(package->db, L"ProductVersion");
4939 msi_reg_set_val_str(hkey, L"DisplayVersion", buffer);
4940 if (buffer)
4942 DWORD verdword = msi_version_str_to_dword(buffer);
4944 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4945 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4946 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4947 msi_free(buffer);
4950 return ERROR_SUCCESS;
4953 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4955 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
4956 MSIRECORD *uirow;
4957 HKEY hkey, props, upgrade_key;
4958 UINT rc;
4960 if (package->script == SCRIPT_NONE)
4961 return msi_schedule_action(package, SCRIPT_INSTALL, L"RegisterProduct");
4963 /* FIXME: also need to publish if the product is in advertise mode */
4964 if (!msi_get_property_int( package->db, L"ProductToBeRegistered", 0 )
4965 && !msi_check_publish(package))
4966 return ERROR_SUCCESS;
4968 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4969 if (rc != ERROR_SUCCESS)
4970 return rc;
4972 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4973 if (rc != ERROR_SUCCESS)
4974 goto done;
4976 rc = msi_publish_install_properties(package, hkey);
4977 if (rc != ERROR_SUCCESS)
4978 goto done;
4980 rc = msi_publish_install_properties(package, props);
4981 if (rc != ERROR_SUCCESS)
4982 goto done;
4984 upgrade_code = msi_dup_property(package->db, L"UpgradeCode");
4985 if (upgrade_code)
4987 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4988 if (rc == ERROR_SUCCESS)
4990 squash_guid( package->ProductCode, squashed_pc );
4991 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4992 RegCloseKey( upgrade_key );
4994 msi_free( upgrade_code );
4996 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4997 package->delete_on_close = FALSE;
4999 done:
5000 uirow = MSI_CreateRecord( 1 );
5001 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5002 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5003 msiobj_release( &uirow->hdr );
5005 RegCloseKey(hkey);
5006 return ERROR_SUCCESS;
5009 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5011 return execute_script(package, SCRIPT_INSTALL);
5014 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5016 MSIPACKAGE *package = param;
5017 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5018 WCHAR *p, *icon_path;
5020 if (!icon) return ERROR_SUCCESS;
5021 if ((icon_path = msi_build_icon_path( package, icon )))
5023 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5024 msi_delete_file( package, icon_path );
5025 if ((p = wcsrchr( icon_path, '\\' )))
5027 *p = 0;
5028 msi_remove_directory( package, icon_path );
5030 msi_free( icon_path );
5032 return ERROR_SUCCESS;
5035 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5037 MSIQUERY *view;
5038 UINT r;
5040 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Icon`", &view );
5041 if (r == ERROR_SUCCESS)
5043 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5044 msiobj_release( &view->hdr );
5045 if (r != ERROR_SUCCESS)
5046 return r;
5048 return ERROR_SUCCESS;
5051 static void remove_product_upgrade_code( MSIPACKAGE *package )
5053 WCHAR *code, product[SQUASHED_GUID_SIZE];
5054 HKEY hkey;
5055 LONG res;
5056 DWORD count;
5058 squash_guid( package->ProductCode, product );
5059 if (!(code = msi_dup_property( package->db, L"UpgradeCode" )))
5061 WARN( "upgrade code not found\n" );
5062 return;
5064 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5066 RegDeleteValueW( hkey, product );
5067 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5068 RegCloseKey( hkey );
5069 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5071 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5073 RegDeleteValueW( hkey, product );
5074 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5075 RegCloseKey( hkey );
5076 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5078 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5080 RegDeleteValueW( hkey, product );
5081 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5082 RegCloseKey( hkey );
5083 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5086 msi_free( code );
5089 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5091 MSIPATCHINFO *patch;
5093 MSIREG_DeleteProductKey(package->ProductCode);
5094 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5095 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5097 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5098 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5099 MSIREG_DeleteUserProductKey(package->ProductCode);
5100 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5102 remove_product_upgrade_code( package );
5104 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5106 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5107 if (!wcscmp( package->ProductCode, patch->products ))
5109 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5110 patch->delete_on_close = TRUE;
5112 /* FIXME: remove local patch package if this is the last product */
5114 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5115 package->delete_on_close = TRUE;
5117 msi_unpublish_icons( package );
5118 return ERROR_SUCCESS;
5121 static BOOL is_full_uninstall( MSIPACKAGE *package )
5123 MSIFEATURE *feature;
5125 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5127 if (feature->Action != INSTALLSTATE_ABSENT &&
5128 (feature->Installed != INSTALLSTATE_ABSENT || feature->Action != INSTALLSTATE_UNKNOWN))
5129 return FALSE;
5132 return TRUE;
5135 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5137 UINT rc;
5139 /* first do the same as an InstallExecute */
5140 rc = execute_script(package, SCRIPT_INSTALL);
5141 if (rc != ERROR_SUCCESS)
5142 return rc;
5144 /* then handle commit actions */
5145 rc = execute_script(package, SCRIPT_COMMIT);
5146 if (rc != ERROR_SUCCESS)
5147 return rc;
5149 if (is_full_uninstall(package))
5150 rc = ACTION_UnpublishProduct(package);
5152 return rc;
5155 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5157 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5158 HKEY hkey;
5160 squash_guid( package->ProductCode, squashed_pc );
5162 GetSystemDirectoryW(sysdir, ARRAY_SIZE(sysdir));
5163 RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", &hkey);
5164 swprintf(buffer, ARRAY_SIZE(buffer), L"%s\\MsiExec.exe /@ \"%s\"", sysdir, squashed_pc);
5166 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5167 RegCloseKey(hkey);
5169 TRACE("Reboot command %s\n",debugstr_w(buffer));
5171 RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\RunOnceEntries",
5172 &hkey);
5173 swprintf( buffer, ARRAY_SIZE(buffer), L"/I \"%s\" AFTERREBOOT=1 RUNONCEENTRY=\"%s\"", package->ProductCode,
5174 squashed_pc );
5176 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5177 RegCloseKey(hkey);
5179 return ERROR_INSTALL_SUSPEND;
5182 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5184 DWORD attrib;
5185 UINT rc;
5188 * We are currently doing what should be done here in the top level Install
5189 * however for Administrative and uninstalls this step will be needed
5191 if (!package->PackagePath)
5192 return ERROR_SUCCESS;
5194 msi_set_sourcedir_props(package, TRUE);
5196 attrib = GetFileAttributesW(package->db->path);
5197 if (attrib == INVALID_FILE_ATTRIBUTES)
5199 MSIRECORD *record;
5200 LPWSTR prompt;
5201 DWORD size = 0;
5203 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5204 package->Context, MSICODE_PRODUCT,
5205 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5206 if (rc == ERROR_MORE_DATA)
5208 prompt = msi_alloc(size * sizeof(WCHAR));
5209 MsiSourceListGetInfoW(package->ProductCode, NULL,
5210 package->Context, MSICODE_PRODUCT,
5211 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5213 else
5214 prompt = strdupW(package->db->path);
5216 record = MSI_CreateRecord(2);
5217 MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK);
5218 MSI_RecordSetStringW(record, 2, prompt);
5219 msi_free(prompt);
5220 while(attrib == INVALID_FILE_ATTRIBUTES)
5222 MSI_RecordSetStringW(record, 0, NULL);
5223 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record);
5224 if (rc == IDCANCEL)
5225 return ERROR_INSTALL_USEREXIT;
5226 attrib = GetFileAttributesW(package->db->path);
5228 rc = ERROR_SUCCESS;
5230 else
5231 return ERROR_SUCCESS;
5233 return rc;
5236 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5238 static const WCHAR szPropKeys[][80] =
5240 L"ProductID",
5241 L"USERNAME",
5242 L"COMPANYNAME",
5243 L"",
5245 static const WCHAR szRegKeys[][80] =
5247 L"ProductID",
5248 L"RegOwner",
5249 L"RegCompany",
5250 L"",
5252 HKEY hkey = 0;
5253 LPWSTR buffer, productid = NULL;
5254 UINT i, rc = ERROR_SUCCESS;
5255 MSIRECORD *uirow;
5257 if (package->script == SCRIPT_NONE)
5258 return msi_schedule_action(package, SCRIPT_INSTALL, L"RegisterUser");
5260 if (msi_check_unpublish(package))
5262 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5263 goto end;
5266 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5267 if (!productid)
5268 goto end;
5270 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5271 NULL, &hkey, TRUE);
5272 if (rc != ERROR_SUCCESS)
5273 goto end;
5275 for( i = 0; szPropKeys[i][0]; i++ )
5277 buffer = msi_dup_property( package->db, szPropKeys[i] );
5278 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5279 msi_free( buffer );
5282 end:
5283 uirow = MSI_CreateRecord( 1 );
5284 MSI_RecordSetStringW( uirow, 1, productid );
5285 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5286 msiobj_release( &uirow->hdr );
5288 msi_free(productid);
5289 RegCloseKey(hkey);
5290 return rc;
5293 static UINT iterate_properties(MSIRECORD *record, void *param)
5295 MSIRECORD *uirow;
5297 uirow = MSI_CloneRecord(record);
5298 if (!uirow) return ERROR_OUTOFMEMORY;
5299 MSI_RecordSetStringW(uirow, 0, L"Property(S): [1] = [2]");
5300 MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
5301 msiobj_release(&uirow->hdr);
5303 return ERROR_SUCCESS;
5307 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5309 WCHAR *productname;
5310 WCHAR *action;
5311 WCHAR *info_template;
5312 MSIQUERY *view;
5313 MSIRECORD *uirow, *uirow_info;
5314 UINT rc;
5316 /* Send COMMONDATA and INFO messages. */
5317 /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
5318 uirow = MSI_CreateRecord(3);
5319 if (!uirow) return ERROR_OUTOFMEMORY;
5320 MSI_RecordSetStringW(uirow, 0, NULL);
5321 MSI_RecordSetInteger(uirow, 1, 0);
5322 MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
5323 MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
5324 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5325 /* FIXME: send INSTALLMESSAGE_PROGRESS */
5326 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5328 if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
5330 uirow_info = MSI_CreateRecord(0);
5331 if (!uirow_info)
5333 msiobj_release(&uirow->hdr);
5334 return ERROR_OUTOFMEMORY;
5336 info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
5337 MSI_RecordSetStringW(uirow_info, 0, info_template);
5338 msi_free(info_template);
5339 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
5340 msiobj_release(&uirow_info->hdr);
5343 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5345 productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
5346 MSI_RecordSetInteger(uirow, 1, 1);
5347 MSI_RecordSetStringW(uirow, 2, productname);
5348 MSI_RecordSetStringW(uirow, 3, NULL);
5349 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5350 msiobj_release(&uirow->hdr);
5352 package->LastActionResult = MSI_NULL_INTEGER;
5354 action = msi_dup_property(package->db, L"EXECUTEACTION");
5355 if (!action) action = msi_strdupW(L"INSTALL", ARRAY_SIZE(L"INSTALL") - 1);
5357 /* Perform the action. Top-level actions trigger a sequence. */
5358 if (!wcscmp(action, L"INSTALL"))
5360 /* Send ACTIONSTART/INFO and INSTALLSTART. */
5361 ui_actionstart(package, L"INSTALL", NULL, NULL);
5362 ui_actioninfo(package, L"INSTALL", TRUE, 0);
5363 uirow = MSI_CreateRecord(2);
5364 if (!uirow)
5366 rc = ERROR_OUTOFMEMORY;
5367 goto end;
5369 MSI_RecordSetStringW(uirow, 0, NULL);
5370 MSI_RecordSetStringW(uirow, 1, productname);
5371 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5372 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
5373 msiobj_release(&uirow->hdr);
5375 /* Perform the installation. Always use the ExecuteSequence. */
5376 package->InWhatSequence |= SEQUENCE_EXEC;
5377 rc = ACTION_ProcessExecSequence(package);
5379 /* Send return value and INSTALLEND. */
5380 ui_actioninfo(package, L"INSTALL", FALSE, !rc);
5381 uirow = MSI_CreateRecord(3);
5382 if (!uirow)
5384 rc = ERROR_OUTOFMEMORY;
5385 goto end;
5387 MSI_RecordSetStringW(uirow, 0, NULL);
5388 MSI_RecordSetStringW(uirow, 1, productname);
5389 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5390 MSI_RecordSetInteger(uirow, 3, !rc);
5391 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
5392 msiobj_release(&uirow->hdr);
5394 else
5395 rc = ACTION_PerformAction(package, action);
5397 /* Send all set properties. */
5398 if (!MSI_OpenQuery(package->db, &view, L"SELECT * FROM `_Property`"))
5400 MSI_IterateRecords(view, NULL, iterate_properties, package);
5401 msiobj_release(&view->hdr);
5404 /* And finally, toggle the cancel off and on. */
5405 uirow = MSI_CreateRecord(2);
5406 if (!uirow)
5408 rc = ERROR_OUTOFMEMORY;
5409 goto end;
5411 MSI_RecordSetStringW(uirow, 0, NULL);
5412 MSI_RecordSetInteger(uirow, 1, 2);
5413 MSI_RecordSetInteger(uirow, 2, 0);
5414 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5415 MSI_RecordSetInteger(uirow, 2, 1);
5416 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5417 msiobj_release(&uirow->hdr);
5419 end:
5420 msi_free(productname);
5421 msi_free(action);
5422 return rc;
5425 static UINT ACTION_INSTALL(MSIPACKAGE *package)
5427 msi_set_property(package->db, L"EXECUTEACTION", L"INSTALL", -1);
5428 if (needs_ui_sequence(package) && ui_sequence_exists(package))
5430 package->InWhatSequence |= SEQUENCE_UI;
5431 return ACTION_ProcessUISequence(package);
5433 else
5434 return ACTION_ExecuteAction(package);
5437 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5439 WCHAR productid_85[21], component_85[21], *ret;
5440 GUID clsid;
5441 DWORD sz;
5443 /* > is used if there is a component GUID and < if not. */
5445 productid_85[0] = 0;
5446 component_85[0] = 0;
5447 CLSIDFromString( package->ProductCode, &clsid );
5449 encode_base85_guid( &clsid, productid_85 );
5450 if (component)
5452 CLSIDFromString( component->ComponentId, &clsid );
5453 encode_base85_guid( &clsid, component_85 );
5456 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5457 debugstr_w(component_85));
5459 sz = 20 + lstrlenW( feature ) + 20 + 3;
5460 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5461 if (ret) swprintf( ret, sz, L"%s%s%c%s", productid_85, feature, component ? '>' : '<', component_85 );
5462 return ret;
5465 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5467 MSIPACKAGE *package = param;
5468 LPCWSTR compgroupid, component, feature, qualifier, text;
5469 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5470 HKEY hkey = NULL;
5471 UINT rc;
5472 MSICOMPONENT *comp;
5473 MSIFEATURE *feat;
5474 DWORD sz;
5475 MSIRECORD *uirow;
5476 int len;
5478 feature = MSI_RecordGetString(rec, 5);
5479 feat = msi_get_loaded_feature(package, feature);
5480 if (!feat)
5481 return ERROR_SUCCESS;
5483 feat->Action = msi_get_feature_action( package, feat );
5484 if (feat->Action != INSTALLSTATE_LOCAL &&
5485 feat->Action != INSTALLSTATE_SOURCE &&
5486 feat->Action != INSTALLSTATE_ADVERTISED)
5488 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5489 return ERROR_SUCCESS;
5492 component = MSI_RecordGetString(rec, 3);
5493 comp = msi_get_loaded_component(package, component);
5494 if (!comp)
5495 return ERROR_SUCCESS;
5497 compgroupid = MSI_RecordGetString(rec,1);
5498 qualifier = MSI_RecordGetString(rec,2);
5500 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5501 if (rc != ERROR_SUCCESS)
5502 goto end;
5504 advertise = msi_create_component_advertise_string( package, comp, feature );
5505 text = MSI_RecordGetString( rec, 4 );
5506 if (text)
5508 p = msi_alloc( (lstrlenW( advertise ) + lstrlenW( text ) + 1) * sizeof(WCHAR) );
5509 lstrcpyW( p, advertise );
5510 lstrcatW( p, text );
5511 msi_free( advertise );
5512 advertise = p;
5514 existing = msi_reg_get_val_str( hkey, qualifier );
5516 sz = lstrlenW( advertise ) + 1;
5517 if (existing)
5519 for (p = existing; *p; p += len)
5521 len = lstrlenW( p ) + 1;
5522 if (wcscmp( advertise, p )) sz += len;
5525 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5527 rc = ERROR_OUTOFMEMORY;
5528 goto end;
5530 q = output;
5531 if (existing)
5533 for (p = existing; *p; p += len)
5535 len = lstrlenW( p ) + 1;
5536 if (wcscmp( advertise, p ))
5538 memcpy( q, p, len * sizeof(WCHAR) );
5539 q += len;
5543 lstrcpyW( q, advertise );
5544 q[lstrlenW( q ) + 1] = 0;
5546 msi_reg_set_val_multi_str( hkey, qualifier, output );
5548 end:
5549 RegCloseKey(hkey);
5550 msi_free( output );
5551 msi_free( advertise );
5552 msi_free( existing );
5554 /* the UI chunk */
5555 uirow = MSI_CreateRecord( 2 );
5556 MSI_RecordSetStringW( uirow, 1, compgroupid );
5557 MSI_RecordSetStringW( uirow, 2, qualifier);
5558 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5559 msiobj_release( &uirow->hdr );
5560 /* FIXME: call ui_progress? */
5562 return rc;
5566 * At present I am ignoring the advertised components part of this and only
5567 * focusing on the qualified component sets
5569 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5571 MSIQUERY *view;
5572 UINT rc;
5574 if (package->script == SCRIPT_NONE)
5575 return msi_schedule_action(package, SCRIPT_INSTALL, L"PublishComponents");
5577 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `PublishComponent`", &view);
5578 if (rc != ERROR_SUCCESS)
5579 return ERROR_SUCCESS;
5581 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5582 msiobj_release(&view->hdr);
5583 return rc;
5586 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5588 MSIPACKAGE *package = param;
5589 LPCWSTR compgroupid, component, feature, qualifier;
5590 MSICOMPONENT *comp;
5591 MSIFEATURE *feat;
5592 MSIRECORD *uirow;
5593 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5594 LONG res;
5596 feature = MSI_RecordGetString( rec, 5 );
5597 feat = msi_get_loaded_feature( package, feature );
5598 if (!feat)
5599 return ERROR_SUCCESS;
5601 feat->Action = msi_get_feature_action( package, feat );
5602 if (feat->Action != INSTALLSTATE_ABSENT)
5604 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5605 return ERROR_SUCCESS;
5608 component = MSI_RecordGetString( rec, 3 );
5609 comp = msi_get_loaded_component( package, component );
5610 if (!comp)
5611 return ERROR_SUCCESS;
5613 compgroupid = MSI_RecordGetString( rec, 1 );
5614 qualifier = MSI_RecordGetString( rec, 2 );
5616 squash_guid( compgroupid, squashed );
5617 lstrcpyW( keypath, L"Software\\Microsoft\\Installer\\Components\\" );
5618 lstrcatW( keypath, squashed );
5620 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5621 if (res != ERROR_SUCCESS)
5623 WARN("Unable to delete component key %d\n", res);
5626 uirow = MSI_CreateRecord( 2 );
5627 MSI_RecordSetStringW( uirow, 1, compgroupid );
5628 MSI_RecordSetStringW( uirow, 2, qualifier );
5629 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5630 msiobj_release( &uirow->hdr );
5632 return ERROR_SUCCESS;
5635 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5637 MSIQUERY *view;
5638 UINT rc;
5640 if (package->script == SCRIPT_NONE)
5641 return msi_schedule_action(package, SCRIPT_INSTALL, L"UnpublishComponents");
5643 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `PublishComponent`", &view );
5644 if (rc != ERROR_SUCCESS)
5645 return ERROR_SUCCESS;
5647 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5648 msiobj_release( &view->hdr );
5649 return rc;
5652 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5654 MSIPACKAGE *package = param;
5655 MSICOMPONENT *component;
5656 MSIRECORD *row;
5657 MSIFILE *file;
5658 SC_HANDLE hscm = NULL, service = NULL;
5659 LPCWSTR comp, key;
5660 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5661 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5662 DWORD serv_type, start_type, err_control;
5663 BOOL is_vital;
5664 SERVICE_DESCRIPTIONW sd = {NULL};
5665 UINT ret = ERROR_SUCCESS;
5667 comp = MSI_RecordGetString( rec, 12 );
5668 component = msi_get_loaded_component( package, comp );
5669 if (!component)
5671 WARN("service component not found\n");
5672 goto done;
5674 component->Action = msi_get_component_action( package, component );
5675 if (component->Action != INSTALLSTATE_LOCAL)
5677 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5678 goto done;
5680 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5681 if (!hscm)
5683 ERR("Failed to open the SC Manager!\n");
5684 goto done;
5687 start_type = MSI_RecordGetInteger(rec, 5);
5688 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5689 goto done;
5691 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5692 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5693 serv_type = MSI_RecordGetInteger(rec, 4);
5694 err_control = MSI_RecordGetInteger(rec, 6);
5695 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5696 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5697 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5698 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5699 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5700 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5702 /* Should the complete install fail if CreateService fails? */
5703 is_vital = (err_control & msidbServiceInstallErrorControlVital);
5705 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
5706 CreateService (under Windows) would fail if not. */
5707 err_control &= ~msidbServiceInstallErrorControlVital;
5709 /* fetch the service path */
5710 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `Component` WHERE `Component` = '%s'", comp);
5711 if (!row)
5713 ERR("Query failed\n");
5714 goto done;
5716 if (!(key = MSI_RecordGetString(row, 6)))
5718 msiobj_release(&row->hdr);
5719 goto done;
5721 file = msi_get_loaded_file(package, key);
5722 msiobj_release(&row->hdr);
5723 if (!file)
5725 ERR("Failed to load the service file\n");
5726 goto done;
5729 if (!args || !args[0]) image_path = file->TargetPath;
5730 else
5732 int len = lstrlenW(file->TargetPath) + lstrlenW(args) + 2;
5733 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5735 ret = ERROR_OUTOFMEMORY;
5736 goto done;
5739 lstrcpyW(image_path, file->TargetPath);
5740 lstrcatW(image_path, L" ");
5741 lstrcatW(image_path, args);
5743 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5744 start_type, err_control, image_path, load_order,
5745 NULL, depends, serv_name, pass);
5747 if (!service)
5749 if (GetLastError() != ERROR_SERVICE_EXISTS)
5751 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5752 if (is_vital)
5753 ret = ERROR_INSTALL_FAILURE;
5757 else if (sd.lpDescription)
5759 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5760 WARN("failed to set service description %u\n", GetLastError());
5763 if (image_path != file->TargetPath) msi_free(image_path);
5764 done:
5765 if (service) CloseServiceHandle(service);
5766 if (hscm) CloseServiceHandle(hscm);
5767 msi_free(name);
5768 msi_free(disp);
5769 msi_free(sd.lpDescription);
5770 msi_free(load_order);
5771 msi_free(serv_name);
5772 msi_free(pass);
5773 msi_free(depends);
5774 msi_free(args);
5776 return ret;
5779 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5781 MSIQUERY *view;
5782 UINT rc;
5784 if (package->script == SCRIPT_NONE)
5785 return msi_schedule_action(package, SCRIPT_INSTALL, L"InstallServices");
5787 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ServiceInstall`", &view);
5788 if (rc != ERROR_SUCCESS)
5789 return ERROR_SUCCESS;
5791 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5792 msiobj_release(&view->hdr);
5793 return rc;
5796 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5797 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5799 LPCWSTR *vector, *temp_vector;
5800 LPWSTR p, q;
5801 DWORD sep_len;
5803 *numargs = 0;
5804 sep_len = ARRAY_SIZE(L"[~]") - 1;
5806 if (!args)
5807 return NULL;
5809 vector = msi_alloc(sizeof(LPWSTR));
5810 if (!vector)
5811 return NULL;
5813 p = args;
5816 (*numargs)++;
5817 vector[*numargs - 1] = p;
5819 if ((q = wcsstr(p, L"[~]")))
5821 *q = '\0';
5823 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5824 if (!temp_vector)
5826 msi_free(vector);
5827 return NULL;
5829 vector = temp_vector;
5831 p = q + sep_len;
5833 } while (q);
5835 return vector;
5838 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5840 MSIPACKAGE *package = param;
5841 MSICOMPONENT *comp;
5842 MSIRECORD *uirow;
5843 SC_HANDLE scm = NULL, service = NULL;
5844 LPCWSTR component, *vector = NULL;
5845 LPWSTR name, args, display_name = NULL;
5846 DWORD event, numargs, len, wait, dummy;
5847 UINT r = ERROR_FUNCTION_FAILED;
5848 SERVICE_STATUS_PROCESS status;
5849 ULONGLONG start_time;
5851 component = MSI_RecordGetString(rec, 6);
5852 comp = msi_get_loaded_component(package, component);
5853 if (!comp)
5854 return ERROR_SUCCESS;
5856 event = MSI_RecordGetInteger( rec, 3 );
5857 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5859 comp->Action = msi_get_component_action( package, comp );
5860 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
5861 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
5863 TRACE("not starting %s\n", debugstr_w(name));
5864 msi_free( name );
5865 return ERROR_SUCCESS;
5868 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5869 wait = MSI_RecordGetInteger(rec, 5);
5871 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5872 if (!scm)
5874 ERR("Failed to open the service control manager\n");
5875 goto done;
5878 len = 0;
5879 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5880 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5882 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5883 GetServiceDisplayNameW( scm, name, display_name, &len );
5886 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5887 if (!service)
5889 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5890 goto done;
5893 vector = msi_service_args_to_vector(args, &numargs);
5895 if (!StartServiceW(service, numargs, vector) &&
5896 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5898 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5899 goto done;
5902 r = ERROR_SUCCESS;
5903 if (wait)
5905 /* wait for at most 30 seconds for the service to be up and running */
5906 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5907 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5909 TRACE("failed to query service status (%u)\n", GetLastError());
5910 goto done;
5912 start_time = GetTickCount64();
5913 while (status.dwCurrentState == SERVICE_START_PENDING)
5915 if (GetTickCount64() - start_time > 30000) break;
5916 Sleep(1000);
5917 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5918 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5920 TRACE("failed to query service status (%u)\n", GetLastError());
5921 goto done;
5924 if (status.dwCurrentState != SERVICE_RUNNING)
5926 WARN("service failed to start %u\n", status.dwCurrentState);
5927 r = ERROR_FUNCTION_FAILED;
5931 done:
5932 uirow = MSI_CreateRecord( 2 );
5933 MSI_RecordSetStringW( uirow, 1, display_name );
5934 MSI_RecordSetStringW( uirow, 2, name );
5935 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5936 msiobj_release( &uirow->hdr );
5938 if (service) CloseServiceHandle(service);
5939 if (scm) CloseServiceHandle(scm);
5941 msi_free(name);
5942 msi_free(args);
5943 msi_free(vector);
5944 msi_free(display_name);
5945 return r;
5948 static UINT ACTION_StartServices( MSIPACKAGE *package )
5950 MSIQUERY *view;
5951 UINT rc;
5953 if (package->script == SCRIPT_NONE)
5954 return msi_schedule_action(package, SCRIPT_INSTALL, L"StartServices");
5956 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ServiceControl`", &view);
5957 if (rc != ERROR_SUCCESS)
5958 return ERROR_SUCCESS;
5960 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5961 msiobj_release(&view->hdr);
5962 return rc;
5965 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5967 DWORD i, needed, count;
5968 ENUM_SERVICE_STATUSW *dependencies;
5969 SERVICE_STATUS ss;
5970 SC_HANDLE depserv;
5971 BOOL stopped, ret = FALSE;
5973 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5974 0, &needed, &count))
5975 return TRUE;
5977 if (GetLastError() != ERROR_MORE_DATA)
5978 return FALSE;
5980 dependencies = msi_alloc(needed);
5981 if (!dependencies)
5982 return FALSE;
5984 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5985 needed, &needed, &count))
5986 goto done;
5988 for (i = 0; i < count; i++)
5990 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5991 SERVICE_STOP | SERVICE_QUERY_STATUS);
5992 if (!depserv)
5993 goto done;
5995 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
5996 CloseServiceHandle(depserv);
5997 if (!stopped)
5998 goto done;
6001 ret = TRUE;
6003 done:
6004 msi_free(dependencies);
6005 return ret;
6008 static UINT stop_service( LPCWSTR name )
6010 SC_HANDLE scm = NULL, service = NULL;
6011 SERVICE_STATUS status;
6012 SERVICE_STATUS_PROCESS ssp;
6013 DWORD needed;
6015 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6016 if (!scm)
6018 WARN("Failed to open the SCM: %d\n", GetLastError());
6019 goto done;
6022 service = OpenServiceW(scm, name,
6023 SERVICE_STOP |
6024 SERVICE_QUERY_STATUS |
6025 SERVICE_ENUMERATE_DEPENDENTS);
6026 if (!service)
6028 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6029 goto done;
6032 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6033 sizeof(SERVICE_STATUS_PROCESS), &needed))
6035 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6036 goto done;
6039 if (ssp.dwCurrentState == SERVICE_STOPPED)
6040 goto done;
6042 stop_service_dependents(scm, service);
6044 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6045 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6047 done:
6048 if (service) CloseServiceHandle(service);
6049 if (scm) CloseServiceHandle(scm);
6051 return ERROR_SUCCESS;
6054 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6056 MSIPACKAGE *package = param;
6057 MSICOMPONENT *comp;
6058 MSIRECORD *uirow;
6059 LPCWSTR component;
6060 WCHAR *name, *display_name = NULL;
6061 DWORD event, len;
6062 SC_HANDLE scm;
6064 component = MSI_RecordGetString( rec, 6 );
6065 comp = msi_get_loaded_component( package, component );
6066 if (!comp)
6067 return ERROR_SUCCESS;
6069 event = MSI_RecordGetInteger( rec, 3 );
6070 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6072 comp->Action = msi_get_component_action( package, comp );
6073 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6074 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6076 TRACE("not stopping %s\n", debugstr_w(name));
6077 msi_free( name );
6078 return ERROR_SUCCESS;
6081 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6082 if (!scm)
6084 ERR("Failed to open the service control manager\n");
6085 goto done;
6088 len = 0;
6089 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6090 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6092 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6093 GetServiceDisplayNameW( scm, name, display_name, &len );
6095 CloseServiceHandle( scm );
6097 stop_service( name );
6099 done:
6100 uirow = MSI_CreateRecord( 2 );
6101 MSI_RecordSetStringW( uirow, 1, display_name );
6102 MSI_RecordSetStringW( uirow, 2, name );
6103 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6104 msiobj_release( &uirow->hdr );
6106 msi_free( name );
6107 msi_free( display_name );
6108 return ERROR_SUCCESS;
6111 static UINT ACTION_StopServices( MSIPACKAGE *package )
6113 MSIQUERY *view;
6114 UINT rc;
6116 if (package->script == SCRIPT_NONE)
6117 return msi_schedule_action(package, SCRIPT_INSTALL, L"StopServices");
6119 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ServiceControl`", &view);
6120 if (rc != ERROR_SUCCESS)
6121 return ERROR_SUCCESS;
6123 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6124 msiobj_release(&view->hdr);
6125 return rc;
6128 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6130 MSIPACKAGE *package = param;
6131 MSICOMPONENT *comp;
6132 MSIRECORD *uirow;
6133 LPWSTR name = NULL, display_name = NULL;
6134 DWORD event, len;
6135 SC_HANDLE scm = NULL, service = NULL;
6137 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6138 if (!comp)
6139 return ERROR_SUCCESS;
6141 event = MSI_RecordGetInteger( rec, 3 );
6142 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6144 comp->Action = msi_get_component_action( package, comp );
6145 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6146 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6148 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6149 msi_free( name );
6150 return ERROR_SUCCESS;
6152 stop_service( name );
6154 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6155 if (!scm)
6157 WARN("Failed to open the SCM: %d\n", GetLastError());
6158 goto done;
6161 len = 0;
6162 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6163 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6165 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6166 GetServiceDisplayNameW( scm, name, display_name, &len );
6169 service = OpenServiceW( scm, name, DELETE );
6170 if (!service)
6172 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6173 goto done;
6176 if (!DeleteService( service ))
6177 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6179 done:
6180 uirow = MSI_CreateRecord( 2 );
6181 MSI_RecordSetStringW( uirow, 1, display_name );
6182 MSI_RecordSetStringW( uirow, 2, name );
6183 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6184 msiobj_release( &uirow->hdr );
6186 if (service) CloseServiceHandle( service );
6187 if (scm) CloseServiceHandle( scm );
6188 msi_free( name );
6189 msi_free( display_name );
6191 return ERROR_SUCCESS;
6194 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6196 MSIQUERY *view;
6197 UINT rc;
6199 if (package->script == SCRIPT_NONE)
6200 return msi_schedule_action(package, SCRIPT_INSTALL, L"DeleteServices");
6202 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ServiceControl`", &view );
6203 if (rc != ERROR_SUCCESS)
6204 return ERROR_SUCCESS;
6206 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6207 msiobj_release( &view->hdr );
6208 return rc;
6211 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6213 MSIPACKAGE *package = param;
6214 LPWSTR driver, driver_path, ptr;
6215 WCHAR outpath[MAX_PATH];
6216 MSIFILE *driver_file = NULL, *setup_file = NULL;
6217 MSICOMPONENT *comp;
6218 MSIRECORD *uirow;
6219 LPCWSTR desc, file_key, component;
6220 DWORD len, usage;
6221 UINT r = ERROR_SUCCESS;
6223 component = MSI_RecordGetString( rec, 2 );
6224 comp = msi_get_loaded_component( package, component );
6225 if (!comp)
6226 return ERROR_SUCCESS;
6228 comp->Action = msi_get_component_action( package, comp );
6229 if (comp->Action != INSTALLSTATE_LOCAL)
6231 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6232 return ERROR_SUCCESS;
6234 desc = MSI_RecordGetString(rec, 3);
6236 file_key = MSI_RecordGetString( rec, 4 );
6237 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6239 file_key = MSI_RecordGetString( rec, 5 );
6240 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6242 if (!driver_file)
6244 ERR("ODBC Driver entry not found!\n");
6245 return ERROR_FUNCTION_FAILED;
6248 len = lstrlenW(desc) + lstrlenW(L"Driver=%s") + lstrlenW(driver_file->FileName);
6249 if (setup_file)
6250 len += lstrlenW(L"Setup=%s") + lstrlenW(setup_file->FileName);
6251 len += lstrlenW(L"FileUsage=1") + 2; /* \0\0 */
6253 driver = msi_alloc(len * sizeof(WCHAR));
6254 if (!driver)
6255 return ERROR_OUTOFMEMORY;
6257 ptr = driver;
6258 lstrcpyW(ptr, desc);
6259 ptr += lstrlenW(ptr) + 1;
6261 len = swprintf(ptr, len - (ptr - driver), L"Driver=%s", driver_file->FileName);
6262 ptr += len + 1;
6264 if (setup_file)
6266 len = swprintf(ptr, len - (ptr - driver), L"Setup=%s", setup_file->FileName);
6267 ptr += len + 1;
6270 lstrcpyW(ptr, L"FileUsage=1");
6271 ptr += lstrlenW(ptr) + 1;
6272 *ptr = '\0';
6274 if (!driver_file->TargetPath)
6276 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6277 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6279 driver_path = strdupW(driver_file->TargetPath);
6280 ptr = wcsrchr(driver_path, '\\');
6281 if (ptr) *ptr = '\0';
6283 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6284 NULL, ODBC_INSTALL_COMPLETE, &usage))
6286 ERR("Failed to install SQL driver!\n");
6287 r = ERROR_FUNCTION_FAILED;
6290 uirow = MSI_CreateRecord( 5 );
6291 MSI_RecordSetStringW( uirow, 1, desc );
6292 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6293 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6294 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6295 msiobj_release( &uirow->hdr );
6297 msi_free(driver);
6298 msi_free(driver_path);
6300 return r;
6303 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6305 MSIPACKAGE *package = param;
6306 LPWSTR translator, translator_path, ptr;
6307 WCHAR outpath[MAX_PATH];
6308 MSIFILE *translator_file = NULL, *setup_file = NULL;
6309 MSICOMPONENT *comp;
6310 MSIRECORD *uirow;
6311 LPCWSTR desc, file_key, component;
6312 DWORD len, usage;
6313 UINT r = ERROR_SUCCESS;
6315 component = MSI_RecordGetString( rec, 2 );
6316 comp = msi_get_loaded_component( package, component );
6317 if (!comp)
6318 return ERROR_SUCCESS;
6320 comp->Action = msi_get_component_action( package, comp );
6321 if (comp->Action != INSTALLSTATE_LOCAL)
6323 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6324 return ERROR_SUCCESS;
6326 desc = MSI_RecordGetString(rec, 3);
6328 file_key = MSI_RecordGetString( rec, 4 );
6329 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6331 file_key = MSI_RecordGetString( rec, 5 );
6332 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6334 if (!translator_file)
6336 ERR("ODBC Translator entry not found!\n");
6337 return ERROR_FUNCTION_FAILED;
6340 len = lstrlenW(desc) + lstrlenW(L"Translator=%s") + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6341 if (setup_file)
6342 len += lstrlenW(L"Setup=%s") + lstrlenW(setup_file->FileName);
6344 translator = msi_alloc(len * sizeof(WCHAR));
6345 if (!translator)
6346 return ERROR_OUTOFMEMORY;
6348 ptr = translator;
6349 lstrcpyW(ptr, desc);
6350 ptr += lstrlenW(ptr) + 1;
6352 len = swprintf(ptr, len - (ptr - translator), L"Translator=%s", translator_file->FileName);
6353 ptr += len + 1;
6355 if (setup_file)
6357 len = swprintf(ptr, len - (ptr - translator), L"Setup=%s", setup_file->FileName);
6358 ptr += len + 1;
6360 *ptr = '\0';
6362 translator_path = strdupW(translator_file->TargetPath);
6363 ptr = wcsrchr(translator_path, '\\');
6364 if (ptr) *ptr = '\0';
6366 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6367 NULL, ODBC_INSTALL_COMPLETE, &usage))
6369 ERR("Failed to install SQL translator!\n");
6370 r = ERROR_FUNCTION_FAILED;
6373 uirow = MSI_CreateRecord( 5 );
6374 MSI_RecordSetStringW( uirow, 1, desc );
6375 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6376 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6377 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6378 msiobj_release( &uirow->hdr );
6380 msi_free(translator);
6381 msi_free(translator_path);
6383 return r;
6386 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6388 MSIPACKAGE *package = param;
6389 MSICOMPONENT *comp;
6390 LPWSTR attrs;
6391 LPCWSTR desc, driver, component;
6392 WORD request = ODBC_ADD_SYS_DSN;
6393 INT registration;
6394 DWORD len;
6395 UINT r = ERROR_SUCCESS;
6396 MSIRECORD *uirow;
6398 component = MSI_RecordGetString( rec, 2 );
6399 comp = msi_get_loaded_component( package, component );
6400 if (!comp)
6401 return ERROR_SUCCESS;
6403 comp->Action = msi_get_component_action( package, comp );
6404 if (comp->Action != INSTALLSTATE_LOCAL)
6406 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6407 return ERROR_SUCCESS;
6410 desc = MSI_RecordGetString(rec, 3);
6411 driver = MSI_RecordGetString(rec, 4);
6412 registration = MSI_RecordGetInteger(rec, 5);
6414 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6415 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6417 len = lstrlenW(L"DSN=%s") + lstrlenW(desc) + 2; /* \0\0 */
6418 attrs = msi_alloc(len * sizeof(WCHAR));
6419 if (!attrs)
6420 return ERROR_OUTOFMEMORY;
6422 len = swprintf(attrs, len, L"DSN=%s", desc);
6423 attrs[len + 1] = 0;
6425 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6426 WARN("Failed to install SQL data source!\n");
6428 uirow = MSI_CreateRecord( 5 );
6429 MSI_RecordSetStringW( uirow, 1, desc );
6430 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6431 MSI_RecordSetInteger( uirow, 3, request );
6432 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6433 msiobj_release( &uirow->hdr );
6435 msi_free(attrs);
6437 return r;
6440 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6442 MSIQUERY *view;
6443 UINT rc;
6445 if (package->script == SCRIPT_NONE)
6446 return msi_schedule_action(package, SCRIPT_INSTALL, L"InstallODBC");
6448 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ODBCDriver`", &view);
6449 if (rc == ERROR_SUCCESS)
6451 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6452 msiobj_release(&view->hdr);
6453 if (rc != ERROR_SUCCESS)
6454 return rc;
6456 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ODBCTranslator`", &view);
6457 if (rc == ERROR_SUCCESS)
6459 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6460 msiobj_release(&view->hdr);
6461 if (rc != ERROR_SUCCESS)
6462 return rc;
6464 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ODBCDataSource`", &view);
6465 if (rc == ERROR_SUCCESS)
6467 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6468 msiobj_release(&view->hdr);
6469 if (rc != ERROR_SUCCESS)
6470 return rc;
6472 return ERROR_SUCCESS;
6475 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6477 MSIPACKAGE *package = param;
6478 MSICOMPONENT *comp;
6479 MSIRECORD *uirow;
6480 DWORD usage;
6481 LPCWSTR desc, component;
6483 component = MSI_RecordGetString( rec, 2 );
6484 comp = msi_get_loaded_component( package, component );
6485 if (!comp)
6486 return ERROR_SUCCESS;
6488 comp->Action = msi_get_component_action( package, comp );
6489 if (comp->Action != INSTALLSTATE_ABSENT)
6491 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6492 return ERROR_SUCCESS;
6495 desc = MSI_RecordGetString( rec, 3 );
6496 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6498 WARN("Failed to remove ODBC driver\n");
6500 else if (!usage)
6502 FIXME("Usage count reached 0\n");
6505 uirow = MSI_CreateRecord( 2 );
6506 MSI_RecordSetStringW( uirow, 1, desc );
6507 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6508 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6509 msiobj_release( &uirow->hdr );
6511 return ERROR_SUCCESS;
6514 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6516 MSIPACKAGE *package = param;
6517 MSICOMPONENT *comp;
6518 MSIRECORD *uirow;
6519 DWORD usage;
6520 LPCWSTR desc, component;
6522 component = MSI_RecordGetString( rec, 2 );
6523 comp = msi_get_loaded_component( package, component );
6524 if (!comp)
6525 return ERROR_SUCCESS;
6527 comp->Action = msi_get_component_action( package, comp );
6528 if (comp->Action != INSTALLSTATE_ABSENT)
6530 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6531 return ERROR_SUCCESS;
6534 desc = MSI_RecordGetString( rec, 3 );
6535 if (!SQLRemoveTranslatorW( desc, &usage ))
6537 WARN("Failed to remove ODBC translator\n");
6539 else if (!usage)
6541 FIXME("Usage count reached 0\n");
6544 uirow = MSI_CreateRecord( 2 );
6545 MSI_RecordSetStringW( uirow, 1, desc );
6546 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6547 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6548 msiobj_release( &uirow->hdr );
6550 return ERROR_SUCCESS;
6553 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6555 MSIPACKAGE *package = param;
6556 MSICOMPONENT *comp;
6557 MSIRECORD *uirow;
6558 LPWSTR attrs;
6559 LPCWSTR desc, driver, component;
6560 WORD request = ODBC_REMOVE_SYS_DSN;
6561 INT registration;
6562 DWORD len;
6564 component = MSI_RecordGetString( rec, 2 );
6565 comp = msi_get_loaded_component( package, component );
6566 if (!comp)
6567 return ERROR_SUCCESS;
6569 comp->Action = msi_get_component_action( package, comp );
6570 if (comp->Action != INSTALLSTATE_ABSENT)
6572 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6573 return ERROR_SUCCESS;
6576 desc = MSI_RecordGetString( rec, 3 );
6577 driver = MSI_RecordGetString( rec, 4 );
6578 registration = MSI_RecordGetInteger( rec, 5 );
6580 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6581 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6583 len = lstrlenW( L"DSN=%s" ) + lstrlenW( desc ) + 2; /* \0\0 */
6584 attrs = msi_alloc( len * sizeof(WCHAR) );
6585 if (!attrs)
6586 return ERROR_OUTOFMEMORY;
6588 FIXME("Use ODBCSourceAttribute table\n");
6590 len = swprintf( attrs, len, L"DSN=%s", desc );
6591 attrs[len + 1] = 0;
6593 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6595 WARN("Failed to remove ODBC data source\n");
6597 msi_free( attrs );
6599 uirow = MSI_CreateRecord( 3 );
6600 MSI_RecordSetStringW( uirow, 1, desc );
6601 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6602 MSI_RecordSetInteger( uirow, 3, request );
6603 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6604 msiobj_release( &uirow->hdr );
6606 return ERROR_SUCCESS;
6609 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6611 MSIQUERY *view;
6612 UINT rc;
6614 if (package->script == SCRIPT_NONE)
6615 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveODBC");
6617 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCDriver`", &view );
6618 if (rc == ERROR_SUCCESS)
6620 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6621 msiobj_release( &view->hdr );
6622 if (rc != ERROR_SUCCESS)
6623 return rc;
6625 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCTranslator`", &view );
6626 if (rc == ERROR_SUCCESS)
6628 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6629 msiobj_release( &view->hdr );
6630 if (rc != ERROR_SUCCESS)
6631 return rc;
6633 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCDataSource`", &view );
6634 if (rc == ERROR_SUCCESS)
6636 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6637 msiobj_release( &view->hdr );
6638 if (rc != ERROR_SUCCESS)
6639 return rc;
6641 return ERROR_SUCCESS;
6644 #define ENV_ACT_SETALWAYS 0x1
6645 #define ENV_ACT_SETABSENT 0x2
6646 #define ENV_ACT_REMOVE 0x4
6647 #define ENV_ACT_REMOVEMATCH 0x8
6649 #define ENV_MOD_MACHINE 0x20000000
6650 #define ENV_MOD_APPEND 0x40000000
6651 #define ENV_MOD_PREFIX 0x80000000
6652 #define ENV_MOD_MASK 0xC0000000
6654 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6656 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6658 const WCHAR *cptr = *name;
6660 *flags = 0;
6661 while (*cptr)
6663 if (*cptr == '=')
6664 *flags |= ENV_ACT_SETALWAYS;
6665 else if (*cptr == '+')
6666 *flags |= ENV_ACT_SETABSENT;
6667 else if (*cptr == '-')
6668 *flags |= ENV_ACT_REMOVE;
6669 else if (*cptr == '!')
6670 *flags |= ENV_ACT_REMOVEMATCH;
6671 else if (*cptr == '*')
6672 *flags |= ENV_MOD_MACHINE | ENV_ACT_REMOVE;
6673 else
6674 break;
6676 cptr++;
6677 (*name)++;
6680 if (!*cptr)
6682 ERR("Missing environment variable\n");
6683 return ERROR_FUNCTION_FAILED;
6686 if (*value)
6688 LPCWSTR ptr = *value;
6689 if (!wcsncmp(ptr, L"[~]", 3))
6691 if (ptr[3] == ';')
6693 *flags |= ENV_MOD_APPEND;
6694 *value += 3;
6696 else
6698 *value = NULL;
6701 else if (lstrlenW(*value) >= 3)
6703 ptr += lstrlenW(ptr) - 3;
6704 if (!wcscmp( ptr, L"[~]" ))
6706 if ((ptr-1) > *value && *(ptr-1) == ';')
6708 *flags |= ENV_MOD_PREFIX;
6709 /* the "[~]" will be removed by deformat_string */;
6711 else
6713 *value = NULL;
6719 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6720 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6721 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6722 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6724 ERR("Invalid flags: %08x\n", *flags);
6725 return ERROR_FUNCTION_FAILED;
6728 if (!*flags)
6729 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6731 return ERROR_SUCCESS;
6734 static UINT open_env_key( DWORD flags, HKEY *key )
6736 const WCHAR *env;
6737 HKEY root;
6738 LONG res;
6740 if (flags & ENV_MOD_MACHINE)
6742 env = L"System\\CurrentControlSet\\Control\\Session Manager\\Environment";
6743 root = HKEY_LOCAL_MACHINE;
6745 else
6747 env = L"Environment";
6748 root = HKEY_CURRENT_USER;
6751 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6752 if (res != ERROR_SUCCESS)
6754 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6755 return ERROR_FUNCTION_FAILED;
6758 return ERROR_SUCCESS;
6761 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6763 MSIPACKAGE *package = param;
6764 LPCWSTR name, value, component;
6765 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
6766 DWORD flags, type, size, len, len_value = 0;
6767 UINT res;
6768 HKEY env = NULL;
6769 MSICOMPONENT *comp;
6770 MSIRECORD *uirow;
6771 int action = 0, found = 0;
6773 component = MSI_RecordGetString(rec, 4);
6774 comp = msi_get_loaded_component(package, component);
6775 if (!comp)
6776 return ERROR_SUCCESS;
6778 comp->Action = msi_get_component_action( package, comp );
6779 if (comp->Action != INSTALLSTATE_LOCAL)
6781 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6782 return ERROR_SUCCESS;
6784 name = MSI_RecordGetString(rec, 2);
6785 value = MSI_RecordGetString(rec, 3);
6787 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6789 res = env_parse_flags(&name, &value, &flags);
6790 if (res != ERROR_SUCCESS || !value)
6791 goto done;
6793 if (value)
6795 DWORD len = deformat_string( package, value, &deformatted );
6796 if (!deformatted)
6798 res = ERROR_OUTOFMEMORY;
6799 goto done;
6802 if (len)
6804 value = deformatted;
6805 if (flags & ENV_MOD_PREFIX)
6807 p = wcsrchr( value, ';' );
6808 len_value = p - value;
6810 else if (flags & ENV_MOD_APPEND)
6812 value = wcschr( value, ';' ) + 1;
6813 len_value = lstrlenW( value );
6815 else len_value = lstrlenW( value );
6817 else
6819 value = NULL;
6823 res = open_env_key( flags, &env );
6824 if (res != ERROR_SUCCESS)
6825 goto done;
6827 if (flags & ENV_MOD_MACHINE)
6828 action |= 0x20000000;
6830 size = 0;
6831 type = REG_SZ;
6832 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6833 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6834 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6835 goto done;
6837 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6839 action = 0x2;
6841 /* Nothing to do. */
6842 if (!value)
6844 res = ERROR_SUCCESS;
6845 goto done;
6847 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6848 newval = strdupW(value);
6849 if (!newval)
6851 res = ERROR_OUTOFMEMORY;
6852 goto done;
6855 else
6857 action = 0x1;
6859 /* Contrary to MSDN, +-variable to [~];path works */
6860 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6862 res = ERROR_SUCCESS;
6863 goto done;
6866 if (!(p = q = data = msi_alloc( size )))
6868 msi_free(deformatted);
6869 RegCloseKey(env);
6870 return ERROR_OUTOFMEMORY;
6873 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
6874 if (res != ERROR_SUCCESS)
6875 goto done;
6877 if (flags & ENV_ACT_REMOVEMATCH && (!value || !wcscmp( data, value )))
6879 action = 0x4;
6880 res = RegDeleteValueW(env, name);
6881 if (res != ERROR_SUCCESS)
6882 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6883 goto done;
6886 for (;;)
6888 while (*q && *q != ';') q++;
6889 len = q - p;
6890 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
6891 (!p[len] || p[len] == ';'))
6893 found = 1;
6894 break;
6896 if (!*q) break;
6897 p = ++q;
6900 if (found)
6902 TRACE("string already set\n");
6903 goto done;
6906 size = (len_value + 1 + lstrlenW( data ) + 1) * sizeof(WCHAR);
6907 if (!(p = newval = msi_alloc( size )))
6909 res = ERROR_OUTOFMEMORY;
6910 goto done;
6913 if (flags & ENV_MOD_PREFIX)
6915 memcpy( newval, value, len_value * sizeof(WCHAR) );
6916 newval[len_value] = ';';
6917 p = newval + len_value + 1;
6918 action |= 0x80000000;
6921 lstrcpyW( p, data );
6923 if (flags & ENV_MOD_APPEND)
6925 p += lstrlenW( data );
6926 *p++ = ';';
6927 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
6928 action |= 0x40000000;
6931 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6932 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
6933 if (res)
6935 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6938 done:
6939 uirow = MSI_CreateRecord( 3 );
6940 MSI_RecordSetStringW( uirow, 1, name );
6941 MSI_RecordSetStringW( uirow, 2, newval );
6942 MSI_RecordSetInteger( uirow, 3, action );
6943 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6944 msiobj_release( &uirow->hdr );
6946 if (env) RegCloseKey(env);
6947 msi_free(deformatted);
6948 msi_free(data);
6949 msi_free(newval);
6950 return res;
6953 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6955 MSIQUERY *view;
6956 UINT rc;
6958 if (package->script == SCRIPT_NONE)
6959 return msi_schedule_action(package, SCRIPT_INSTALL, L"WriteEnvironmentStrings");
6961 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Environment`", &view);
6962 if (rc != ERROR_SUCCESS)
6963 return ERROR_SUCCESS;
6965 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6966 msiobj_release(&view->hdr);
6967 return rc;
6970 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6972 MSIPACKAGE *package = param;
6973 LPCWSTR name, value, component;
6974 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
6975 DWORD flags, type, size, len, len_value = 0, len_new_value;
6976 HKEY env = NULL;
6977 MSICOMPONENT *comp;
6978 MSIRECORD *uirow;
6979 int action = 0;
6980 LONG res;
6981 UINT r;
6983 component = MSI_RecordGetString( rec, 4 );
6984 comp = msi_get_loaded_component( package, component );
6985 if (!comp)
6986 return ERROR_SUCCESS;
6988 comp->Action = msi_get_component_action( package, comp );
6989 if (comp->Action != INSTALLSTATE_ABSENT)
6991 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6992 return ERROR_SUCCESS;
6994 name = MSI_RecordGetString( rec, 2 );
6995 value = MSI_RecordGetString( rec, 3 );
6997 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6999 r = env_parse_flags( &name, &value, &flags );
7000 if (r != ERROR_SUCCESS)
7001 return r;
7003 if (!(flags & ENV_ACT_REMOVE))
7005 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7006 return ERROR_SUCCESS;
7009 if (value)
7011 DWORD len = deformat_string( package, value, &deformatted );
7012 if (!deformatted)
7014 res = ERROR_OUTOFMEMORY;
7015 goto done;
7018 if (len)
7020 value = deformatted;
7021 if (flags & ENV_MOD_PREFIX)
7023 p = wcsrchr( value, ';' );
7024 len_value = p - value;
7026 else if (flags & ENV_MOD_APPEND)
7028 value = wcschr( value, ';' ) + 1;
7029 len_value = lstrlenW( value );
7031 else len_value = lstrlenW( value );
7033 else
7035 value = NULL;
7039 r = open_env_key( flags, &env );
7040 if (r != ERROR_SUCCESS)
7042 r = ERROR_SUCCESS;
7043 goto done;
7046 if (flags & ENV_MOD_MACHINE)
7047 action |= 0x20000000;
7049 size = 0;
7050 type = REG_SZ;
7051 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7052 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7053 goto done;
7055 if (!(new_value = msi_alloc( size ))) goto done;
7057 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7058 if (res != ERROR_SUCCESS)
7059 goto done;
7061 len_new_value = size / sizeof(WCHAR) - 1;
7062 p = q = new_value;
7063 for (;;)
7065 while (*q && *q != ';') q++;
7066 len = q - p;
7067 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7069 if (*q == ';') q++;
7070 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7071 break;
7073 if (!*q) break;
7074 p = ++q;
7077 if (!new_value[0] || !value)
7079 TRACE("removing %s\n", debugstr_w(name));
7080 res = RegDeleteValueW( env, name );
7081 if (res != ERROR_SUCCESS)
7082 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7084 else
7086 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7087 size = (lstrlenW( new_value ) + 1) * sizeof(WCHAR);
7088 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7089 if (res != ERROR_SUCCESS)
7090 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7093 done:
7094 uirow = MSI_CreateRecord( 3 );
7095 MSI_RecordSetStringW( uirow, 1, name );
7096 MSI_RecordSetStringW( uirow, 2, value );
7097 MSI_RecordSetInteger( uirow, 3, action );
7098 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7099 msiobj_release( &uirow->hdr );
7101 if (env) RegCloseKey( env );
7102 msi_free( deformatted );
7103 msi_free( new_value );
7104 return r;
7107 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7109 MSIQUERY *view;
7110 UINT rc;
7112 if (package->script == SCRIPT_NONE)
7113 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveEnvironmentStrings");
7115 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Environment`", &view );
7116 if (rc != ERROR_SUCCESS)
7117 return ERROR_SUCCESS;
7119 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7120 msiobj_release( &view->hdr );
7121 return rc;
7124 UINT msi_validate_product_id( MSIPACKAGE *package )
7126 LPWSTR key, template, id;
7127 UINT r = ERROR_SUCCESS;
7129 id = msi_dup_property( package->db, L"ProductID" );
7130 if (id)
7132 msi_free( id );
7133 return ERROR_SUCCESS;
7135 template = msi_dup_property( package->db, L"PIDTemplate" );
7136 key = msi_dup_property( package->db, L"PIDKEY" );
7137 if (key && template)
7139 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7140 r = msi_set_property( package->db, L"ProductID", key, -1 );
7142 msi_free( template );
7143 msi_free( key );
7144 return r;
7147 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7149 return msi_validate_product_id( package );
7152 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7154 TRACE("\n");
7155 package->need_reboot_at_end = 1;
7156 return ERROR_SUCCESS;
7159 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7161 MSIRECORD *uirow;
7162 int space = msi_get_property_int( package->db, L"AVAILABLEFREEREG", 0 );
7164 TRACE("%p %d kilobytes\n", package, space);
7166 uirow = MSI_CreateRecord( 1 );
7167 MSI_RecordSetInteger( uirow, 1, space );
7168 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7169 msiobj_release( &uirow->hdr );
7171 return ERROR_SUCCESS;
7174 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7176 TRACE("%p\n", package);
7178 msi_set_property( package->db, L"RollbackDisabled", L"1", -1 );
7179 return ERROR_SUCCESS;
7182 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7184 FIXME("%p\n", package);
7185 return ERROR_SUCCESS;
7188 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7190 MSIQUERY *view;
7191 UINT r, count;
7193 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCDriver`", &view );
7194 if (r == ERROR_SUCCESS)
7196 count = 0;
7197 r = MSI_IterateRecords( view, &count, NULL, package );
7198 msiobj_release( &view->hdr );
7199 if (r != ERROR_SUCCESS)
7200 return r;
7201 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7203 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCTranslator`", &view );
7204 if (r == ERROR_SUCCESS)
7206 count = 0;
7207 r = MSI_IterateRecords( view, &count, NULL, package );
7208 msiobj_release( &view->hdr );
7209 if (r != ERROR_SUCCESS)
7210 return r;
7211 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7213 return ERROR_SUCCESS;
7216 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7218 MSIPACKAGE *package = param;
7219 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7220 int attrs = MSI_RecordGetInteger( rec, 5 );
7221 UINT len = ARRAY_SIZE( L"msiexec /qn /i %s REMOVE=%s" );
7222 WCHAR *product, *features, *cmd;
7223 STARTUPINFOW si;
7224 PROCESS_INFORMATION info;
7225 BOOL ret;
7227 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7228 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7230 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7232 len += lstrlenW( product );
7233 if (features)
7234 len += lstrlenW( features );
7235 else
7236 len += ARRAY_SIZE( L"ALL" );
7238 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7240 msi_free( product );
7241 msi_free( features );
7242 return ERROR_OUTOFMEMORY;
7244 swprintf( cmd, len, L"msiexec /qn /i %s REMOVE=%s", product, features ? features : L"ALL" );
7245 msi_free( product );
7246 msi_free( features );
7248 memset( &si, 0, sizeof(STARTUPINFOW) );
7249 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7250 msi_free( cmd );
7251 if (!ret) return GetLastError();
7252 CloseHandle( info.hThread );
7254 WaitForSingleObject( info.hProcess, INFINITE );
7255 CloseHandle( info.hProcess );
7256 return ERROR_SUCCESS;
7259 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7261 MSIQUERY *view;
7262 UINT r;
7264 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Upgrade`", &view );
7265 if (r == ERROR_SUCCESS)
7267 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7268 msiobj_release( &view->hdr );
7269 if (r != ERROR_SUCCESS)
7270 return r;
7272 return ERROR_SUCCESS;
7275 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7277 MSIPACKAGE *package = param;
7278 int attributes = MSI_RecordGetInteger( rec, 5 );
7280 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7282 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7283 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7284 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7285 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7286 HKEY hkey;
7287 UINT r;
7289 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7291 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7292 if (r != ERROR_SUCCESS)
7293 return ERROR_SUCCESS;
7295 else
7297 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7298 if (r != ERROR_SUCCESS)
7299 return ERROR_SUCCESS;
7301 RegCloseKey( hkey );
7303 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7304 debugstr_w(upgrade_code), debugstr_w(version_min),
7305 debugstr_w(version_max), debugstr_w(language));
7307 return ERROR_SUCCESS;
7310 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7312 MSIQUERY *view;
7313 UINT r;
7315 if (msi_get_property_int( package->db, L"Installed", 0 ))
7317 TRACE("product is installed, skipping action\n");
7318 return ERROR_SUCCESS;
7320 if (msi_get_property_int( package->db, L"Preselected", 0 ))
7322 TRACE("Preselected property is set, not migrating feature states\n");
7323 return ERROR_SUCCESS;
7325 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Upgrade`", &view );
7326 if (r == ERROR_SUCCESS)
7328 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7329 msiobj_release( &view->hdr );
7330 if (r != ERROR_SUCCESS)
7331 return r;
7333 return ERROR_SUCCESS;
7336 static BOOL msi_bind_image( MSIPACKAGE *package, const char *filename, const char *path )
7338 BOOL ret;
7339 msi_disable_fs_redirection( package );
7340 ret = BindImage( filename, path, NULL );
7341 msi_revert_fs_redirection( package );
7342 return ret;
7345 static void bind_image( MSIPACKAGE *package, const char *filename, const char *path )
7347 if (!msi_bind_image( package, filename, path ))
7349 WARN("failed to bind image %u\n", GetLastError());
7353 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7355 UINT i;
7356 MSIFILE *file;
7357 MSIPACKAGE *package = param;
7358 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7359 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7360 char *filenameA, *pathA;
7361 WCHAR *pathW, **path_list;
7363 if (!(file = msi_get_loaded_file( package, key )))
7365 WARN("file %s not found\n", debugstr_w(key));
7366 return ERROR_SUCCESS;
7368 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7370 path_list = msi_split_string( paths, ';' );
7371 if (!path_list) bind_image( package, filenameA, NULL );
7372 else
7374 for (i = 0; path_list[i] && path_list[i][0]; i++)
7376 deformat_string( package, path_list[i], &pathW );
7377 if ((pathA = strdupWtoA( pathW )))
7379 bind_image( package, filenameA, pathA );
7380 msi_free( pathA );
7382 msi_free( pathW );
7385 msi_free( path_list );
7386 msi_free( filenameA );
7388 return ERROR_SUCCESS;
7391 static UINT ACTION_BindImage( MSIPACKAGE *package )
7393 MSIQUERY *view;
7394 UINT r;
7396 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `BindImage`", &view );
7397 if (r == ERROR_SUCCESS)
7399 MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7400 msiobj_release( &view->hdr );
7402 return ERROR_SUCCESS;
7405 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7407 MSIQUERY *view;
7408 DWORD count = 0;
7409 UINT r;
7411 r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `%s`", table );
7412 if (r == ERROR_SUCCESS)
7414 r = MSI_IterateRecords(view, &count, NULL, package);
7415 msiobj_release(&view->hdr);
7416 if (r != ERROR_SUCCESS)
7417 return r;
7419 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7420 return ERROR_SUCCESS;
7423 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7425 return msi_unimplemented_action_stub( package, "IsolateComponents", L"IsolateComponent" );
7428 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7430 return msi_unimplemented_action_stub( package, "RMCCPSearch", L"CCPSearch" );
7433 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7435 return msi_unimplemented_action_stub( package, "RegisterComPlus", L"Complus" );
7438 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7440 return msi_unimplemented_action_stub( package, "UnregisterComPlus", L"Complus" );
7443 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7445 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", L"SFPCatalog" );
7448 static const struct
7450 const WCHAR *action;
7451 const UINT description;
7452 const UINT template;
7453 UINT (*handler)(MSIPACKAGE *);
7454 const WCHAR *action_rollback;
7456 StandardActions[] =
7458 { L"AllocateRegistrySpace", IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7459 { L"AppSearch", IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7460 { L"BindImage", IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7461 { L"CCPSearch", IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7462 { L"CostFinalize", IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7463 { L"CostInitialize", IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7464 { L"CreateFolders", IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, L"RemoveFolders" },
7465 { L"CreateShortcuts", IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, L"RemoveShortcuts" },
7466 { L"DeleteServices", IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, L"InstallServices" },
7467 { L"DisableRollback", 0, 0, ACTION_DisableRollback, NULL },
7468 { L"DuplicateFiles", IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, L"RemoveDuplicateFiles" },
7469 { L"ExecuteAction", 0, 0, ACTION_ExecuteAction, NULL },
7470 { L"FileCost", IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7471 { L"FindRelatedProducts", IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7472 { L"ForceReboot", 0, 0, ACTION_ForceReboot, NULL },
7473 { L"InstallAdminPackage", IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7474 { L"InstallExecute", 0, 0, ACTION_InstallExecute, NULL },
7475 { L"InstallExecuteAgain", 0, 0, ACTION_InstallExecute, NULL },
7476 { L"InstallFiles", IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, L"RemoveFiles" },
7477 { L"InstallFinalize", 0, 0, ACTION_InstallFinalize, NULL },
7478 { L"InstallInitialize", 0, 0, ACTION_InstallInitialize, NULL },
7479 { L"InstallODBC", IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, L"RemoveODBC" },
7480 { L"InstallServices", IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, L"DeleteServices" },
7481 { L"InstallSFPCatalogFile", IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7482 { L"InstallValidate", IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7483 { L"IsolateComponents", 0, 0, ACTION_IsolateComponents, NULL },
7484 { L"LaunchConditions", IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7485 { L"MigrateFeutureStates", IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7486 { L"MoveFiles", IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7487 { L"MsiPublishAssemblies", IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, L"MsiUnpublishAssemblies" },
7488 { L"MsiUnpublishAssemblies", IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, L"MsiPublishAssemblies" },
7489 { L"PatchFiles", IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7490 { L"ProcessComponents", IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, L"ProcessComponents" },
7491 { L"PublishComponents", IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, L"UnpublishComponents" },
7492 { L"PublishFeatures", IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, L"UnpublishFeatures" },
7493 { L"PublishProduct", IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, L"UnpublishProduct" },
7494 { L"RegisterClassInfo", IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, L"UnregisterClassInfo" },
7495 { L"RegisterComPlus", IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, L"UnregisterComPlus" },
7496 { L"RegisterExtensionInfo", IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, L"UnregisterExtensionInfo" },
7497 { L"RegisterFonts", IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, L"UnregisterFonts" },
7498 { L"RegisterMIMEInfo", IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, L"UnregisterMIMEInfo" },
7499 { L"RegisterProduct", IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
7500 { L"RegisterProgIdInfo", IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, L"UnregisterProgIdInfo" },
7501 { L"RegisterTypeLibraries", IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, L"UnregisterTypeLibraries" },
7502 { L"RegisterUser", IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
7503 { L"RemoveDuplicateFiles", IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, L"DuplicateFiles" },
7504 { L"RemoveEnvironmentStrings", IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, L"WriteEnvironmentStrings" },
7505 { L"RemoveExistingProducts", IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
7506 { L"RemoveFiles", IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, L"InstallFiles" },
7507 { L"RemoveFolders", IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, L"CreateFolders" },
7508 { L"RemoveIniValues", IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, L"WriteIniValues" },
7509 { L"RemoveODBC", IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, L"InstallODBC" },
7510 { L"RemoveRegistryValues", IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, L"WriteRegistryValues" },
7511 { L"RemoveShortcuts", IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, L"CreateShortcuts" },
7512 { L"ResolveSource", 0, 0, ACTION_ResolveSource, NULL },
7513 { L"RMCCPSearch", IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
7514 { L"ScheduleReboot", 0, 0, ACTION_ScheduleReboot, NULL },
7515 { L"SelfRegModules", IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, L"SelfUnregModules" },
7516 { L"SelfUnregModules", IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, L"SelfRegModules" },
7517 { L"SetODBCFolders", IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
7518 { L"StartServices", IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, L"StopServices" },
7519 { L"StopServices", IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, L"StartServices" },
7520 { L"UnpublishComponents", IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, L"PublishComponents" },
7521 { L"UnpublishFeatures", IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, L"PublishFeatures" },
7522 { L"UnpublishProduct", IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7523 { L"UnregisterClassInfo", IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, L"RegisterClassInfo" },
7524 { L"UnregisterComPlus", IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, L"RegisterComPlus" },
7525 { L"UnregisterExtensionInfo", IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, L"RegisterExtensionInfo" },
7526 { L"UnregisterFonts", IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, L"RegisterFonts" },
7527 { L"UnregisterMIMEInfo", IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, L"RegisterMIMEInfo" },
7528 { L"UnregisterProgIdInfo", IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, L"RegisterProgIdInfo" },
7529 { L"UnregisterTypeLibraries", IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, L"RegisterTypeLibraries" },
7530 { L"ValidateProductID", 0, 0, ACTION_ValidateProductID, NULL },
7531 { L"WriteEnvironmentStrings", IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, L"RemoveEnvironmentStrings" },
7532 { L"WriteIniValues", IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, L"RemoveIniValues" },
7533 { L"WriteRegistryValues", IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, L"RemoveRegistryValues" },
7534 { L"INSTALL", 0, 0, ACTION_INSTALL, NULL },
7535 { 0 }
7538 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
7540 UINT rc = ERROR_FUNCTION_NOT_CALLED;
7541 UINT i;
7543 i = 0;
7544 while (StandardActions[i].action != NULL)
7546 if (!wcscmp( StandardActions[i].action, action ))
7548 WCHAR description[100] = {0}, template[100] = {0};
7550 if (StandardActions[i].description != 0)
7551 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
7552 if (StandardActions[i].template != 0)
7553 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
7555 ui_actionstart(package, action, description, template);
7556 if (StandardActions[i].handler)
7558 ui_actioninfo( package, action, TRUE, 0 );
7559 rc = StandardActions[i].handler( package );
7560 ui_actioninfo( package, action, FALSE, !rc );
7562 if (StandardActions[i].action_rollback && !package->need_rollback)
7564 TRACE("scheduling rollback action\n");
7565 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7568 else
7570 FIXME("unhandled standard action %s\n", debugstr_w(action));
7571 rc = ERROR_SUCCESS;
7573 break;
7575 i++;
7578 return rc;
7581 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
7583 UINT rc;
7585 TRACE("Performing action (%s)\n", debugstr_w(action));
7587 package->action_progress_increment = 0;
7588 rc = ACTION_HandleStandardAction(package, action);
7590 if (rc == ERROR_FUNCTION_NOT_CALLED)
7591 rc = ACTION_HandleCustomAction(package, action);
7593 if (rc == ERROR_FUNCTION_NOT_CALLED)
7594 WARN("unhandled msi action %s\n", debugstr_w(action));
7596 return rc;
7599 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7601 UINT rc = ERROR_SUCCESS;
7602 MSIRECORD *row;
7604 if (needs_ui_sequence(package))
7605 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `InstallUISequence` WHERE `Sequence` = %d", seq);
7606 else
7607 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `InstallExecuteSequence` WHERE `Sequence` = %d", seq);
7609 if (row)
7611 LPCWSTR action, cond;
7613 TRACE("Running the actions\n");
7615 /* check conditions */
7616 cond = MSI_RecordGetString(row, 2);
7618 /* this is a hack to skip errors in the condition code */
7619 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7621 msiobj_release(&row->hdr);
7622 return ERROR_SUCCESS;
7625 action = MSI_RecordGetString(row, 1);
7626 if (!action)
7628 ERR("failed to fetch action\n");
7629 msiobj_release(&row->hdr);
7630 return ERROR_FUNCTION_FAILED;
7633 rc = ACTION_PerformAction(package, action);
7635 msiobj_release(&row->hdr);
7638 return rc;
7641 /****************************************************
7642 * TOP level entry points
7643 *****************************************************/
7645 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7646 LPCWSTR szCommandLine )
7648 WCHAR *reinstall = NULL, *productcode, *action;
7649 UINT rc;
7650 DWORD len = 0;
7652 if (szPackagePath)
7654 LPWSTR p, dir;
7655 LPCWSTR file;
7657 dir = strdupW(szPackagePath);
7658 p = wcsrchr(dir, '\\');
7659 if (p)
7661 *(++p) = 0;
7662 file = szPackagePath + (p - dir);
7664 else
7666 msi_free(dir);
7667 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7668 GetCurrentDirectoryW(MAX_PATH, dir);
7669 lstrcatW(dir, L"\\");
7670 file = szPackagePath;
7673 msi_free( package->PackagePath );
7674 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7675 if (!package->PackagePath)
7677 msi_free(dir);
7678 return ERROR_OUTOFMEMORY;
7681 lstrcpyW(package->PackagePath, dir);
7682 lstrcatW(package->PackagePath, file);
7683 msi_free(dir);
7685 msi_set_sourcedir_props(package, FALSE);
7688 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7689 if (rc != ERROR_SUCCESS)
7690 return rc;
7692 msi_apply_transforms( package );
7693 msi_apply_patches( package );
7695 if (msi_get_property( package->db, L"ACTION", NULL, &len ))
7696 msi_set_property( package->db, L"ACTION", L"INSTALL", -1 );
7697 action = msi_dup_property( package->db, L"ACTION" );
7698 CharUpperW(action);
7700 msi_set_original_database_property( package->db, szPackagePath );
7701 msi_parse_command_line( package, szCommandLine, FALSE );
7702 msi_adjust_privilege_properties( package );
7703 msi_set_context( package );
7705 productcode = msi_dup_property( package->db, L"ProductCode" );
7706 if (wcsicmp( productcode, package->ProductCode ))
7708 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7709 msi_free( package->ProductCode );
7710 package->ProductCode = productcode;
7712 else msi_free( productcode );
7714 if (msi_get_property_int( package->db, L"DISABLEROLLBACK", 0 ))
7716 TRACE("disabling rollback\n");
7717 msi_set_property( package->db, L"RollbackDisabled", L"1", -1 );
7720 rc = ACTION_PerformAction(package, action);
7722 /* process the ending type action */
7723 if (rc == ERROR_SUCCESS)
7724 ACTION_PerformActionSequence(package, -1);
7725 else if (rc == ERROR_INSTALL_USEREXIT)
7726 ACTION_PerformActionSequence(package, -2);
7727 else if (rc == ERROR_INSTALL_SUSPEND)
7728 ACTION_PerformActionSequence(package, -4);
7729 else /* failed */
7731 ACTION_PerformActionSequence(package, -3);
7732 if (!msi_get_property_int( package->db, L"RollbackDisabled", 0 ))
7734 package->need_rollback = TRUE;
7738 /* finish up running custom actions */
7739 ACTION_FinishCustomActions(package);
7741 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, L"REINSTALL" )))
7743 WARN("installation failed, running rollback script\n");
7744 execute_script( package, SCRIPT_ROLLBACK );
7746 msi_free( reinstall );
7747 msi_free( action );
7749 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7750 return ERROR_SUCCESS_REBOOT_REQUIRED;
7752 return rc;