ntdll: Use a separate memory allocation for the kernel stack.
[wine.git] / dlls / msi / action.c
blob8b3d1c42aa52e2842139f684d6e168483635f652
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, const WCHAR *action, BOOL start, INT rc)
74 MSIRECORD *row;
75 WCHAR *template;
77 template = msi_get_error_message(package->db, start ? MSIERR_INFO_ACTIONSTART : MSIERR_INFO_ACTIONENDED);
79 row = MSI_CreateRecord(2);
80 if (!row)
82 free(template);
83 return;
85 MSI_RecordSetStringW(row, 0, template);
86 MSI_RecordSetStringW(row, 1, action);
87 MSI_RecordSetInteger(row, 2, start ? package->LastActionResult : rc);
88 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
89 msiobj_release(&row->hdr);
90 free(template);
91 if (!start) package->LastActionResult = rc;
94 enum parse_state
96 state_whitespace,
97 state_token,
98 state_quote
101 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
103 enum parse_state state = state_quote;
104 const WCHAR *p;
105 WCHAR *out = value;
106 BOOL ignore, in_quotes = FALSE;
107 int count = 0, len = 0;
109 for (p = str; *p; p++)
111 ignore = FALSE;
112 switch (state)
114 case state_whitespace:
115 switch (*p)
117 case ' ':
118 in_quotes = TRUE;
119 ignore = TRUE;
120 len++;
121 break;
122 case '"':
123 state = state_quote;
124 if (in_quotes && p[1] != '\"') count--;
125 else count++;
126 break;
127 default:
128 state = state_token;
129 in_quotes = TRUE;
130 len++;
131 break;
133 break;
135 case state_token:
136 switch (*p)
138 case '"':
139 state = state_quote;
140 if (in_quotes) count--;
141 else count++;
142 break;
143 case ' ':
144 state = state_whitespace;
145 if (!count) goto done;
146 in_quotes = TRUE;
147 len++;
148 break;
149 default:
150 if (count) in_quotes = TRUE;
151 len++;
152 break;
154 break;
156 case state_quote:
157 switch (*p)
159 case '"':
160 if (in_quotes && p[1] != '\"') count--;
161 else count++;
162 break;
163 case ' ':
164 state = state_whitespace;
165 if (!count || (count > 1 && !len)) goto done;
166 in_quotes = TRUE;
167 len++;
168 break;
169 default:
170 state = state_token;
171 if (count) in_quotes = TRUE;
172 len++;
173 break;
175 break;
177 default: break;
179 if (!ignore && value) *out++ = *p;
180 if (!count) in_quotes = FALSE;
183 done:
184 if (value)
186 if (!len) *value = 0;
187 else *out = 0;
190 if(quotes) *quotes = count;
191 return p - str;
194 static void remove_quotes( WCHAR *str )
196 WCHAR *p = str;
197 int len = lstrlenW( str );
199 while ((p = wcschr( p, '"' )))
201 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
202 p++;
206 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
207 BOOL preserve_case )
209 LPCWSTR ptr, ptr2;
210 int num_quotes;
211 DWORD len;
212 WCHAR *prop, *val;
213 UINT r;
215 if (!szCommandLine)
216 return ERROR_SUCCESS;
218 ptr = szCommandLine;
219 while (*ptr)
221 while (*ptr == ' ') ptr++;
222 if (!*ptr) break;
224 ptr2 = wcschr( ptr, '=' );
225 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
227 len = ptr2 - ptr;
228 if (!len) return ERROR_INVALID_COMMAND_LINE;
230 while (ptr[len - 1] == ' ') len--;
232 prop = malloc( (len + 1) * sizeof(WCHAR) );
233 memcpy( prop, ptr, len * sizeof(WCHAR) );
234 prop[len] = 0;
235 if (!preserve_case) wcsupr( prop );
237 ptr2++;
238 while (*ptr2 == ' ') ptr2++;
240 num_quotes = 0;
241 val = malloc( (wcslen( ptr2 ) + 1) * sizeof(WCHAR) );
242 len = parse_prop( ptr2, val, &num_quotes );
243 if (num_quotes % 2)
245 WARN("unbalanced quotes\n");
246 free( val );
247 free( prop );
248 return ERROR_INVALID_COMMAND_LINE;
250 remove_quotes( val );
251 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
253 r = msi_set_property( package->db, prop, val, -1 );
254 if (r == ERROR_SUCCESS && !wcscmp( prop, L"SourceDir" ))
255 msi_reset_source_folders( package );
257 free( val );
258 free( prop );
260 ptr = ptr2 + len;
263 return ERROR_SUCCESS;
266 const WCHAR *msi_get_command_line_option(const WCHAR *cmd, const WCHAR *option, UINT *len)
268 DWORD opt_len = lstrlenW(option);
270 if (!cmd)
271 return NULL;
273 while (*cmd)
275 BOOL found = FALSE;
277 while (*cmd == ' ') cmd++;
278 if (!*cmd) break;
280 if(!wcsnicmp(cmd, option, opt_len))
281 found = TRUE;
283 cmd = wcschr( cmd, '=' );
284 if(!cmd) break;
285 cmd++;
286 while (*cmd == ' ') cmd++;
287 if (!*cmd) break;
289 *len = parse_prop( cmd, NULL, NULL);
290 if (found) return cmd;
291 cmd += *len;
294 return NULL;
297 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
299 LPCWSTR pc;
300 LPWSTR p, *ret = NULL;
301 UINT count = 0;
303 if (!str)
304 return ret;
306 /* count the number of substrings */
307 for ( pc = str, count = 0; pc; count++ )
309 pc = wcschr( pc, sep );
310 if (pc)
311 pc++;
314 /* allocate space for an array of substring pointers and the substrings */
315 ret = malloc( (count + 1) * sizeof(WCHAR *) + (wcslen(str) + 1) * sizeof(WCHAR) );
316 if (!ret)
317 return ret;
319 /* copy the string and set the pointers */
320 p = (LPWSTR) &ret[count+1];
321 lstrcpyW( p, str );
322 for( count = 0; (ret[count] = p); count++ )
324 p = wcschr( p, sep );
325 if (p)
326 *p++ = 0;
329 return ret;
332 static BOOL ui_sequence_exists( MSIPACKAGE *package )
334 MSIQUERY *view;
335 DWORD count = 0;
337 if (!(MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `InstallUISequence` WHERE `Sequence` > 0", &view )))
339 MSI_IterateRecords( view, &count, NULL, package );
340 msiobj_release( &view->hdr );
342 return count != 0;
345 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
347 WCHAR *source, *check, *p, *db;
348 DWORD len;
350 if (!(db = msi_dup_property( package->db, L"OriginalDatabase" )))
351 return ERROR_OUTOFMEMORY;
353 if (!(p = wcsrchr( db, '\\' )) && !(p = wcsrchr( db, '/' )))
355 free(db);
356 return ERROR_SUCCESS;
358 len = p - db + 2;
359 source = malloc( len * sizeof(WCHAR) );
360 lstrcpynW( source, db, len );
361 free( db );
363 check = msi_dup_property( package->db, L"SourceDir" );
364 if (!check || replace)
366 UINT r = msi_set_property( package->db, L"SourceDir", source, -1 );
367 if (r == ERROR_SUCCESS)
368 msi_reset_source_folders( package );
370 free( check );
372 check = msi_dup_property( package->db, L"SOURCEDIR" );
373 if (!check || replace)
374 msi_set_property( package->db, L"SOURCEDIR", source, -1 );
376 free( check );
377 free( source );
379 return ERROR_SUCCESS;
382 static BOOL needs_ui_sequence(MSIPACKAGE *package)
384 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
387 UINT msi_set_context(MSIPACKAGE *package)
389 UINT r = msi_locate_product( package->ProductCode, &package->Context );
390 if (r != ERROR_SUCCESS)
392 int num = msi_get_property_int( package->db, L"ALLUSERS", 0 );
393 if (num == 1 || num == 2)
394 package->Context = MSIINSTALLCONTEXT_MACHINE;
395 else
396 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
398 return ERROR_SUCCESS;
401 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
403 UINT rc;
404 LPCWSTR cond, action;
405 MSIPACKAGE *package = param;
407 action = MSI_RecordGetString(row,1);
408 if (!action)
410 ERR("Error is retrieving action name\n");
411 return ERROR_FUNCTION_FAILED;
414 /* check conditions */
415 cond = MSI_RecordGetString(row,2);
417 /* this is a hack to skip errors in the condition code */
418 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
420 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
421 return ERROR_SUCCESS;
424 rc = ACTION_PerformAction(package, action);
426 msi_dialog_check_messages( NULL );
428 if (rc == ERROR_FUNCTION_NOT_CALLED)
429 rc = ERROR_SUCCESS;
431 if (rc != ERROR_SUCCESS)
432 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
434 if (package->need_reboot_now)
436 TRACE("action %s asked for immediate reboot, suspending installation\n",
437 debugstr_w(action));
438 rc = ACTION_ForceReboot( package );
440 return rc;
443 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
445 MSIQUERY *view;
446 UINT r;
448 TRACE("%p %s\n", package, debugstr_w(table));
450 r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `%s` WHERE `Sequence` > 0 ORDER BY `Sequence`", table );
451 if (r == ERROR_SUCCESS)
453 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
454 msiobj_release(&view->hdr);
456 return r;
459 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package)
461 MSIQUERY *view;
462 UINT rc;
464 if (package->ExecuteSequenceRun)
466 TRACE("Execute Sequence already Run\n");
467 return ERROR_SUCCESS;
470 package->ExecuteSequenceRun = TRUE;
472 rc = MSI_OpenQuery(package->db, &view,
473 L"SELECT * FROM `InstallExecuteSequence` WHERE `Sequence` > 0 ORDER BY `Sequence`");
474 if (rc == ERROR_SUCCESS)
476 TRACE("Running the actions\n");
478 msi_set_property( package->db, L"SourceDir", NULL, -1 );
479 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
480 msiobj_release(&view->hdr);
482 return rc;
485 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
487 MSIQUERY *view;
488 UINT rc;
490 rc = MSI_DatabaseOpenViewW(package->db,
491 L"SELECT * FROM `InstallUISequence` WHERE `Sequence` > 0 ORDER BY `Sequence`",
492 &view);
493 if (rc == ERROR_SUCCESS)
495 TRACE("Running the actions\n");
496 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
497 msiobj_release(&view->hdr);
499 return rc;
502 /********************************************************
503 * ACTION helper functions and functions that perform the actions
504 *******************************************************/
505 static UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action)
507 UINT arc;
508 INT uirc;
510 uirc = ui_actionstart(package, action, NULL, NULL);
511 if (uirc == IDCANCEL)
512 return ERROR_INSTALL_USEREXIT;
513 ui_actioninfo(package, action, TRUE, 0);
514 arc = ACTION_CustomAction(package, action);
515 uirc = !arc;
517 if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package))
519 uirc = ACTION_ShowDialog(package, action);
520 switch (uirc)
522 case -1:
523 return ERROR_SUCCESS; /* stop immediately */
524 case 0: arc = ERROR_FUNCTION_NOT_CALLED; break;
525 case 1: arc = ERROR_SUCCESS; break;
526 case 2: arc = ERROR_INSTALL_USEREXIT; break;
527 case 3: arc = ERROR_INSTALL_FAILURE; break;
528 case 4: arc = ERROR_INSTALL_SUSPEND; break;
529 case 5: arc = ERROR_MORE_DATA; break;
530 case 6: arc = ERROR_INVALID_HANDLE_STATE; break;
531 case 7: arc = ERROR_INVALID_DATA; break;
532 case 8: arc = ERROR_INSTALL_ALREADY_RUNNING; break;
533 case 9: arc = ERROR_INSTALL_PACKAGE_REJECTED; break;
534 default: arc = ERROR_FUNCTION_FAILED; break;
538 ui_actioninfo(package, action, FALSE, uirc);
540 return arc;
543 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
545 MSICOMPONENT *comp;
547 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
549 if (!wcscmp( Component, comp->Component )) return comp;
551 return NULL;
554 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
556 MSIFEATURE *feature;
558 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
560 if (!wcscmp( Feature, feature->Feature )) return feature;
562 return NULL;
565 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
567 MSIFILE *file;
569 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
571 if (!wcscmp( key, file->File )) return file;
573 return NULL;
576 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
578 MSIFOLDER *folder;
580 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
582 if (!wcscmp( dir, folder->Directory )) return folder;
584 return NULL;
587 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
589 MSIRECORD *row;
591 row = MSI_CreateRecord( 4 );
592 MSI_RecordSetInteger( row, 1, a );
593 MSI_RecordSetInteger( row, 2, b );
594 MSI_RecordSetInteger( row, 3, c );
595 MSI_RecordSetInteger( row, 4, d );
596 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
597 msiobj_release( &row->hdr );
599 msi_dialog_check_messages( NULL );
602 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
604 if (!comp->Enabled)
606 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
607 return INSTALLSTATE_UNKNOWN;
609 if (package->need_rollback) return comp->Installed;
610 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
612 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
613 return INSTALLSTATE_UNKNOWN;
615 return comp->ActionRequest;
618 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
620 if (package->need_rollback) return feature->Installed;
621 return feature->ActionRequest;
624 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
626 MSIPACKAGE *package = param;
627 LPCWSTR dir, component, full_path;
628 MSIRECORD *uirow;
629 MSIFOLDER *folder;
630 MSICOMPONENT *comp;
632 component = MSI_RecordGetString(row, 2);
633 if (!component)
634 return ERROR_SUCCESS;
636 comp = msi_get_loaded_component(package, component);
637 if (!comp)
638 return ERROR_SUCCESS;
640 comp->Action = msi_get_component_action( package, comp );
641 if (comp->Action != INSTALLSTATE_LOCAL)
643 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
644 return ERROR_SUCCESS;
647 dir = MSI_RecordGetString(row,1);
648 if (!dir)
650 ERR("Unable to get folder id\n");
651 return ERROR_SUCCESS;
654 uirow = MSI_CreateRecord(1);
655 MSI_RecordSetStringW(uirow, 1, dir);
656 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
657 msiobj_release(&uirow->hdr);
659 full_path = msi_get_target_folder( package, dir );
660 if (!full_path)
662 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
663 return ERROR_SUCCESS;
665 TRACE("folder is %s\n", debugstr_w(full_path));
667 folder = msi_get_loaded_folder( package, dir );
668 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( package, full_path );
669 folder->State = FOLDER_STATE_CREATED;
671 return ERROR_SUCCESS;
674 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
676 MSIQUERY *view;
677 UINT rc;
679 if (package->script == SCRIPT_NONE)
680 return msi_schedule_action(package, SCRIPT_INSTALL, L"CreateFolders");
682 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `CreateFolder`", &view );
683 if (rc != ERROR_SUCCESS)
684 return ERROR_SUCCESS;
686 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
687 msiobj_release(&view->hdr);
688 return rc;
691 static void remove_persistent_folder( MSIFOLDER *folder )
693 FolderList *fl;
695 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
697 remove_persistent_folder( fl->folder );
699 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
701 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
705 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
707 MSIPACKAGE *package = param;
708 LPCWSTR dir, component, full_path;
709 MSIRECORD *uirow;
710 MSIFOLDER *folder;
711 MSICOMPONENT *comp;
713 component = MSI_RecordGetString(row, 2);
714 if (!component)
715 return ERROR_SUCCESS;
717 comp = msi_get_loaded_component(package, component);
718 if (!comp)
719 return ERROR_SUCCESS;
721 comp->Action = msi_get_component_action( package, comp );
722 if (comp->Action != INSTALLSTATE_ABSENT)
724 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
725 return ERROR_SUCCESS;
728 dir = MSI_RecordGetString( row, 1 );
729 if (!dir)
731 ERR("Unable to get folder id\n");
732 return ERROR_SUCCESS;
735 full_path = msi_get_target_folder( package, dir );
736 if (!full_path)
738 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
739 return ERROR_SUCCESS;
741 TRACE("folder is %s\n", debugstr_w(full_path));
743 uirow = MSI_CreateRecord( 1 );
744 MSI_RecordSetStringW( uirow, 1, dir );
745 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
746 msiobj_release( &uirow->hdr );
748 folder = msi_get_loaded_folder( package, dir );
749 remove_persistent_folder( folder );
750 return ERROR_SUCCESS;
753 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
755 MSIQUERY *view;
756 UINT rc;
758 if (package->script == SCRIPT_NONE)
759 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveFolders");
761 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `CreateFolder`", &view );
762 if (rc != ERROR_SUCCESS)
763 return ERROR_SUCCESS;
765 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
766 msiobj_release( &view->hdr );
767 return rc;
770 static UINT load_component( MSIRECORD *row, LPVOID param )
772 MSIPACKAGE *package = param;
773 MSICOMPONENT *comp;
775 comp = calloc( 1, sizeof(MSICOMPONENT) );
776 if (!comp)
777 return ERROR_FUNCTION_FAILED;
779 list_add_tail( &package->components, &comp->entry );
781 /* fill in the data */
782 comp->Component = msi_dup_record_field( row, 1 );
784 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
786 comp->ComponentId = msi_dup_record_field( row, 2 );
787 comp->Directory = msi_dup_record_field( row, 3 );
788 comp->Attributes = MSI_RecordGetInteger(row,4);
789 comp->Condition = msi_dup_record_field( row, 5 );
790 comp->KeyPath = msi_dup_record_field( row, 6 );
792 comp->Installed = INSTALLSTATE_UNKNOWN;
793 comp->Action = INSTALLSTATE_UNKNOWN;
794 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
796 comp->assembly = msi_load_assembly( package, comp );
797 return ERROR_SUCCESS;
800 UINT msi_load_all_components( MSIPACKAGE *package )
802 MSIQUERY *view;
803 UINT r;
805 if (!list_empty(&package->components))
806 return ERROR_SUCCESS;
808 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Component`", &view );
809 if (r != ERROR_SUCCESS)
810 return r;
812 r = MSI_IterateRecords(view, NULL, load_component, package);
813 msiobj_release(&view->hdr);
814 return r;
817 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
819 ComponentList *cl;
821 cl = malloc( sizeof(*cl) );
822 if ( !cl )
823 return ERROR_NOT_ENOUGH_MEMORY;
824 cl->component = comp;
825 list_add_tail( &feature->Components, &cl->entry );
827 return ERROR_SUCCESS;
830 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
832 FeatureList *fl;
834 fl = malloc( sizeof(*fl) );
835 if ( !fl )
836 return ERROR_NOT_ENOUGH_MEMORY;
837 fl->feature = child;
838 list_add_tail( &parent->Children, &fl->entry );
840 return ERROR_SUCCESS;
843 struct package_feature
845 MSIPACKAGE *package;
846 MSIFEATURE *feature;
849 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
851 struct package_feature *package_feature = param;
852 LPCWSTR component;
853 MSICOMPONENT *comp;
855 component = MSI_RecordGetString(row,1);
857 /* check to see if the component is already loaded */
858 comp = msi_get_loaded_component( package_feature->package, component );
859 if (!comp)
861 WARN("ignoring unknown component %s\n", debugstr_w(component));
862 return ERROR_SUCCESS;
864 add_feature_component( package_feature->feature, comp );
865 comp->Enabled = TRUE;
867 return ERROR_SUCCESS;
870 static UINT load_feature(MSIRECORD *row, LPVOID param)
872 MSIPACKAGE *package = param;
873 MSIFEATURE *feature;
874 MSIQUERY *view;
875 struct package_feature package_feature;
876 UINT rc;
878 /* fill in the data */
880 feature = calloc( 1, sizeof(MSIFEATURE) );
881 if (!feature)
882 return ERROR_NOT_ENOUGH_MEMORY;
884 list_init( &feature->Children );
885 list_init( &feature->Components );
887 feature->Feature = msi_dup_record_field( row, 1 );
889 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
891 feature->Feature_Parent = msi_dup_record_field( row, 2 );
892 feature->Title = msi_dup_record_field( row, 3 );
893 feature->Description = msi_dup_record_field( row, 4 );
895 if (!MSI_RecordIsNull(row,5))
896 feature->Display = MSI_RecordGetInteger(row,5);
898 feature->Level= MSI_RecordGetInteger(row,6);
899 feature->Directory = msi_dup_record_field( row, 7 );
900 feature->Attributes = MSI_RecordGetInteger(row,8);
902 feature->Installed = INSTALLSTATE_UNKNOWN;
903 feature->Action = INSTALLSTATE_UNKNOWN;
904 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
906 list_add_tail( &package->features, &feature->entry );
908 /* load feature components */
910 rc = MSI_OpenQuery( package->db, &view, L"SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = '%s'",
911 feature->Feature );
912 if (rc != ERROR_SUCCESS)
913 return ERROR_SUCCESS;
915 package_feature.package = package;
916 package_feature.feature = feature;
918 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents, &package_feature);
919 msiobj_release(&view->hdr);
920 return rc;
923 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
925 MSIPACKAGE *package = param;
926 MSIFEATURE *parent, *child;
928 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
929 if (!child)
930 return ERROR_FUNCTION_FAILED;
932 if (!child->Feature_Parent)
933 return ERROR_SUCCESS;
935 parent = msi_get_loaded_feature( package, child->Feature_Parent );
936 if (!parent)
937 return ERROR_FUNCTION_FAILED;
939 add_feature_child( parent, child );
940 return ERROR_SUCCESS;
943 UINT msi_load_all_features( MSIPACKAGE *package )
945 MSIQUERY *view;
946 UINT r;
948 if (!list_empty(&package->features))
949 return ERROR_SUCCESS;
951 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Feature` ORDER BY `Display`", &view );
952 if (r != ERROR_SUCCESS)
953 return r;
955 r = MSI_IterateRecords( view, NULL, load_feature, package );
956 if (r != ERROR_SUCCESS)
958 msiobj_release( &view->hdr );
959 return r;
961 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
962 msiobj_release( &view->hdr );
963 return r;
966 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
968 if (!p)
969 return p;
970 p = wcschr(p, ch);
971 if (!p)
972 return p;
973 *p = 0;
974 return p+1;
977 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
979 MSIQUERY *view = NULL;
980 MSIRECORD *row = NULL;
981 UINT r;
983 TRACE("%s\n", debugstr_w(file->File));
985 r = MSI_OpenQuery(package->db, &view, L"SELECT * FROM `MsiFileHash` WHERE `File_` = '%s'", file->File);
986 if (r != ERROR_SUCCESS)
987 goto done;
989 r = MSI_ViewExecute(view, NULL);
990 if (r != ERROR_SUCCESS)
991 goto done;
993 r = MSI_ViewFetch(view, &row);
994 if (r != ERROR_SUCCESS)
995 goto done;
997 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
998 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
999 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1000 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1001 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1003 done:
1004 if (view) msiobj_release(&view->hdr);
1005 if (row) msiobj_release(&row->hdr);
1006 return r;
1009 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1011 MSIRECORD *row = MSI_QueryGetRecord( package->db, L"SELECT `DiskId` FROM `Media` WHERE `LastSequence` >= %d",
1012 file->Sequence );
1013 if (!row)
1015 WARN("query failed\n");
1016 return ERROR_FUNCTION_FAILED;
1019 file->disk_id = MSI_RecordGetInteger( row, 1 );
1020 msiobj_release( &row->hdr );
1021 return ERROR_SUCCESS;
1024 static UINT load_file(MSIRECORD *row, LPVOID param)
1026 MSIPACKAGE* package = param;
1027 LPCWSTR component;
1028 MSIFILE *file;
1030 /* fill in the data */
1032 file = calloc( 1, sizeof(MSIFILE) );
1033 if (!file)
1034 return ERROR_NOT_ENOUGH_MEMORY;
1036 file->File = msi_dup_record_field( row, 1 );
1038 component = MSI_RecordGetString( row, 2 );
1039 file->Component = msi_get_loaded_component( package, component );
1041 if (!file->Component)
1043 WARN("Component not found: %s\n", debugstr_w(component));
1044 free(file->File);
1045 free(file);
1046 return ERROR_SUCCESS;
1049 file->FileName = msi_dup_record_field( row, 3 );
1050 msi_reduce_to_long_filename( file->FileName );
1052 file->ShortName = msi_dup_record_field( row, 3 );
1053 file->LongName = wcsdup( folder_split_path(file->ShortName, '|') );
1055 file->FileSize = MSI_RecordGetInteger( row, 4 );
1056 file->Version = msi_dup_record_field( row, 5 );
1057 file->Language = msi_dup_record_field( row, 6 );
1058 file->Attributes = MSI_RecordGetInteger( row, 7 );
1059 file->Sequence = MSI_RecordGetInteger( row, 8 );
1061 file->state = msifs_invalid;
1063 /* if the compressed bits are not set in the file attributes,
1064 * then read the information from the package word count property
1066 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1068 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1070 else if (file->Attributes & (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1072 file->IsCompressed = TRUE;
1074 else if (file->Attributes & msidbFileAttributesNoncompressed)
1076 file->IsCompressed = FALSE;
1078 else file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1080 load_file_hash(package, file);
1081 load_file_disk_id(package, file);
1083 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1085 list_add_tail( &package->files, &file->entry );
1086 return ERROR_SUCCESS;
1089 static UINT load_all_files(MSIPACKAGE *package)
1091 MSIQUERY *view;
1092 UINT rc;
1094 if (!list_empty(&package->files))
1095 return ERROR_SUCCESS;
1097 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `File` ORDER BY `Sequence`", &view);
1098 if (rc != ERROR_SUCCESS)
1099 return ERROR_SUCCESS;
1101 rc = MSI_IterateRecords(view, NULL, load_file, package);
1102 msiobj_release(&view->hdr);
1103 return rc;
1106 static UINT load_media( MSIRECORD *row, LPVOID param )
1108 MSIPACKAGE *package = param;
1109 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1110 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1112 /* FIXME: load external cabinets and directory sources too */
1113 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1114 return ERROR_SUCCESS;
1116 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1119 static UINT load_all_media( MSIPACKAGE *package )
1121 MSIQUERY *view;
1122 UINT r;
1124 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Media` ORDER BY `DiskId`", &view );
1125 if (r != ERROR_SUCCESS)
1126 return ERROR_SUCCESS;
1128 r = MSI_IterateRecords( view, NULL, load_media, package );
1129 msiobj_release( &view->hdr );
1130 return r;
1133 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1135 MSIRECORD *rec = MSI_QueryGetRecord( package->db, L"SELECT `DiskId` FROM `Media` WHERE `LastSequence` >= %u",
1136 patch->Sequence );
1137 if (!rec)
1139 WARN("query failed\n");
1140 return ERROR_FUNCTION_FAILED;
1143 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1144 msiobj_release( &rec->hdr );
1145 return ERROR_SUCCESS;
1148 static UINT load_patch(MSIRECORD *row, LPVOID param)
1150 MSIPACKAGE *package = param;
1151 MSIFILEPATCH *patch;
1152 const WCHAR *file_key;
1154 patch = calloc( 1, sizeof(MSIFILEPATCH) );
1155 if (!patch)
1156 return ERROR_NOT_ENOUGH_MEMORY;
1158 file_key = MSI_RecordGetString( row, 1 );
1159 patch->File = msi_get_loaded_file( package, file_key );
1160 if (!patch->File)
1162 ERR("Failed to find target for patch in File table\n");
1163 free(patch);
1164 return ERROR_FUNCTION_FAILED;
1167 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1168 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1169 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1171 /* FIXME:
1172 * Header field - for patch validation.
1173 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1176 load_patch_disk_id( package, patch );
1178 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1180 list_add_tail( &package->filepatches, &patch->entry );
1182 return ERROR_SUCCESS;
1185 static UINT load_all_patches(MSIPACKAGE *package)
1187 MSIQUERY *view;
1188 UINT rc;
1190 if (!list_empty(&package->filepatches))
1191 return ERROR_SUCCESS;
1193 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Patch` ORDER BY `Sequence`", &view);
1194 if (rc != ERROR_SUCCESS)
1195 return ERROR_SUCCESS;
1197 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1198 msiobj_release(&view->hdr);
1199 return rc;
1202 static UINT iterate_patched_component( MSIRECORD *row, LPVOID param )
1204 MSIPACKAGE *package = param;
1205 const WCHAR *name;
1206 MSICOMPONENT *c;
1208 name = MSI_RecordGetString( row, 1 );
1209 TRACE( "found patched component: %s\n", wine_dbgstr_w(name) );
1210 c = msi_get_loaded_component( package, name );
1211 if (!c)
1212 return ERROR_SUCCESS;
1214 c->updated = 1;
1215 if (!wcscmp( MSI_RecordGetString( row, 2 ), L"INSERT" ))
1216 c->added = 1;
1217 return ERROR_SUCCESS;
1220 static void mark_patched_components( MSIPACKAGE *package )
1222 static const WCHAR select[] = L"SELECT `Row`, `Column` FROM `_TransformView` WHERE `Table`='Component'";
1223 MSIQUERY *q;
1224 UINT r;
1226 r = MSI_OpenQuery( package->db, &q, select );
1227 if (r != ERROR_SUCCESS)
1228 return;
1230 MSI_IterateRecords( q, NULL, iterate_patched_component, package );
1231 msiobj_release( &q->hdr );
1233 while (1)
1235 r = MSI_OpenQuery( package->db, &q, L"ALTER TABLE `_TransformView` FREE" );
1236 if (r != ERROR_SUCCESS)
1237 return;
1238 r = MSI_ViewExecute( q, NULL );
1239 msiobj_release( &q->hdr );
1240 if (r != ERROR_SUCCESS)
1241 return;
1245 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1247 MSIQUERY *view;
1249 folder->persistent = FALSE;
1250 if (!MSI_OpenQuery( package->db, &view, L"SELECT * FROM `CreateFolder` WHERE `Directory_` = '%s'",
1251 folder->Directory ))
1253 if (!MSI_ViewExecute( view, NULL ))
1255 MSIRECORD *rec;
1256 if (!MSI_ViewFetch( view, &rec ))
1258 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1259 folder->persistent = TRUE;
1260 msiobj_release( &rec->hdr );
1263 msiobj_release( &view->hdr );
1265 return ERROR_SUCCESS;
1268 static UINT load_folder( MSIRECORD *row, LPVOID param )
1270 MSIPACKAGE *package = param;
1271 static WCHAR szEmpty[] = L"";
1272 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1273 MSIFOLDER *folder;
1275 if (!(folder = calloc( 1, sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1276 list_init( &folder->children );
1277 folder->Directory = msi_dup_record_field( row, 1 );
1278 folder->Parent = msi_dup_record_field( row, 2 );
1279 p = msi_dup_record_field(row, 3);
1281 TRACE("%s\n", debugstr_w(folder->Directory));
1283 /* split src and target dir */
1284 tgt_short = p;
1285 src_short = folder_split_path( p, ':' );
1287 /* split the long and short paths */
1288 tgt_long = folder_split_path( tgt_short, '|' );
1289 src_long = folder_split_path( src_short, '|' );
1291 /* check for no-op dirs */
1292 if (tgt_short && !wcscmp( L".", tgt_short ))
1293 tgt_short = szEmpty;
1294 if (src_short && !wcscmp( L".", src_short ))
1295 src_short = szEmpty;
1297 if (!tgt_long)
1298 tgt_long = tgt_short;
1300 if (!src_short) {
1301 src_short = tgt_short;
1302 src_long = tgt_long;
1305 if (!src_long)
1306 src_long = src_short;
1308 /* FIXME: use the target short path too */
1309 folder->TargetDefault = wcsdup(tgt_long);
1310 folder->SourceShortPath = wcsdup(src_short);
1311 folder->SourceLongPath = wcsdup(src_long);
1312 free(p);
1314 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1315 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1316 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1318 load_folder_persistence( package, folder );
1320 list_add_tail( &package->folders, &folder->entry );
1321 return ERROR_SUCCESS;
1324 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1326 FolderList *fl;
1328 if (!(fl = malloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1329 fl->folder = child;
1330 list_add_tail( &parent->children, &fl->entry );
1331 return ERROR_SUCCESS;
1334 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1336 MSIPACKAGE *package = param;
1337 MSIFOLDER *parent, *child;
1339 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1340 return ERROR_FUNCTION_FAILED;
1342 if (!child->Parent) return ERROR_SUCCESS;
1344 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1345 return ERROR_FUNCTION_FAILED;
1347 return add_folder_child( parent, child );
1350 static UINT load_all_folders( MSIPACKAGE *package )
1352 MSIQUERY *view;
1353 UINT r;
1355 if (!list_empty(&package->folders))
1356 return ERROR_SUCCESS;
1358 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Directory`", &view );
1359 if (r != ERROR_SUCCESS)
1360 return r;
1362 r = MSI_IterateRecords( view, NULL, load_folder, package );
1363 if (r != ERROR_SUCCESS)
1365 msiobj_release( &view->hdr );
1366 return r;
1368 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1369 msiobj_release( &view->hdr );
1370 return r;
1373 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1375 msi_set_property( package->db, L"CostingComplete", L"0", -1 );
1376 msi_set_property( package->db, L"ROOTDRIVE", L"C:\\", -1 );
1378 load_all_folders( package );
1379 msi_load_all_components( package );
1380 msi_load_all_features( package );
1381 load_all_files( package );
1382 load_all_patches( package );
1383 mark_patched_components( package );
1384 load_all_media( package );
1386 return ERROR_SUCCESS;
1389 static UINT execute_script( MSIPACKAGE *package, UINT script )
1391 UINT i, rc = ERROR_SUCCESS;
1393 TRACE("executing script %u\n", script);
1395 package->script = script;
1397 if (script == SCRIPT_ROLLBACK)
1399 for (i = package->script_actions_count[script]; i > 0; i--)
1401 rc = ACTION_PerformAction(package, package->script_actions[script][i-1]);
1402 if (rc != ERROR_SUCCESS)
1404 ERR("Execution of script %i halted; action %s returned %u\n",
1405 script, debugstr_w(package->script_actions[script][i-1]), rc);
1406 break;
1410 else
1412 for (i = 0; i < package->script_actions_count[script]; i++)
1414 rc = ACTION_PerformAction(package, package->script_actions[script][i]);
1415 if (rc != ERROR_SUCCESS)
1417 ERR("Execution of script %i halted; action %s returned %u\n",
1418 script, debugstr_w(package->script_actions[script][i]), rc);
1419 break;
1424 package->script = SCRIPT_NONE;
1426 msi_free_action_script(package, script);
1427 return rc;
1430 static UINT ACTION_FileCost(MSIPACKAGE *package)
1432 return ERROR_SUCCESS;
1435 static void get_client_counts( MSIPACKAGE *package )
1437 MSICOMPONENT *comp;
1438 HKEY hkey;
1440 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1442 if (!comp->ComponentId) continue;
1444 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, L"S-1-5-18", &hkey, FALSE ) &&
1445 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1447 comp->num_clients = 0;
1448 continue;
1450 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1451 NULL, NULL, NULL, NULL );
1452 RegCloseKey( hkey );
1456 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1458 MSICOMPONENT *comp;
1459 UINT r;
1461 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1463 if (!comp->ComponentId) continue;
1465 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1466 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1467 &comp->Installed );
1468 if (r == ERROR_SUCCESS) continue;
1470 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1471 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1472 &comp->Installed );
1473 if (r == ERROR_SUCCESS) continue;
1475 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1476 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1477 &comp->Installed );
1478 if (r == ERROR_SUCCESS) continue;
1480 comp->Installed = INSTALLSTATE_ABSENT;
1484 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1486 MSIFEATURE *feature;
1488 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1490 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1492 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1493 feature->Installed = INSTALLSTATE_ABSENT;
1494 else
1495 feature->Installed = state;
1499 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1501 return (feature->Level > 0 && feature->Level <= level);
1504 static BOOL process_state_property(MSIPACKAGE* package, int level,
1505 LPCWSTR property, INSTALLSTATE state)
1507 LPWSTR override;
1508 MSIFEATURE *feature;
1509 BOOL remove = !wcscmp(property, L"REMOVE");
1510 BOOL reinstall = !wcscmp(property, L"REINSTALL");
1512 override = msi_dup_property( package->db, property );
1513 if (!override)
1514 return FALSE;
1516 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1518 if (feature->Level <= 0)
1519 continue;
1521 if (reinstall)
1522 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1523 else if (remove)
1524 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1526 if (!wcsicmp( override, L"ALL" ))
1528 feature->Action = state;
1529 feature->ActionRequest = state;
1531 else
1533 LPWSTR ptr = override;
1534 LPWSTR ptr2 = wcschr(override,',');
1536 while (ptr)
1538 int len = ptr2 - ptr;
1540 if ((ptr2 && lstrlenW(feature->Feature) == len && !wcsncmp(ptr, feature->Feature, len))
1541 || (!ptr2 && !wcscmp(ptr, feature->Feature)))
1543 feature->Action = state;
1544 feature->ActionRequest = state;
1545 break;
1547 if (ptr2)
1549 ptr=ptr2+1;
1550 ptr2 = wcschr(ptr,',');
1552 else
1553 break;
1557 free(override);
1558 return TRUE;
1561 static BOOL process_overrides( MSIPACKAGE *package, int level )
1563 BOOL ret = FALSE;
1565 /* all these activation/deactivation things happen in order and things
1566 * later on the list override things earlier on the list.
1568 * 0 INSTALLLEVEL processing
1569 * 1 ADDLOCAL
1570 * 2 REMOVE
1571 * 3 ADDSOURCE
1572 * 4 ADDDEFAULT
1573 * 5 REINSTALL
1574 * 6 ADVERTISE
1575 * 7 COMPADDLOCAL
1576 * 8 COMPADDSOURCE
1577 * 9 FILEADDLOCAL
1578 * 10 FILEADDSOURCE
1579 * 11 FILEADDDEFAULT
1581 ret |= process_state_property( package, level, L"ADDLOCAL", INSTALLSTATE_LOCAL );
1582 ret |= process_state_property( package, level, L"REMOVE", INSTALLSTATE_ABSENT );
1583 ret |= process_state_property( package, level, L"ADDSOURCE", INSTALLSTATE_SOURCE );
1584 ret |= process_state_property( package, level, L"REINSTALL", INSTALLSTATE_UNKNOWN );
1585 ret |= process_state_property( package, level, L"ADVERTISE", INSTALLSTATE_ADVERTISED );
1587 if (ret)
1588 msi_set_property( package->db, L"Preselected", L"1", -1 );
1590 return ret;
1593 static void disable_children( MSIFEATURE *feature, int level )
1595 FeatureList *fl;
1597 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1599 if (!is_feature_selected( feature, level ))
1601 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1602 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1603 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1605 fl->feature->Level = feature->Level;
1606 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1607 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1609 disable_children( fl->feature, level );
1613 static void follow_parent( MSIFEATURE *feature )
1615 FeatureList *fl;
1617 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1619 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1621 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1622 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1623 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1625 fl->feature->Action = feature->Action;
1626 fl->feature->ActionRequest = feature->ActionRequest;
1628 follow_parent( fl->feature );
1632 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1634 int level;
1635 MSICOMPONENT* component;
1636 MSIFEATURE *feature;
1638 TRACE("Checking Install Level\n");
1640 level = msi_get_property_int(package->db, L"INSTALLLEVEL", 1);
1642 if (msi_get_property_int( package->db, L"Preselected", 0 ))
1644 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1646 if (!is_feature_selected( feature, level )) continue;
1648 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1650 if (feature->Installed == INSTALLSTATE_ABSENT)
1652 feature->Action = INSTALLSTATE_UNKNOWN;
1653 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1655 else
1657 feature->Action = feature->Installed;
1658 feature->ActionRequest = feature->Installed;
1663 else if (!msi_get_property_int( package->db, L"Installed", 0 ))
1665 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1667 if (!is_feature_selected( feature, level )) continue;
1669 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1671 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1673 feature->Action = INSTALLSTATE_SOURCE;
1674 feature->ActionRequest = INSTALLSTATE_SOURCE;
1676 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1678 feature->Action = INSTALLSTATE_ADVERTISED;
1679 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1681 else
1683 feature->Action = INSTALLSTATE_LOCAL;
1684 feature->ActionRequest = INSTALLSTATE_LOCAL;
1689 else
1691 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1693 ComponentList *cl;
1694 MSIFEATURE *cur;
1696 if (!is_feature_selected( feature, level )) continue;
1697 if (feature->ActionRequest != INSTALLSTATE_UNKNOWN) continue;
1699 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1701 if (!cl->component->updated && !cl->component->added)
1702 continue;
1704 cur = feature;
1705 while (cur)
1707 if (cur->ActionRequest != INSTALLSTATE_UNKNOWN)
1708 break;
1710 if (cur->Installed != INSTALLSTATE_ABSENT)
1712 cur->Action = cur->Installed;
1713 cur->ActionRequest = cur->Installed;
1715 else if (!cl->component->added)
1717 break;
1719 else if (cur->Attributes & msidbFeatureAttributesFavorSource)
1721 cur->Action = INSTALLSTATE_SOURCE;
1722 cur->ActionRequest = INSTALLSTATE_SOURCE;
1724 else if (cur->Attributes & msidbFeatureAttributesFavorAdvertise)
1726 cur->Action = INSTALLSTATE_ADVERTISED;
1727 cur->ActionRequest = INSTALLSTATE_ADVERTISED;
1729 else
1731 cur->Action = INSTALLSTATE_LOCAL;
1732 cur->ActionRequest = INSTALLSTATE_LOCAL;
1735 if (!cur->Feature_Parent)
1736 break;
1737 cur = msi_get_loaded_feature(package, cur->Feature_Parent);
1743 /* disable child features of unselected parent or follow parent */
1744 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1746 if (feature->Feature_Parent) continue;
1747 disable_children( feature, level );
1748 follow_parent( feature );
1751 /* now we want to set component state based based on feature state */
1752 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1754 ComponentList *cl;
1756 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1757 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1758 feature->ActionRequest, feature->Action);
1760 /* features with components that have compressed files are made local */
1761 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1763 if (cl->component->ForceLocalState &&
1764 feature->ActionRequest == INSTALLSTATE_SOURCE)
1766 feature->Action = INSTALLSTATE_LOCAL;
1767 feature->ActionRequest = INSTALLSTATE_LOCAL;
1768 break;
1772 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1774 component = cl->component;
1776 switch (feature->ActionRequest)
1778 case INSTALLSTATE_ABSENT:
1779 component->anyAbsent = 1;
1780 break;
1781 case INSTALLSTATE_ADVERTISED:
1782 component->hasAdvertisedFeature = 1;
1783 break;
1784 case INSTALLSTATE_SOURCE:
1785 component->hasSourceFeature = 1;
1786 break;
1787 case INSTALLSTATE_LOCAL:
1788 component->hasLocalFeature = 1;
1789 break;
1790 case INSTALLSTATE_DEFAULT:
1791 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1792 component->hasAdvertisedFeature = 1;
1793 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1794 component->hasSourceFeature = 1;
1795 else
1796 component->hasLocalFeature = 1;
1797 break;
1798 case INSTALLSTATE_UNKNOWN:
1799 if (feature->Installed == INSTALLSTATE_ADVERTISED)
1800 component->hasAdvertisedFeature = 1;
1801 if (feature->Installed == INSTALLSTATE_SOURCE)
1802 component->hasSourceFeature = 1;
1803 if (feature->Installed == INSTALLSTATE_LOCAL)
1804 component->hasLocalFeature = 1;
1805 break;
1806 default:
1807 break;
1812 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1814 /* check if it's local or source */
1815 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1816 (component->hasLocalFeature || component->hasSourceFeature))
1818 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1819 !component->ForceLocalState)
1821 component->Action = INSTALLSTATE_SOURCE;
1822 component->ActionRequest = INSTALLSTATE_SOURCE;
1824 else
1826 component->Action = INSTALLSTATE_LOCAL;
1827 component->ActionRequest = INSTALLSTATE_LOCAL;
1829 continue;
1832 /* if any feature is local, the component must be local too */
1833 if (component->hasLocalFeature)
1835 component->Action = INSTALLSTATE_LOCAL;
1836 component->ActionRequest = INSTALLSTATE_LOCAL;
1837 continue;
1839 if (component->hasSourceFeature)
1841 component->Action = INSTALLSTATE_SOURCE;
1842 component->ActionRequest = INSTALLSTATE_SOURCE;
1843 continue;
1845 if (component->hasAdvertisedFeature)
1847 component->Action = INSTALLSTATE_ADVERTISED;
1848 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1849 continue;
1851 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1852 if (component->anyAbsent && component->ComponentId)
1854 component->Action = INSTALLSTATE_ABSENT;
1855 component->ActionRequest = INSTALLSTATE_ABSENT;
1859 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1861 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1863 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1864 component->Action = INSTALLSTATE_LOCAL;
1865 component->ActionRequest = INSTALLSTATE_LOCAL;
1868 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1869 component->Installed == INSTALLSTATE_SOURCE &&
1870 component->hasSourceFeature)
1872 component->Action = INSTALLSTATE_UNKNOWN;
1873 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1876 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1877 component->num_clients++;
1878 else if (component->Action == INSTALLSTATE_ABSENT)
1880 component->num_clients--;
1882 if (component->num_clients > 0)
1884 TRACE("multiple clients uses %s - disallowing uninstallation\n", debugstr_w(component->Component));
1885 component->Action = INSTALLSTATE_UNKNOWN;
1889 TRACE("component %s (installed %d request %d action %d)\n",
1890 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1893 return ERROR_SUCCESS;
1896 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1898 MSIPACKAGE *package = param;
1899 LPCWSTR name;
1900 MSIFEATURE *feature;
1902 name = MSI_RecordGetString( row, 1 );
1904 feature = msi_get_loaded_feature( package, name );
1905 if (!feature)
1906 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1907 else
1909 LPCWSTR Condition;
1910 Condition = MSI_RecordGetString(row,3);
1912 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1914 int level = MSI_RecordGetInteger(row,2);
1915 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1916 feature->Level = level;
1919 return ERROR_SUCCESS;
1922 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
1924 DWORD ms, ls;
1926 msi_parse_version_string( version, &ms, &ls );
1928 if (fi->dwFileVersionMS > ms) return 1;
1929 else if (fi->dwFileVersionMS < ms) return -1;
1930 else if (fi->dwFileVersionLS > ls) return 1;
1931 else if (fi->dwFileVersionLS < ls) return -1;
1932 return 0;
1935 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
1937 DWORD ms1, ms2;
1939 msi_parse_version_string( ver1, &ms1, NULL );
1940 msi_parse_version_string( ver2, &ms2, NULL );
1942 if (ms1 > ms2) return 1;
1943 else if (ms1 < ms2) return -1;
1944 return 0;
1947 static WCHAR *create_temp_dir( MSIDATABASE *db )
1949 static UINT id;
1950 WCHAR *ret;
1952 if (!db->tempfolder)
1954 WCHAR tmp[MAX_PATH];
1955 DWORD len = ARRAY_SIZE( tmp );
1957 if (msi_get_property( db, L"TempFolder", tmp, &len ) ||
1958 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
1960 GetTempPathW( MAX_PATH, tmp );
1962 if (!(db->tempfolder = wcsdup( tmp ))) return NULL;
1965 if ((ret = malloc( (wcslen( db->tempfolder ) + 20) * sizeof(WCHAR) )))
1967 for (;;)
1969 if (!GetTempFileNameW( db->tempfolder, L"msi", ++id, ret ))
1971 free( ret );
1972 return NULL;
1974 if (CreateDirectoryW( ret, NULL )) break;
1978 return ret;
1982 * msi_build_directory_name()
1984 * This function is to save messing round with directory names
1985 * It handles adding backslashes between path segments,
1986 * and can add \ at the end of the directory name if told to.
1988 * It takes a variable number of arguments.
1989 * It always allocates a new string for the result, so make sure
1990 * to free the return value when finished with it.
1992 * The first arg is the number of path segments that follow.
1993 * The arguments following count are a list of path segments.
1994 * A path segment may be NULL.
1996 * Path segments will be added with a \ separating them.
1997 * A \ will not be added after the last segment, however if the
1998 * last segment is NULL, then the last character will be a \
2000 WCHAR * WINAPIV msi_build_directory_name( DWORD count, ... )
2002 DWORD sz = 1, i;
2003 WCHAR *dir;
2004 va_list va;
2006 va_start( va, count );
2007 for (i = 0; i < count; i++)
2009 const WCHAR *str = va_arg( va, const WCHAR * );
2010 if (str) sz += lstrlenW( str ) + 1;
2012 va_end( va );
2014 dir = malloc( sz * sizeof(WCHAR) );
2015 dir[0] = 0;
2017 va_start( va, count );
2018 for (i = 0; i < count; i++)
2020 const WCHAR *str = va_arg( va, const WCHAR * );
2021 if (!str) continue;
2022 lstrcatW( dir, str );
2023 if ( i + 1 != count && dir[0] && dir[lstrlenW( dir ) - 1] != '\\') lstrcatW( dir, L"\\" );
2025 va_end( va );
2026 return dir;
2029 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2031 return comp->assembly && !comp->assembly->application;
2034 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2036 free( file->TargetPath );
2037 if (msi_is_global_assembly( file->Component ))
2039 MSIASSEMBLY *assembly = file->Component->assembly;
2041 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2042 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2044 else
2046 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2047 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2050 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2053 static UINT calculate_file_cost( MSIPACKAGE *package )
2055 VS_FIXEDFILEINFO *file_version;
2056 WCHAR *font_version;
2057 MSIFILE *file;
2059 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2061 MSICOMPONENT *comp = file->Component;
2062 DWORD file_size;
2064 if (!comp->Enabled) continue;
2066 if (file->IsCompressed)
2067 comp->ForceLocalState = TRUE;
2069 set_target_path( package, file );
2071 if (msi_get_file_attributes( package, file->TargetPath ) == INVALID_FILE_ATTRIBUTES)
2073 comp->Cost += file->FileSize;
2074 continue;
2076 file_size = msi_get_disk_file_size( package, file->TargetPath );
2077 TRACE("%s (size %lu)\n", debugstr_w(file->TargetPath), file_size);
2079 if (file->Version)
2081 if ((file_version = msi_get_disk_file_version( package, file->TargetPath )))
2083 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2085 comp->Cost += file->FileSize - file_size;
2087 free( file_version );
2088 continue;
2090 else if ((font_version = msi_get_font_file_version( package, file->TargetPath )))
2092 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2094 comp->Cost += file->FileSize - file_size;
2096 free( font_version );
2097 continue;
2100 if (file_size != file->FileSize)
2102 comp->Cost += file->FileSize - file_size;
2106 return ERROR_SUCCESS;
2109 WCHAR *msi_normalize_path( const WCHAR *in )
2111 const WCHAR *p = in;
2112 WCHAR *q, *ret;
2113 int n, len = lstrlenW( in ) + 2;
2115 if (!(q = ret = malloc( len * sizeof(WCHAR) ))) return NULL;
2117 len = 0;
2118 while (1)
2120 /* copy until the end of the string or a space */
2121 while (*p != ' ' && (*q = *p))
2123 p++, len++;
2124 /* reduce many backslashes to one */
2125 if (*p != '\\' || *q != '\\')
2126 q++;
2129 /* quit at the end of the string */
2130 if (!*p)
2131 break;
2133 /* count the number of spaces */
2134 n = 0;
2135 while (p[n] == ' ')
2136 n++;
2138 /* if it's leading or trailing space, skip it */
2139 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2140 p += n;
2141 else /* copy n spaces */
2142 while (n && (*q++ = *p++)) n--;
2144 while (q - ret > 0 && q[-1] == ' ') q--;
2145 if (q - ret > 0 && q[-1] != '\\')
2147 q[0] = '\\';
2148 q[1] = 0;
2150 return ret;
2153 static WCHAR *get_install_location( MSIPACKAGE *package )
2155 HKEY hkey;
2156 WCHAR *path;
2158 if (!package->ProductCode) return NULL;
2159 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2160 if ((path = msi_reg_get_val_str( hkey, L"InstallLocation" )) && !path[0])
2162 free( path );
2163 path = NULL;
2165 RegCloseKey( hkey );
2166 return path;
2169 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2171 FolderList *fl;
2172 MSIFOLDER *folder, *parent, *child;
2173 WCHAR *path, *normalized_path;
2175 TRACE("resolving %s\n", debugstr_w(name));
2177 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2179 if (!wcscmp( folder->Directory, L"TARGETDIR" )) /* special resolving for target root dir */
2181 if (!(path = get_install_location( package )) &&
2182 (!load_prop || !(path = msi_dup_property( package->db, L"TARGETDIR" ))))
2184 path = msi_dup_property( package->db, L"ROOTDRIVE" );
2187 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2189 if (folder->Parent && wcscmp( folder->Directory, folder->Parent ))
2191 parent = msi_get_loaded_folder( package, folder->Parent );
2192 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2194 else
2195 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2198 normalized_path = msi_normalize_path( path );
2199 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2200 free( path );
2202 free( folder->ResolvedTarget );
2203 folder->ResolvedTarget = normalized_path;
2205 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2207 child = fl->folder;
2208 msi_resolve_target_folder( package, child->Directory, load_prop );
2210 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2213 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2215 MSICOMPONENT *comp;
2216 ULONGLONG ret = 0;
2218 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2220 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2222 return ret;
2225 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2227 MSICOMPONENT *comp;
2228 MSIQUERY *view;
2229 WCHAR *level, *primary_key, *primary_folder;
2230 UINT rc;
2232 TRACE("Building directory properties\n");
2233 msi_resolve_target_folder( package, L"TARGETDIR", TRUE );
2235 TRACE("Evaluating component conditions\n");
2236 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2238 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2240 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2241 comp->Enabled = FALSE;
2243 else
2244 comp->Enabled = TRUE;
2246 get_client_counts( package );
2248 /* read components states from the registry */
2249 ACTION_GetComponentInstallStates(package);
2250 ACTION_GetFeatureInstallStates(package);
2252 if (!process_overrides( package, msi_get_property_int( package->db, L"INSTALLLEVEL", 1 ) ))
2254 TRACE("Evaluating feature conditions\n");
2256 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Condition`", &view );
2257 if (rc == ERROR_SUCCESS)
2259 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2260 msiobj_release( &view->hdr );
2261 if (rc != ERROR_SUCCESS)
2262 return rc;
2266 TRACE("Calculating file cost\n");
2267 calculate_file_cost( package );
2269 msi_set_property( package->db, L"CostingComplete", L"1", -1 );
2270 /* set default run level if not set */
2271 level = msi_dup_property( package->db, L"INSTALLLEVEL" );
2272 if (!level) msi_set_property( package->db, L"INSTALLLEVEL", L"1", -1 );
2273 free(level);
2275 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2277 if ((primary_key = msi_dup_property( package->db, L"PRIMARYFOLDER" )))
2279 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2281 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2282 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2284 ULARGE_INTEGER free;
2285 ULONGLONG required;
2286 WCHAR buf[21];
2288 primary_folder[2] = 0;
2289 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2291 swprintf( buf, ARRAY_SIZE(buf), L"%lu", free.QuadPart / 512 );
2292 msi_set_property( package->db, L"PrimaryVolumeSpaceAvailable", buf, -1 );
2294 required = get_volume_space_required( package );
2295 swprintf( buf, ARRAY_SIZE(buf), L"%lu", required / 512 );
2296 msi_set_property( package->db, L"PrimaryVolumeSpaceRequired", buf, -1 );
2298 swprintf( buf, ARRAY_SIZE(buf), L"%lu", (free.QuadPart - required) / 512 );
2299 msi_set_property( package->db, L"PrimaryVolumeSpaceRemaining", buf, -1 );
2300 msi_set_property( package->db, L"PrimaryVolumePath", primary_folder, 2 );
2302 free( primary_folder );
2304 free( primary_key );
2307 /* FIXME: check volume disk space */
2308 msi_set_property( package->db, L"OutOfDiskSpace", L"0", -1 );
2309 msi_set_property( package->db, L"OutOfNoRbDiskSpace", L"0", -1 );
2311 return ERROR_SUCCESS;
2314 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2316 BYTE *data;
2318 if (!value)
2320 *size = sizeof(WCHAR);
2321 *type = REG_SZ;
2322 if ((data = malloc( *size ))) *(WCHAR *)data = 0;
2323 return data;
2325 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2327 if (value[1]=='x')
2329 LPWSTR ptr;
2330 CHAR byte[5];
2331 LPWSTR deformated = NULL;
2332 int count;
2334 deformat_string(package, &value[2], &deformated);
2336 /* binary value type */
2337 ptr = deformated;
2338 *type = REG_BINARY;
2339 if (lstrlenW(ptr)%2)
2340 *size = (lstrlenW(ptr)/2)+1;
2341 else
2342 *size = lstrlenW(ptr)/2;
2344 data = malloc(*size);
2346 byte[0] = '0';
2347 byte[1] = 'x';
2348 byte[4] = 0;
2349 count = 0;
2350 /* if uneven pad with a zero in front */
2351 if (lstrlenW(ptr)%2)
2353 byte[2]= '0';
2354 byte[3]= *ptr;
2355 ptr++;
2356 data[count] = (BYTE)strtol(byte,NULL,0);
2357 count ++;
2358 TRACE("Uneven byte count\n");
2360 while (*ptr)
2362 byte[2]= *ptr;
2363 ptr++;
2364 byte[3]= *ptr;
2365 ptr++;
2366 data[count] = (BYTE)strtol(byte,NULL,0);
2367 count ++;
2369 free(deformated);
2371 TRACE( "data %lu bytes(%u)\n", *size, count );
2373 else
2375 LPWSTR deformated;
2376 LPWSTR p;
2377 DWORD d = 0;
2378 deformat_string(package, &value[1], &deformated);
2380 *type=REG_DWORD;
2381 *size = sizeof(DWORD);
2382 data = malloc(*size);
2383 p = deformated;
2384 if (*p == '-')
2385 p++;
2386 while (*p)
2388 if ( (*p < '0') || (*p > '9') )
2389 break;
2390 d *= 10;
2391 d += (*p - '0');
2392 p++;
2394 if (deformated[0] == '-')
2395 d = -d;
2396 *(DWORD *)data = d;
2397 TRACE( "DWORD %lu\n", *(DWORD *)data);
2399 free(deformated);
2402 else
2404 const WCHAR *ptr = value;
2406 *type = REG_SZ;
2407 if (value[0] == '#')
2409 ptr++; len--;
2410 if (value[1] == '%')
2412 ptr++; len--;
2413 *type = REG_EXPAND_SZ;
2416 data = (BYTE *)msi_strdupW( ptr, len );
2417 if (len > lstrlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2418 *size = (len + 1) * sizeof(WCHAR);
2420 return data;
2423 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2425 const WCHAR *ret;
2427 switch (root)
2429 case -1:
2430 if (msi_get_property_int( package->db, L"ALLUSERS", 0 ))
2432 *root_key = HKEY_LOCAL_MACHINE;
2433 ret = L"HKEY_LOCAL_MACHINE\\";
2435 else
2437 *root_key = HKEY_CURRENT_USER;
2438 ret = L"HKEY_CURRENT_USER\\";
2440 break;
2441 case 0:
2442 *root_key = HKEY_CLASSES_ROOT;
2443 ret = L"HKEY_CLASSES_ROOT\\";
2444 break;
2445 case 1:
2446 *root_key = HKEY_CURRENT_USER;
2447 ret = L"HKEY_CURRENT_USER\\";
2448 break;
2449 case 2:
2450 *root_key = HKEY_LOCAL_MACHINE;
2451 ret = L"HKEY_LOCAL_MACHINE\\";
2452 break;
2453 case 3:
2454 *root_key = HKEY_USERS;
2455 ret = L"HKEY_USERS\\";
2456 break;
2457 default:
2458 ERR("Unknown root %i\n", root);
2459 return NULL;
2462 return ret;
2465 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2467 REGSAM view = 0;
2468 if (is_wow64 || is_64bit)
2469 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2470 return view;
2473 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2475 WCHAR *subkey, *p, *q;
2476 HKEY hkey, ret = NULL;
2477 LONG res;
2479 access |= get_registry_view( comp );
2481 if (!(subkey = wcsdup( path ))) return NULL;
2482 p = subkey;
2483 if ((q = wcschr( p, '\\' ))) *q = 0;
2484 if (create)
2485 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2486 else
2487 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2488 if (res)
2490 TRACE( "failed to open key %s (%ld)\n", debugstr_w(subkey), res );
2491 free( subkey );
2492 return NULL;
2494 if (q && q[1])
2496 ret = open_key( comp, hkey, q + 1, create, access );
2497 RegCloseKey( hkey );
2499 else ret = hkey;
2500 free( subkey );
2501 return ret;
2504 static BOOL is_special_entry( const WCHAR *name )
2506 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2509 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2511 const WCHAR *p = str;
2512 WCHAR **ret;
2513 int i = 0;
2515 *count = 0;
2516 if (!str) return NULL;
2517 while ((p - str) < len)
2519 p += lstrlenW( p ) + 1;
2520 (*count)++;
2522 if (!(ret = malloc( *count * sizeof(WCHAR *) ))) return NULL;
2523 p = str;
2524 while ((p - str) < len)
2526 if (!(ret[i] = wcsdup( p )))
2528 for (; i >= 0; i--) free( ret[i] );
2529 free( ret );
2530 return NULL;
2532 p += lstrlenW( p ) + 1;
2533 i++;
2535 return ret;
2538 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2539 WCHAR **right, DWORD right_count, DWORD *size )
2541 WCHAR *ret, *p;
2542 unsigned int i;
2544 *size = sizeof(WCHAR);
2545 for (i = 0; i < left_count; i++) *size += (lstrlenW( left[i] ) + 1) * sizeof(WCHAR);
2546 for (i = 0; i < right_count; i++) *size += (lstrlenW( right[i] ) + 1) * sizeof(WCHAR);
2548 if (!(ret = p = malloc( *size ))) return NULL;
2550 for (i = 0; i < left_count; i++)
2552 lstrcpyW( p, left[i] );
2553 p += lstrlenW( p ) + 1;
2555 for (i = 0; i < right_count; i++)
2557 lstrcpyW( p, right[i] );
2558 p += lstrlenW( p ) + 1;
2560 *p = 0;
2561 return ret;
2564 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2565 WCHAR **new, DWORD new_count )
2567 DWORD ret = old_count;
2568 unsigned int i, j, k;
2570 for (i = 0; i < new_count; i++)
2572 for (j = 0; j < old_count; j++)
2574 if (old[j] && !wcscmp( new[i], old[j] ))
2576 free( old[j] );
2577 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2578 old[k] = NULL;
2579 ret--;
2583 return ret;
2586 enum join_op
2588 JOIN_OP_APPEND,
2589 JOIN_OP_PREPEND,
2590 JOIN_OP_REPLACE
2593 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2594 WCHAR **new, DWORD new_count, DWORD *size )
2596 switch (op)
2598 case JOIN_OP_APPEND:
2599 old_count = remove_duplicate_values( old, old_count, new, new_count );
2600 return flatten_multi_string_values( old, old_count, new, new_count, size );
2602 case JOIN_OP_PREPEND:
2603 old_count = remove_duplicate_values( old, old_count, new, new_count );
2604 return flatten_multi_string_values( new, new_count, old, old_count, size );
2606 case JOIN_OP_REPLACE:
2607 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2609 default:
2610 ERR("unhandled join op %u\n", op);
2611 return NULL;
2615 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2616 BYTE *new_value, DWORD new_size, DWORD *size )
2618 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2619 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2620 enum join_op op = JOIN_OP_REPLACE;
2621 WCHAR **old = NULL, **new = NULL;
2622 BYTE *ret;
2624 if (new_size / sizeof(WCHAR) - 1 > 1)
2626 new_ptr = (const WCHAR *)new_value;
2627 new_len = new_size / sizeof(WCHAR) - 1;
2629 if (!new_ptr[0] && new_ptr[new_len - 1])
2631 op = JOIN_OP_APPEND;
2632 new_len--;
2633 new_ptr++;
2635 else if (new_ptr[0] && !new_ptr[new_len - 1])
2637 op = JOIN_OP_PREPEND;
2638 new_len--;
2640 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2642 op = JOIN_OP_REPLACE;
2643 new_len -= 2;
2644 new_ptr++;
2646 new = split_multi_string_values( new_ptr, new_len, &new_count );
2648 if (old_size / sizeof(WCHAR) - 1 > 1)
2650 old_ptr = (const WCHAR *)old_value;
2651 old_len = old_size / sizeof(WCHAR) - 1;
2652 old = split_multi_string_values( old_ptr, old_len, &old_count );
2654 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2655 for (i = 0; i < old_count; i++) free( old[i] );
2656 for (i = 0; i < new_count; i++) free( new[i] );
2657 free( old );
2658 free( new );
2659 return ret;
2662 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2664 BYTE *ret;
2665 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2666 if (!(ret = malloc( *size ))) return NULL;
2667 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2668 return ret;
2671 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2673 MSIPACKAGE *package = param;
2674 BYTE *new_value, *old_value = NULL;
2675 HKEY root_key, hkey;
2676 DWORD type, old_type, new_size, old_size = 0;
2677 LPWSTR deformated, uikey;
2678 const WCHAR *szRoot, *component, *name, *key, *str;
2679 MSICOMPONENT *comp;
2680 MSIRECORD * uirow;
2681 INT root;
2682 BOOL check_first = FALSE;
2683 int len;
2685 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2687 component = MSI_RecordGetString(row, 6);
2688 comp = msi_get_loaded_component(package,component);
2689 if (!comp)
2690 return ERROR_SUCCESS;
2692 comp->Action = msi_get_component_action( package, comp );
2693 if (comp->Action != INSTALLSTATE_LOCAL && comp->Action != INSTALLSTATE_SOURCE)
2695 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2696 return ERROR_SUCCESS;
2699 name = MSI_RecordGetString(row, 4);
2700 if( MSI_RecordIsNull(row,5) && name )
2702 /* null values can have special meanings */
2703 if (name[0]=='-' && name[1] == 0)
2704 return ERROR_SUCCESS;
2705 if ((name[0] == '+' || name[0] == '*') && !name[1])
2706 check_first = TRUE;
2709 root = MSI_RecordGetInteger(row,2);
2710 key = MSI_RecordGetString(row, 3);
2712 szRoot = get_root_key( package, root, &root_key );
2713 if (!szRoot)
2714 return ERROR_SUCCESS;
2716 deformat_string(package, key , &deformated);
2717 uikey = malloc( (wcslen(deformated) + wcslen(szRoot) + 1) * sizeof(WCHAR) );
2718 lstrcpyW(uikey,szRoot);
2719 lstrcatW(uikey,deformated);
2721 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2723 ERR("Could not create key %s\n", debugstr_w(deformated));
2724 free(uikey);
2725 free(deformated);
2726 return ERROR_FUNCTION_FAILED;
2728 free( deformated );
2729 str = msi_record_get_string( row, 5, NULL );
2730 len = deformat_string( package, str, &deformated );
2731 new_value = parse_value( package, deformated, len, &type, &new_size );
2733 free( deformated );
2734 deformat_string(package, name, &deformated);
2736 if (!is_special_entry( name ))
2738 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2739 if (type == REG_MULTI_SZ)
2741 BYTE *new;
2742 if (old_value && old_type != REG_MULTI_SZ)
2744 free( old_value );
2745 old_value = NULL;
2746 old_size = 0;
2748 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2749 free( new_value );
2750 new_value = new;
2752 if (!check_first)
2754 TRACE( "setting value %s of %s type %lu\n", debugstr_w(deformated), debugstr_w(uikey), type );
2755 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2757 else if (!old_value)
2759 if (deformated || new_size)
2761 TRACE( "setting value %s of %s type %lu\n", debugstr_w(deformated), debugstr_w(uikey), type );
2762 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2765 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2767 RegCloseKey(hkey);
2769 uirow = MSI_CreateRecord(3);
2770 MSI_RecordSetStringW(uirow,2,deformated);
2771 MSI_RecordSetStringW(uirow,1,uikey);
2772 if (type == REG_SZ || type == REG_EXPAND_SZ)
2773 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2774 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2775 msiobj_release( &uirow->hdr );
2777 free(new_value);
2778 free(old_value);
2779 free(deformated);
2780 free(uikey);
2782 return ERROR_SUCCESS;
2785 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2787 MSIQUERY *view;
2788 UINT rc;
2790 if (package->script == SCRIPT_NONE)
2791 return msi_schedule_action(package, SCRIPT_INSTALL, L"WriteRegistryValues");
2793 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Registry`", &view);
2794 if (rc != ERROR_SUCCESS)
2795 return ERROR_SUCCESS;
2797 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2798 msiobj_release(&view->hdr);
2799 return rc;
2802 static int is_key_empty(const MSICOMPONENT *comp, HKEY root, const WCHAR *path)
2804 DWORD subkeys, values;
2805 HKEY key;
2806 LONG res;
2808 key = open_key(comp, root, path, FALSE, KEY_READ);
2809 if (!key) return 0;
2811 res = RegQueryInfoKeyW(key, 0, 0, 0, &subkeys, 0, 0, &values, 0, 0, 0, 0);
2812 RegCloseKey(key);
2814 return !res && !subkeys && !values;
2817 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2819 LONG res = ERROR_SUCCESS;
2820 REGSAM access = get_registry_view( comp );
2821 WCHAR *subkey, *p;
2822 HKEY hkey;
2824 if (!(subkey = wcsdup( path ))) return;
2827 if ((p = wcsrchr( subkey, '\\' )))
2829 *p = 0;
2830 if (!p[1]) continue; /* trailing backslash */
2831 hkey = open_key( comp, root, subkey, FALSE, READ_CONTROL );
2832 if (!hkey) break;
2833 if (!is_key_empty(comp, hkey, p + 1))
2835 RegCloseKey(hkey);
2836 break;
2838 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2839 RegCloseKey( hkey );
2841 else if (is_key_empty(comp, root, subkey))
2842 res = RegDeleteKeyExW( root, subkey, access, 0 );
2843 if (res)
2845 TRACE( "failed to delete key %s (%ld)\n", debugstr_w(subkey), res );
2846 break;
2848 } while (p);
2849 free( subkey );
2852 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
2854 LONG res;
2855 HKEY hkey;
2857 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
2859 if ((res = RegDeleteValueW( hkey, value )))
2860 TRACE( "failed to delete value %s (%ld)\n", debugstr_w(value), res );
2862 RegCloseKey( hkey );
2863 if (is_key_empty(comp, root, path))
2865 TRACE("removing empty key %s\n", debugstr_w(path));
2866 delete_key( comp, root, path );
2871 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2873 LONG res;
2874 HKEY hkey;
2876 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
2877 res = RegDeleteTreeW( hkey, NULL );
2878 if (res) TRACE( "failed to delete subtree of %s (%ld)\n", debugstr_w(path), res );
2879 delete_key( comp, root, path );
2880 RegCloseKey( hkey );
2883 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2885 MSIPACKAGE *package = param;
2886 LPCWSTR component, name, key_str, root_key_str;
2887 LPWSTR deformated_key, deformated_name, ui_key_str;
2888 MSICOMPONENT *comp;
2889 MSIRECORD *uirow;
2890 BOOL delete_key = FALSE;
2891 HKEY hkey_root;
2892 UINT size;
2893 INT root;
2895 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2897 component = MSI_RecordGetString( row, 6 );
2898 comp = msi_get_loaded_component( package, component );
2899 if (!comp)
2900 return ERROR_SUCCESS;
2902 comp->Action = msi_get_component_action( package, comp );
2903 if (comp->Action != INSTALLSTATE_ABSENT)
2905 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2906 return ERROR_SUCCESS;
2909 name = MSI_RecordGetString( row, 4 );
2910 if (MSI_RecordIsNull( row, 5 ) && name )
2912 if (name[0] == '+' && !name[1])
2913 return ERROR_SUCCESS;
2914 if ((name[0] == '-' || name[0] == '*') && !name[1])
2916 delete_key = TRUE;
2917 name = NULL;
2921 root = MSI_RecordGetInteger( row, 2 );
2922 key_str = MSI_RecordGetString( row, 3 );
2924 root_key_str = get_root_key( package, root, &hkey_root );
2925 if (!root_key_str)
2926 return ERROR_SUCCESS;
2928 deformat_string( package, key_str, &deformated_key );
2929 size = lstrlenW( deformated_key ) + lstrlenW( root_key_str ) + 1;
2930 ui_key_str = malloc( size * sizeof(WCHAR) );
2931 lstrcpyW( ui_key_str, root_key_str );
2932 lstrcatW( ui_key_str, deformated_key );
2934 deformat_string( package, name, &deformated_name );
2936 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
2937 else delete_value( comp, hkey_root, deformated_key, deformated_name );
2938 free( deformated_key );
2940 uirow = MSI_CreateRecord( 2 );
2941 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2942 MSI_RecordSetStringW( uirow, 2, deformated_name );
2943 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2944 msiobj_release( &uirow->hdr );
2946 free( ui_key_str );
2947 free( deformated_name );
2948 return ERROR_SUCCESS;
2951 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2953 MSIPACKAGE *package = param;
2954 LPCWSTR component, name, key_str, root_key_str;
2955 LPWSTR deformated_key, deformated_name, ui_key_str;
2956 MSICOMPONENT *comp;
2957 MSIRECORD *uirow;
2958 BOOL delete_key = FALSE;
2959 HKEY hkey_root;
2960 UINT size;
2961 INT root;
2963 component = MSI_RecordGetString( row, 5 );
2964 comp = msi_get_loaded_component( package, component );
2965 if (!comp)
2966 return ERROR_SUCCESS;
2968 comp->Action = msi_get_component_action( package, comp );
2969 if (comp->Action != INSTALLSTATE_LOCAL)
2971 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2972 return ERROR_SUCCESS;
2975 if ((name = MSI_RecordGetString( row, 4 )))
2977 if (name[0] == '-' && !name[1])
2979 delete_key = TRUE;
2980 name = NULL;
2984 root = MSI_RecordGetInteger( row, 2 );
2985 key_str = MSI_RecordGetString( row, 3 );
2987 root_key_str = get_root_key( package, root, &hkey_root );
2988 if (!root_key_str)
2989 return ERROR_SUCCESS;
2991 deformat_string( package, key_str, &deformated_key );
2992 size = lstrlenW( deformated_key ) + lstrlenW( root_key_str ) + 1;
2993 ui_key_str = malloc( size * sizeof(WCHAR) );
2994 lstrcpyW( ui_key_str, root_key_str );
2995 lstrcatW( ui_key_str, deformated_key );
2997 deformat_string( package, name, &deformated_name );
2999 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3000 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3001 free( deformated_key );
3003 uirow = MSI_CreateRecord( 2 );
3004 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3005 MSI_RecordSetStringW( uirow, 2, deformated_name );
3006 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3007 msiobj_release( &uirow->hdr );
3009 free( ui_key_str );
3010 free( deformated_name );
3011 return ERROR_SUCCESS;
3014 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3016 MSIQUERY *view;
3017 UINT rc;
3019 if (package->script == SCRIPT_NONE)
3020 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveRegistryValues");
3022 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Registry`", &view );
3023 if (rc == ERROR_SUCCESS)
3025 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3026 msiobj_release( &view->hdr );
3027 if (rc != ERROR_SUCCESS)
3028 return rc;
3030 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `RemoveRegistry`", &view );
3031 if (rc == ERROR_SUCCESS)
3033 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3034 msiobj_release( &view->hdr );
3035 if (rc != ERROR_SUCCESS)
3036 return rc;
3038 return ERROR_SUCCESS;
3041 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3043 return ERROR_SUCCESS;
3047 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3049 MSICOMPONENT *comp;
3050 DWORD total = 0, count = 0;
3051 MSIQUERY *view;
3052 MSIFEATURE *feature;
3053 MSIFILE *file;
3054 UINT rc;
3056 TRACE("InstallValidate\n");
3058 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Registry`", &view );
3059 if (rc == ERROR_SUCCESS)
3061 rc = MSI_IterateRecords( view, &count, NULL, package );
3062 msiobj_release( &view->hdr );
3063 if (rc != ERROR_SUCCESS)
3064 return rc;
3065 total += count * REG_PROGRESS_VALUE;
3067 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3068 total += COMPONENT_PROGRESS_VALUE;
3070 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3071 total += file->FileSize;
3073 msi_ui_progress( package, 0, total, 0, 0 );
3075 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3077 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3078 debugstr_w(feature->Feature), feature->Installed,
3079 feature->ActionRequest, feature->Action);
3081 return ERROR_SUCCESS;
3084 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3086 MSIPACKAGE* package = param;
3087 const WCHAR *cond, *message;
3088 UINT r;
3090 cond = MSI_RecordGetString(row, 1);
3091 r = MSI_EvaluateConditionW(package, cond);
3092 if (r == MSICONDITION_FALSE)
3094 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3096 WCHAR *deformated;
3097 message = MSI_RecordGetString(row, 2);
3098 deformat_string(package, message, &deformated);
3099 MessageBoxW(NULL, deformated, L"Install Failed", MB_OK);
3100 free(deformated);
3103 return ERROR_INSTALL_FAILURE;
3106 return ERROR_SUCCESS;
3109 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3111 MSIQUERY *view;
3112 UINT rc;
3114 TRACE("Checking launch conditions\n");
3116 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `LaunchCondition`", &view);
3117 if (rc != ERROR_SUCCESS)
3118 return ERROR_SUCCESS;
3120 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3121 msiobj_release(&view->hdr);
3122 return rc;
3125 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3128 if (!cmp->KeyPath)
3129 return wcsdup( msi_get_target_folder( package, cmp->Directory ) );
3131 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3133 MSIRECORD *row;
3134 UINT root, len;
3135 LPWSTR deformated, buffer, deformated_name;
3136 LPCWSTR key, name;
3138 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `Registry` WHERE `Registry` = '%s'", cmp->KeyPath);
3139 if (!row)
3140 return NULL;
3142 root = MSI_RecordGetInteger(row,2);
3143 key = MSI_RecordGetString(row, 3);
3144 name = MSI_RecordGetString(row, 4);
3145 deformat_string(package, key , &deformated);
3146 deformat_string(package, name, &deformated_name);
3148 len = lstrlenW(deformated) + 6;
3149 if (deformated_name)
3150 len+=lstrlenW(deformated_name);
3152 buffer = malloc(len * sizeof(WCHAR));
3154 if (deformated_name)
3155 swprintf(buffer, len, L"%02d:\\%s\\%s", root, deformated, deformated_name);
3156 else
3157 swprintf(buffer, len, L"%02d:\\%s\\", root, deformated);
3159 free(deformated);
3160 free(deformated_name);
3161 msiobj_release(&row->hdr);
3163 return buffer;
3165 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3167 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3168 return NULL;
3170 else
3172 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3174 if (file)
3175 return wcsdup( file->TargetPath );
3177 return NULL;
3180 static HKEY open_shared_dlls_key( MSICOMPONENT *comp, BOOL create, REGSAM access )
3182 return open_key( comp, HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs",
3183 create, access );
3186 static UINT get_shared_dlls_count( MSICOMPONENT *comp )
3188 DWORD count, type, sz = sizeof(count);
3189 HKEY hkey = open_shared_dlls_key( comp, FALSE, KEY_READ );
3190 if (RegQueryValueExW( hkey, comp->FullKeypath, NULL, &type, (BYTE *)&count, &sz )) count = 0;
3191 RegCloseKey( hkey );
3192 return count;
3195 static void write_shared_dlls_count( MSICOMPONENT *comp, const WCHAR *path, INT count )
3197 HKEY hkey = open_shared_dlls_key( comp, TRUE, KEY_SET_VALUE );
3198 if (count > 0)
3199 msi_reg_set_val_dword( hkey, path, count );
3200 else
3201 RegDeleteValueW( hkey, path );
3202 RegCloseKey(hkey);
3205 static void refcount_component( MSIPACKAGE *package, MSICOMPONENT *comp )
3207 MSIFEATURE *feature;
3208 INT count = 0;
3209 BOOL write = FALSE;
3211 /* only refcount DLLs */
3212 if (!comp->KeyPath || comp->assembly || comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3213 comp->Attributes & msidbComponentAttributesODBCDataSource)
3214 write = FALSE;
3215 else
3217 count = get_shared_dlls_count( comp );
3218 write = (count > 0);
3219 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3220 write = TRUE;
3223 /* increment counts */
3224 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3226 ComponentList *cl;
3228 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3229 continue;
3231 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3233 if ( cl->component == comp )
3234 count++;
3238 /* decrement counts */
3239 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3241 ComponentList *cl;
3243 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3244 continue;
3246 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3248 if ( cl->component == comp )
3249 count--;
3253 /* ref count all the files in the component */
3254 if (write)
3256 MSIFILE *file;
3258 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3260 if (file->Component == comp)
3261 write_shared_dlls_count( comp, file->TargetPath, count );
3265 /* add a count for permanent */
3266 if (comp->Attributes & msidbComponentAttributesPermanent)
3267 count ++;
3269 comp->RefCount = count;
3271 if (write)
3272 write_shared_dlls_count( comp, comp->FullKeypath, comp->RefCount );
3275 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3277 if (comp->assembly)
3279 DWORD len = lstrlenW( L"<\\" ) + lstrlenW( comp->assembly->display_name );
3280 WCHAR *keypath = malloc( (len + 1) * sizeof(WCHAR) );
3282 if (keypath)
3284 lstrcpyW( keypath, L"<\\" );
3285 lstrcatW( keypath, comp->assembly->display_name );
3287 return keypath;
3289 return resolve_keypath( package, comp );
3292 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3294 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3295 UINT rc;
3296 MSICOMPONENT *comp;
3297 HKEY hkey;
3299 TRACE("\n");
3301 msi_set_sourcedir_props(package, FALSE);
3303 if (package->script == SCRIPT_NONE)
3304 return msi_schedule_action(package, SCRIPT_INSTALL, L"ProcessComponents");
3306 squash_guid( package->ProductCode, squashed_pc );
3308 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3310 MSIRECORD *uirow;
3311 INSTALLSTATE action;
3313 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3314 if (!comp->ComponentId)
3315 continue;
3317 squash_guid( comp->ComponentId, squashed_cc );
3318 free( comp->FullKeypath );
3319 comp->FullKeypath = build_full_keypath( package, comp );
3321 refcount_component( package, comp );
3323 if (package->need_rollback) action = comp->Installed;
3324 else action = comp->ActionRequest;
3326 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3327 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3328 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3330 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3332 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3333 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, L"S-1-5-18", &hkey, TRUE);
3334 else
3335 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3337 if (rc != ERROR_SUCCESS)
3338 continue;
3340 if (comp->Attributes & msidbComponentAttributesPermanent)
3342 msi_reg_set_val_str(hkey, L"00000000000000000000000000000000", comp->FullKeypath);
3344 if (action == INSTALLSTATE_LOCAL)
3345 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3346 else
3348 MSIFILE *file;
3349 MSIRECORD *row;
3350 LPWSTR ptr, ptr2;
3351 WCHAR source[MAX_PATH];
3352 WCHAR base[MAX_PATH];
3353 LPWSTR sourcepath;
3355 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3356 continue;
3358 if (!(row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `Media` WHERE `LastSequence` >= %d "
3359 "ORDER BY `DiskId`", file->Sequence)))
3360 return ERROR_FUNCTION_FAILED;
3362 swprintf(source, ARRAY_SIZE(source), L"%02d\\", MSI_RecordGetInteger(row, 1));
3363 ptr2 = wcsrchr(source, '\\') + 1;
3364 msiobj_release(&row->hdr);
3366 lstrcpyW(base, package->PackagePath);
3367 ptr = wcsrchr(base, '\\');
3368 *(ptr + 1) = '\0';
3370 sourcepath = msi_resolve_file_source(package, file);
3371 ptr = sourcepath + lstrlenW(base);
3372 lstrcpyW(ptr2, ptr);
3373 free(sourcepath);
3375 msi_reg_set_val_str( hkey, squashed_pc, source );
3377 RegCloseKey(hkey);
3379 else if (action == INSTALLSTATE_ABSENT)
3381 if (comp->num_clients <= 0)
3383 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3384 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, L"S-1-5-18" );
3385 else
3386 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3388 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3390 else
3392 LONG res;
3394 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3395 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, L"S-1-5-18", &hkey, FALSE );
3396 else
3397 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3399 if (rc != ERROR_SUCCESS)
3401 WARN( "failed to open component key %u\n", rc );
3402 continue;
3404 res = RegDeleteValueW( hkey, squashed_pc );
3405 RegCloseKey(hkey);
3406 if (res) WARN( "failed to delete component value %ld\n", res );
3410 /* UI stuff */
3411 uirow = MSI_CreateRecord(3);
3412 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3413 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3414 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3415 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3416 msiobj_release( &uirow->hdr );
3418 return ERROR_SUCCESS;
3421 struct typelib
3423 CLSID clsid;
3424 LPWSTR source;
3425 LPWSTR path;
3426 ITypeLib *ptLib;
3429 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3430 LPWSTR lpszName, LONG_PTR lParam)
3432 TLIBATTR *attr;
3433 struct typelib *tl_struct = (struct typelib *)lParam;
3434 int sz;
3435 HRESULT res;
3437 if (!IS_INTRESOURCE(lpszName))
3439 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3440 return TRUE;
3443 sz = lstrlenW(tl_struct->source)+4;
3445 if ((INT_PTR)lpszName == 1)
3446 tl_struct->path = wcsdup(tl_struct->source);
3447 else
3449 tl_struct->path = malloc(sz * sizeof(WCHAR));
3450 swprintf(tl_struct->path, sz, L"%s\\%d", tl_struct->source, lpszName);
3453 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3454 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3455 if (FAILED(res))
3457 free(tl_struct->path);
3458 tl_struct->path = NULL;
3460 return TRUE;
3463 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3464 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3466 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3467 return FALSE;
3470 free(tl_struct->path);
3471 tl_struct->path = NULL;
3473 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3474 ITypeLib_Release(tl_struct->ptLib);
3476 return TRUE;
3479 static HMODULE load_library( MSIPACKAGE *package, const WCHAR *filename, DWORD flags )
3481 HMODULE module;
3482 msi_disable_fs_redirection( package );
3483 module = LoadLibraryExW( filename, NULL, flags );
3484 msi_revert_fs_redirection( package );
3485 return module;
3488 static HRESULT load_typelib( MSIPACKAGE *package, const WCHAR *filename, REGKIND kind, ITypeLib **lib )
3490 HRESULT hr;
3491 msi_disable_fs_redirection( package );
3492 hr = LoadTypeLibEx( filename, kind, lib );
3493 msi_revert_fs_redirection( package );
3494 return hr;
3497 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3499 MSIPACKAGE* package = param;
3500 LPCWSTR component;
3501 MSICOMPONENT *comp;
3502 MSIFILE *file;
3503 struct typelib tl_struct;
3504 ITypeLib *tlib;
3505 HMODULE module;
3506 HRESULT hr;
3508 component = MSI_RecordGetString(row,3);
3509 comp = msi_get_loaded_component(package,component);
3510 if (!comp)
3511 return ERROR_SUCCESS;
3513 comp->Action = msi_get_component_action( package, comp );
3514 if (comp->Action != INSTALLSTATE_LOCAL)
3516 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3517 return ERROR_SUCCESS;
3520 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3522 TRACE("component has no key path\n");
3523 return ERROR_SUCCESS;
3525 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3527 module = load_library( package, file->TargetPath, LOAD_LIBRARY_AS_DATAFILE );
3528 if (module)
3530 LPCWSTR guid;
3531 guid = MSI_RecordGetString(row,1);
3532 CLSIDFromString( guid, &tl_struct.clsid);
3533 tl_struct.source = wcsdup( file->TargetPath );
3534 tl_struct.path = NULL;
3536 EnumResourceNamesW(module, L"TYPELIB", Typelib_EnumResNameProc,
3537 (LONG_PTR)&tl_struct);
3539 if (tl_struct.path)
3541 LPCWSTR helpid, help_path = NULL;
3542 HRESULT res;
3544 helpid = MSI_RecordGetString(row,6);
3546 if (helpid) help_path = msi_get_target_folder( package, helpid );
3547 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3549 if (FAILED(res))
3550 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3551 else
3552 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3554 ITypeLib_Release(tl_struct.ptLib);
3555 free(tl_struct.path);
3557 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3559 FreeLibrary(module);
3560 free(tl_struct.source);
3562 else
3564 hr = load_typelib( package, file->TargetPath, REGKIND_REGISTER, &tlib );
3565 if (FAILED(hr))
3567 ERR( "failed to load type library: %#lx\n", hr );
3568 return ERROR_INSTALL_FAILURE;
3571 ITypeLib_Release(tlib);
3574 return ERROR_SUCCESS;
3577 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3579 MSIQUERY *view;
3580 UINT rc;
3582 if (package->script == SCRIPT_NONE)
3583 return msi_schedule_action(package, SCRIPT_INSTALL, L"RegisterTypeLibraries");
3585 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `TypeLib`", &view);
3586 if (rc != ERROR_SUCCESS)
3587 return ERROR_SUCCESS;
3589 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3590 msiobj_release(&view->hdr);
3591 return rc;
3594 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3596 MSIPACKAGE *package = param;
3597 LPCWSTR component, guid;
3598 MSICOMPONENT *comp;
3599 GUID libid;
3600 UINT version;
3601 LCID language;
3602 SYSKIND syskind;
3603 HRESULT hr;
3605 component = MSI_RecordGetString( row, 3 );
3606 comp = msi_get_loaded_component( package, component );
3607 if (!comp)
3608 return ERROR_SUCCESS;
3610 comp->Action = msi_get_component_action( package, comp );
3611 if (comp->Action != INSTALLSTATE_ABSENT)
3613 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3614 return ERROR_SUCCESS;
3616 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3618 guid = MSI_RecordGetString( row, 1 );
3619 CLSIDFromString( guid, &libid );
3620 version = MSI_RecordGetInteger( row, 4 );
3621 language = MSI_RecordGetInteger( row, 2 );
3623 #ifdef _WIN64
3624 syskind = SYS_WIN64;
3625 #else
3626 syskind = SYS_WIN32;
3627 #endif
3629 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3630 if (FAILED(hr))
3632 WARN( "failed to unregister typelib: %#lx\n", hr );
3635 return ERROR_SUCCESS;
3638 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3640 MSIQUERY *view;
3641 UINT rc;
3643 if (package->script == SCRIPT_NONE)
3644 return msi_schedule_action(package, SCRIPT_INSTALL, L"UnregisterTypeLibraries");
3646 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `TypeLib`", &view );
3647 if (rc != ERROR_SUCCESS)
3648 return ERROR_SUCCESS;
3650 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3651 msiobj_release( &view->hdr );
3652 return rc;
3655 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3657 LPCWSTR directory, extension, link_folder;
3658 WCHAR *link_file = NULL, *filename, *new_filename;
3660 directory = MSI_RecordGetString( row, 2 );
3661 link_folder = msi_get_target_folder( package, directory );
3662 if (!link_folder)
3664 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3665 return NULL;
3667 /* may be needed because of a bug somewhere else */
3668 msi_create_full_path( package, link_folder );
3670 filename = msi_dup_record_field( row, 3 );
3671 if (!filename) return NULL;
3672 msi_reduce_to_long_filename( filename );
3674 extension = wcsrchr( filename, '.' );
3675 if (!extension || wcsicmp( extension, L".lnk" ))
3677 int len = lstrlenW( filename );
3678 new_filename = realloc( filename, len * sizeof(WCHAR) + sizeof(L".lnk") );
3679 if (!new_filename) goto done;
3680 filename = new_filename;
3681 memcpy( filename + len, L".lnk", sizeof(L".lnk") );
3683 link_file = msi_build_directory_name( 2, link_folder, filename );
3685 done:
3686 free( filename );
3687 return link_file;
3690 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3692 WCHAR *folder, *dest, *path;
3694 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3695 folder = msi_dup_property( package->db, L"WindowsFolder" );
3696 else
3698 WCHAR *appdata = msi_dup_property( package->db, L"AppDataFolder" );
3699 folder = msi_build_directory_name( 2, appdata, L"Microsoft\\" );
3700 free( appdata );
3702 dest = msi_build_directory_name( 3, folder, L"Installer\\", package->ProductCode );
3703 msi_create_full_path( package, dest );
3704 path = msi_build_directory_name( 2, dest, icon_name );
3705 free( folder );
3706 free( dest );
3707 return path;
3710 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3712 MSIPACKAGE *package = param;
3713 LPWSTR link_file, deformated, path;
3714 LPCWSTR component, target;
3715 MSICOMPONENT *comp;
3716 IShellLinkW *sl = NULL;
3717 IPersistFile *pf = NULL;
3718 HRESULT res;
3720 component = MSI_RecordGetString(row, 4);
3721 comp = msi_get_loaded_component(package, component);
3722 if (!comp)
3723 return ERROR_SUCCESS;
3725 comp->Action = msi_get_component_action( package, comp );
3726 if (comp->Action != INSTALLSTATE_LOCAL)
3728 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3729 return ERROR_SUCCESS;
3731 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3733 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3734 &IID_IShellLinkW, (LPVOID *) &sl );
3736 if (FAILED( res ))
3738 ERR("CLSID_ShellLink not available\n");
3739 goto err;
3742 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3743 if (FAILED( res ))
3745 ERR("QueryInterface(IID_IPersistFile) failed\n");
3746 goto err;
3749 target = MSI_RecordGetString(row, 5);
3750 if (wcschr(target, '['))
3752 deformat_string( package, target, &path );
3753 TRACE("target path is %s\n", debugstr_w(path));
3754 IShellLinkW_SetPath( sl, path );
3755 free( path );
3757 else
3759 FIXME("poorly handled shortcut format, advertised shortcut\n");
3760 path = resolve_keypath( package, comp );
3761 IShellLinkW_SetPath( sl, path );
3762 free( path );
3765 if (!MSI_RecordIsNull(row,6))
3767 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3768 deformat_string(package, arguments, &deformated);
3769 IShellLinkW_SetArguments(sl,deformated);
3770 free(deformated);
3773 if (!MSI_RecordIsNull(row,7))
3775 LPCWSTR description = MSI_RecordGetString(row, 7);
3776 IShellLinkW_SetDescription(sl, description);
3779 if (!MSI_RecordIsNull(row,8))
3780 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3782 if (!MSI_RecordIsNull(row,9))
3784 INT index;
3785 LPCWSTR icon = MSI_RecordGetString(row, 9);
3787 path = msi_build_icon_path(package, icon);
3788 index = MSI_RecordGetInteger(row,10);
3790 /* no value means 0 */
3791 if (index == MSI_NULL_INTEGER)
3792 index = 0;
3794 IShellLinkW_SetIconLocation(sl, path, index);
3795 free(path);
3798 if (!MSI_RecordIsNull(row,11))
3799 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3801 if (!MSI_RecordIsNull(row,12))
3803 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3804 full_path = msi_get_target_folder( package, wkdir );
3805 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3808 link_file = get_link_file(package, row);
3809 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3811 msi_disable_fs_redirection( package );
3812 IPersistFile_Save(pf, link_file, FALSE);
3813 msi_revert_fs_redirection( package );
3815 free(link_file);
3817 err:
3818 if (pf)
3819 IPersistFile_Release( pf );
3820 if (sl)
3821 IShellLinkW_Release( sl );
3823 return ERROR_SUCCESS;
3826 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3828 MSIQUERY *view;
3829 HRESULT res;
3830 UINT rc;
3832 if (package->script == SCRIPT_NONE)
3833 return msi_schedule_action(package, SCRIPT_INSTALL, L"CreateShortcuts");
3835 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Shortcut`", &view);
3836 if (rc != ERROR_SUCCESS)
3837 return ERROR_SUCCESS;
3839 res = CoInitialize( NULL );
3841 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3842 msiobj_release(&view->hdr);
3844 if (SUCCEEDED(res)) CoUninitialize();
3845 return rc;
3848 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3850 MSIPACKAGE *package = param;
3851 LPWSTR link_file;
3852 LPCWSTR component;
3853 MSICOMPONENT *comp;
3855 component = MSI_RecordGetString( row, 4 );
3856 comp = msi_get_loaded_component( package, component );
3857 if (!comp)
3858 return ERROR_SUCCESS;
3860 comp->Action = msi_get_component_action( package, comp );
3861 if (comp->Action != INSTALLSTATE_ABSENT)
3863 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3864 return ERROR_SUCCESS;
3866 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3868 link_file = get_link_file( package, row );
3869 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3870 if (!msi_delete_file( package, link_file )) WARN( "failed to remove shortcut file %lu\n", GetLastError() );
3871 free( link_file );
3873 return ERROR_SUCCESS;
3876 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3878 MSIQUERY *view;
3879 UINT rc;
3881 if (package->script == SCRIPT_NONE)
3882 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveShortcuts");
3884 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Shortcut`", &view );
3885 if (rc != ERROR_SUCCESS)
3886 return ERROR_SUCCESS;
3888 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3889 msiobj_release( &view->hdr );
3890 return rc;
3893 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3895 MSIPACKAGE *package = param;
3896 HANDLE handle;
3897 WCHAR *icon_path;
3898 const WCHAR *filename;
3899 char buffer[1024];
3900 DWORD sz;
3901 UINT rc;
3903 filename = MSI_RecordGetString( row, 1 );
3904 if (!filename)
3906 ERR("Unable to get filename\n");
3907 return ERROR_SUCCESS;
3910 icon_path = msi_build_icon_path( package, filename );
3912 TRACE("Creating icon file at %s\n", debugstr_w(icon_path));
3914 handle = msi_create_file( package, icon_path, GENERIC_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL );
3915 if (handle == INVALID_HANDLE_VALUE)
3917 ERR("Unable to create file %s\n", debugstr_w(icon_path));
3918 free( icon_path );
3919 return ERROR_SUCCESS;
3924 DWORD count;
3925 sz = 1024;
3926 rc = MSI_RecordReadStream( row, 2, buffer, &sz );
3927 if (rc != ERROR_SUCCESS)
3929 ERR("Failed to get stream\n");
3930 msi_delete_file( package, icon_path );
3931 break;
3933 WriteFile( handle, buffer, sz, &count, NULL );
3934 } while (sz == 1024);
3936 free( icon_path );
3937 CloseHandle( handle );
3939 return ERROR_SUCCESS;
3942 static UINT publish_icons(MSIPACKAGE *package)
3944 MSIQUERY *view;
3945 UINT r;
3947 r = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Icon`", &view);
3948 if (r == ERROR_SUCCESS)
3950 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3951 msiobj_release(&view->hdr);
3952 if (r != ERROR_SUCCESS)
3953 return r;
3955 return ERROR_SUCCESS;
3958 static UINT publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3960 UINT r;
3961 HKEY source;
3962 LPWSTR buffer;
3963 MSIMEDIADISK *disk;
3964 MSISOURCELISTINFO *info;
3966 r = RegCreateKeyW(hkey, L"SourceList", &source);
3967 if (r != ERROR_SUCCESS)
3968 return r;
3970 RegCloseKey(source);
3972 buffer = wcsrchr(package->PackagePath, '\\') + 1;
3973 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3974 package->Context, MSICODE_PRODUCT,
3975 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3976 if (r != ERROR_SUCCESS)
3977 return r;
3979 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3980 package->Context, MSICODE_PRODUCT,
3981 INSTALLPROPERTY_MEDIAPACKAGEPATHW, L"");
3982 if (r != ERROR_SUCCESS)
3983 return r;
3985 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3986 package->Context, MSICODE_PRODUCT,
3987 INSTALLPROPERTY_DISKPROMPTW, L"");
3988 if (r != ERROR_SUCCESS)
3989 return r;
3991 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3993 if (!wcscmp( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3994 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3995 info->options, info->value);
3996 else
3997 MsiSourceListSetInfoW(package->ProductCode, NULL,
3998 info->context, info->options,
3999 info->property, info->value);
4002 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4004 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4005 disk->context, disk->options,
4006 disk->disk_id, disk->volume_label, disk->disk_prompt);
4009 return ERROR_SUCCESS;
4012 static UINT publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4014 WCHAR *buffer, *ptr, *guids, packcode[SQUASHED_GUID_SIZE];
4015 DWORD langid;
4017 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4018 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4019 free(buffer);
4021 langid = msi_get_property_int(package->db, L"ProductLanguage", 0);
4022 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4024 /* FIXME */
4025 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4027 buffer = msi_dup_property(package->db, L"ARPPRODUCTICON");
4028 if (buffer)
4030 LPWSTR path = msi_build_icon_path(package, buffer);
4031 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4032 free(path);
4033 free(buffer);
4036 buffer = msi_dup_property(package->db, L"ProductVersion");
4037 if (buffer)
4039 DWORD verdword = msi_version_str_to_dword(buffer);
4040 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4041 free(buffer);
4044 msi_reg_set_val_dword(hkey, L"Assignment", 0);
4045 msi_reg_set_val_dword(hkey, L"AdvertiseFlags", 0x184);
4046 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4047 msi_reg_set_val_multi_str(hkey, L"Clients", L":\0");
4049 if (!(guids = msi_get_package_code(package->db))) return ERROR_OUTOFMEMORY;
4050 if ((ptr = wcschr(guids, ';'))) *ptr = 0;
4051 squash_guid(guids, packcode);
4052 free(guids);
4053 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4055 return ERROR_SUCCESS;
4058 static UINT publish_upgrade_code(MSIPACKAGE *package)
4060 UINT r;
4061 HKEY hkey;
4062 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4064 upgrade = msi_dup_property(package->db, L"UpgradeCode");
4065 if (!upgrade)
4066 return ERROR_SUCCESS;
4068 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4069 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4070 else
4071 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4073 if (r != ERROR_SUCCESS)
4075 WARN("failed to open upgrade code key\n");
4076 free(upgrade);
4077 return ERROR_SUCCESS;
4079 squash_guid(package->ProductCode, squashed_pc);
4080 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4081 RegCloseKey(hkey);
4082 free(upgrade);
4083 return ERROR_SUCCESS;
4086 static BOOL check_publish(MSIPACKAGE *package)
4088 MSIFEATURE *feature;
4090 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4092 feature->Action = msi_get_feature_action( package, feature );
4093 if (feature->Action == INSTALLSTATE_LOCAL || feature->Action == INSTALLSTATE_SOURCE)
4094 return TRUE;
4097 return FALSE;
4100 static BOOL check_unpublish(MSIPACKAGE *package)
4102 MSIFEATURE *feature;
4104 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4106 feature->Action = msi_get_feature_action( package, feature );
4107 if (feature->Action != INSTALLSTATE_ABSENT)
4108 return FALSE;
4111 return TRUE;
4114 static UINT publish_patches( MSIPACKAGE *package )
4116 WCHAR patch_squashed[GUID_SIZE];
4117 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4118 LONG res;
4119 MSIPATCHINFO *patch;
4120 UINT r;
4121 WCHAR *p, *all_patches = NULL;
4122 DWORD len = 0;
4124 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4125 if (r != ERROR_SUCCESS)
4126 return ERROR_FUNCTION_FAILED;
4128 res = RegCreateKeyExW( product_key, L"Patches", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4129 if (res != ERROR_SUCCESS)
4131 r = ERROR_FUNCTION_FAILED;
4132 goto done;
4135 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4136 if (r != ERROR_SUCCESS)
4137 goto done;
4139 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4141 squash_guid( patch->patchcode, patch_squashed );
4142 len += lstrlenW( patch_squashed ) + 1;
4145 p = all_patches = malloc( (len + 1) * sizeof(WCHAR) );
4146 if (!all_patches)
4147 goto done;
4149 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4151 HKEY patch_key;
4153 squash_guid( patch->patchcode, p );
4154 p += lstrlenW( p ) + 1;
4156 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4157 (const BYTE *)patch->transforms,
4158 (lstrlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4159 if (res != ERROR_SUCCESS)
4160 goto done;
4162 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4163 if (r != ERROR_SUCCESS)
4164 goto done;
4166 res = RegSetValueExW( patch_key, L"LocalPackage", 0, REG_SZ, (const BYTE *)patch->localfile,
4167 (lstrlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4168 RegCloseKey( patch_key );
4169 if (res != ERROR_SUCCESS)
4170 goto done;
4172 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4174 res = GetLastError();
4175 ERR( "unable to copy patch package %lu\n", res );
4176 goto done;
4178 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4179 if (res != ERROR_SUCCESS)
4180 goto done;
4182 res = RegSetValueExW( patch_key, L"State", 0, REG_DWORD, (const BYTE *)&patch->state,
4183 sizeof(patch->state) );
4184 if (res != ERROR_SUCCESS)
4186 RegCloseKey( patch_key );
4187 goto done;
4190 res = RegSetValueExW( patch_key, L"Uninstallable", 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
4191 sizeof(patch->uninstallable) );
4192 RegCloseKey( patch_key );
4193 if (res != ERROR_SUCCESS)
4194 goto done;
4197 all_patches[len] = 0;
4198 res = RegSetValueExW( patches_key, L"Patches", 0, REG_MULTI_SZ,
4199 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4200 if (res != ERROR_SUCCESS)
4201 goto done;
4203 res = RegSetValueExW( product_patches_key, L"AllPatches", 0, REG_MULTI_SZ,
4204 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4205 if (res != ERROR_SUCCESS)
4206 r = ERROR_FUNCTION_FAILED;
4208 done:
4209 RegCloseKey( product_patches_key );
4210 RegCloseKey( patches_key );
4211 RegCloseKey( product_key );
4212 free( all_patches );
4213 return r;
4216 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4218 UINT rc;
4219 HKEY hukey = NULL, hudkey = NULL;
4220 MSIRECORD *uirow;
4221 BOOL republish = FALSE;
4223 if (package->script == SCRIPT_NONE)
4224 return msi_schedule_action(package, SCRIPT_INSTALL, L"PublishProduct");
4226 if (!list_empty(&package->patches))
4228 rc = publish_patches(package);
4229 if (rc != ERROR_SUCCESS)
4230 goto end;
4233 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4234 &hukey, FALSE);
4235 if (rc == ERROR_SUCCESS)
4237 WCHAR *package_code;
4239 package_code = msi_reg_get_val_str(hukey, INSTALLPROPERTY_PACKAGECODEW);
4240 if (package_code)
4242 WCHAR *guid;
4244 guid = msi_get_package_code(package->db);
4245 if (guid)
4247 WCHAR packed[SQUASHED_GUID_SIZE];
4249 squash_guid(guid, packed);
4250 free(guid);
4251 if (!wcscmp(packed, package_code))
4253 TRACE("re-publishing product - new package\n");
4254 republish = TRUE;
4257 free(package_code);
4261 /* FIXME: also need to publish if the product is in advertise mode */
4262 if (!republish && !check_publish(package))
4264 if (hukey)
4265 RegCloseKey(hukey);
4266 return ERROR_SUCCESS;
4269 if (!hukey)
4271 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4272 &hukey, TRUE);
4273 if (rc != ERROR_SUCCESS)
4274 goto end;
4277 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4278 NULL, &hudkey, TRUE);
4279 if (rc != ERROR_SUCCESS)
4280 goto end;
4282 rc = publish_upgrade_code(package);
4283 if (rc != ERROR_SUCCESS)
4284 goto end;
4286 rc = publish_product_properties(package, hukey);
4287 if (rc != ERROR_SUCCESS)
4288 goto end;
4290 rc = publish_sourcelist(package, hukey);
4291 if (rc != ERROR_SUCCESS)
4292 goto end;
4294 rc = publish_icons(package);
4296 end:
4297 uirow = MSI_CreateRecord( 1 );
4298 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4299 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4300 msiobj_release( &uirow->hdr );
4302 RegCloseKey(hukey);
4303 RegCloseKey(hudkey);
4304 return rc;
4307 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4309 WCHAR *filename, *ptr, *folder, *ret;
4310 const WCHAR *dirprop;
4312 filename = msi_dup_record_field( row, 2 );
4313 if (filename && (ptr = wcschr( filename, '|' )))
4314 ptr++;
4315 else
4316 ptr = filename;
4318 dirprop = MSI_RecordGetString( row, 3 );
4319 if (dirprop)
4321 folder = wcsdup( msi_get_target_folder( package, dirprop ) );
4322 if (!folder) folder = msi_dup_property( package->db, dirprop );
4324 else
4325 folder = msi_dup_property( package->db, L"WindowsFolder" );
4327 if (!folder)
4329 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4330 free( filename );
4331 return NULL;
4334 ret = msi_build_directory_name( 2, folder, ptr );
4336 free( filename );
4337 free( folder );
4338 return ret;
4341 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4343 MSIPACKAGE *package = param;
4344 LPCWSTR component, section, key, value, identifier;
4345 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4346 MSIRECORD * uirow;
4347 INT action;
4348 MSICOMPONENT *comp;
4350 component = MSI_RecordGetString(row, 8);
4351 comp = msi_get_loaded_component(package,component);
4352 if (!comp)
4353 return ERROR_SUCCESS;
4355 comp->Action = msi_get_component_action( package, comp );
4356 if (comp->Action != INSTALLSTATE_LOCAL)
4358 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4359 return ERROR_SUCCESS;
4362 identifier = MSI_RecordGetString(row,1);
4363 section = MSI_RecordGetString(row,4);
4364 key = MSI_RecordGetString(row,5);
4365 value = MSI_RecordGetString(row,6);
4366 action = MSI_RecordGetInteger(row,7);
4368 deformat_string(package,section,&deformated_section);
4369 deformat_string(package,key,&deformated_key);
4370 deformat_string(package,value,&deformated_value);
4372 fullname = get_ini_file_name(package, row);
4374 if (action == 0)
4376 TRACE("Adding value %s to section %s in %s\n",
4377 debugstr_w(deformated_key), debugstr_w(deformated_section),
4378 debugstr_w(fullname));
4379 WritePrivateProfileStringW(deformated_section, deformated_key,
4380 deformated_value, fullname);
4382 else if (action == 1)
4384 WCHAR returned[10];
4385 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4386 returned, 10, fullname);
4387 if (returned[0] == 0)
4389 TRACE("Adding value %s to section %s in %s\n",
4390 debugstr_w(deformated_key), debugstr_w(deformated_section),
4391 debugstr_w(fullname));
4393 WritePrivateProfileStringW(deformated_section, deformated_key,
4394 deformated_value, fullname);
4397 else if (action == 3)
4398 FIXME("Append to existing section not yet implemented\n");
4400 uirow = MSI_CreateRecord(4);
4401 MSI_RecordSetStringW(uirow,1,identifier);
4402 MSI_RecordSetStringW(uirow,2,deformated_section);
4403 MSI_RecordSetStringW(uirow,3,deformated_key);
4404 MSI_RecordSetStringW(uirow,4,deformated_value);
4405 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4406 msiobj_release( &uirow->hdr );
4408 free(fullname);
4409 free(deformated_key);
4410 free(deformated_value);
4411 free(deformated_section);
4412 return ERROR_SUCCESS;
4415 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4417 MSIQUERY *view;
4418 UINT rc;
4420 if (package->script == SCRIPT_NONE)
4421 return msi_schedule_action(package, SCRIPT_INSTALL, L"WriteIniValues");
4423 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `IniFile`", &view);
4424 if (rc != ERROR_SUCCESS)
4425 return ERROR_SUCCESS;
4427 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4428 msiobj_release(&view->hdr);
4429 return rc;
4432 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4434 MSIPACKAGE *package = param;
4435 LPCWSTR component, section, key, value, identifier;
4436 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4437 MSICOMPONENT *comp;
4438 MSIRECORD *uirow;
4439 INT action;
4441 component = MSI_RecordGetString( row, 8 );
4442 comp = msi_get_loaded_component( package, component );
4443 if (!comp)
4444 return ERROR_SUCCESS;
4446 comp->Action = msi_get_component_action( package, comp );
4447 if (comp->Action != INSTALLSTATE_ABSENT)
4449 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4450 return ERROR_SUCCESS;
4453 identifier = MSI_RecordGetString( row, 1 );
4454 section = MSI_RecordGetString( row, 4 );
4455 key = MSI_RecordGetString( row, 5 );
4456 value = MSI_RecordGetString( row, 6 );
4457 action = MSI_RecordGetInteger( row, 7 );
4459 deformat_string( package, section, &deformated_section );
4460 deformat_string( package, key, &deformated_key );
4461 deformat_string( package, value, &deformated_value );
4463 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4465 filename = get_ini_file_name( package, row );
4467 TRACE("Removing key %s from section %s in %s\n",
4468 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4470 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4472 WARN( "unable to remove key %lu\n", GetLastError() );
4474 free( filename );
4476 else
4477 FIXME("Unsupported action %d\n", action);
4480 uirow = MSI_CreateRecord( 4 );
4481 MSI_RecordSetStringW( uirow, 1, identifier );
4482 MSI_RecordSetStringW( uirow, 2, deformated_section );
4483 MSI_RecordSetStringW( uirow, 3, deformated_key );
4484 MSI_RecordSetStringW( uirow, 4, deformated_value );
4485 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4486 msiobj_release( &uirow->hdr );
4488 free( deformated_key );
4489 free( deformated_value );
4490 free( deformated_section );
4491 return ERROR_SUCCESS;
4494 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4496 MSIPACKAGE *package = param;
4497 LPCWSTR component, section, key, value, identifier;
4498 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4499 MSICOMPONENT *comp;
4500 MSIRECORD *uirow;
4501 INT action;
4503 component = MSI_RecordGetString( row, 8 );
4504 comp = msi_get_loaded_component( package, component );
4505 if (!comp)
4506 return ERROR_SUCCESS;
4508 comp->Action = msi_get_component_action( package, comp );
4509 if (comp->Action != INSTALLSTATE_LOCAL)
4511 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4512 return ERROR_SUCCESS;
4515 identifier = MSI_RecordGetString( row, 1 );
4516 section = MSI_RecordGetString( row, 4 );
4517 key = MSI_RecordGetString( row, 5 );
4518 value = MSI_RecordGetString( row, 6 );
4519 action = MSI_RecordGetInteger( row, 7 );
4521 deformat_string( package, section, &deformated_section );
4522 deformat_string( package, key, &deformated_key );
4523 deformat_string( package, value, &deformated_value );
4525 if (action == msidbIniFileActionRemoveLine)
4527 filename = get_ini_file_name( package, row );
4529 TRACE("Removing key %s from section %s in %s\n",
4530 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4532 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4534 WARN( "unable to remove key %lu\n", GetLastError() );
4536 free( filename );
4538 else
4539 FIXME("Unsupported action %d\n", action);
4541 uirow = MSI_CreateRecord( 4 );
4542 MSI_RecordSetStringW( uirow, 1, identifier );
4543 MSI_RecordSetStringW( uirow, 2, deformated_section );
4544 MSI_RecordSetStringW( uirow, 3, deformated_key );
4545 MSI_RecordSetStringW( uirow, 4, deformated_value );
4546 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4547 msiobj_release( &uirow->hdr );
4549 free( deformated_key );
4550 free( deformated_value );
4551 free( deformated_section );
4552 return ERROR_SUCCESS;
4555 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4557 MSIQUERY *view;
4558 UINT rc;
4560 if (package->script == SCRIPT_NONE)
4561 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveIniValues");
4563 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `IniFile`", &view );
4564 if (rc == ERROR_SUCCESS)
4566 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4567 msiobj_release( &view->hdr );
4568 if (rc != ERROR_SUCCESS)
4569 return rc;
4571 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `RemoveIniFile`", &view );
4572 if (rc == ERROR_SUCCESS)
4574 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4575 msiobj_release( &view->hdr );
4576 if (rc != ERROR_SUCCESS)
4577 return rc;
4579 return ERROR_SUCCESS;
4582 static void register_dll( const WCHAR *dll, BOOL unregister )
4584 static const WCHAR regW[] = L"regsvr32.exe \"%s\"";
4585 static const WCHAR unregW[] = L"regsvr32.exe /u \"%s\"";
4586 PROCESS_INFORMATION pi;
4587 STARTUPINFOW si;
4588 WCHAR *cmd;
4590 if (!(cmd = malloc( wcslen(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4592 if (unregister) swprintf( cmd, lstrlenW(dll) + ARRAY_SIZE(unregW), unregW, dll );
4593 else swprintf( cmd, lstrlenW(dll) + ARRAY_SIZE(unregW), regW, dll );
4595 memset( &si, 0, sizeof(STARTUPINFOW) );
4596 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4598 CloseHandle( pi.hThread );
4599 msi_dialog_check_messages( pi.hProcess );
4600 CloseHandle( pi.hProcess );
4602 free( cmd );
4605 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4607 MSIPACKAGE *package = param;
4608 LPCWSTR filename;
4609 MSIFILE *file;
4610 MSIRECORD *uirow;
4612 filename = MSI_RecordGetString( row, 1 );
4613 file = msi_get_loaded_file( package, filename );
4614 if (!file)
4616 WARN("unable to find file %s\n", debugstr_w(filename));
4617 return ERROR_SUCCESS;
4619 file->Component->Action = msi_get_component_action( package, file->Component );
4620 if (file->Component->Action != INSTALLSTATE_LOCAL)
4622 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4623 return ERROR_SUCCESS;
4626 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4627 register_dll( file->TargetPath, FALSE );
4629 uirow = MSI_CreateRecord( 2 );
4630 MSI_RecordSetStringW( uirow, 1, file->File );
4631 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4632 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4633 msiobj_release( &uirow->hdr );
4635 return ERROR_SUCCESS;
4638 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4640 MSIQUERY *view;
4641 UINT rc;
4643 if (package->script == SCRIPT_NONE)
4644 return msi_schedule_action(package, SCRIPT_INSTALL, L"SelfRegModules");
4646 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `SelfReg`", &view);
4647 if (rc != ERROR_SUCCESS)
4648 return ERROR_SUCCESS;
4650 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4651 msiobj_release(&view->hdr);
4652 return rc;
4655 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4657 MSIPACKAGE *package = param;
4658 LPCWSTR filename;
4659 MSIFILE *file;
4660 MSIRECORD *uirow;
4662 filename = MSI_RecordGetString( row, 1 );
4663 file = msi_get_loaded_file( package, filename );
4664 if (!file)
4666 WARN("unable to find file %s\n", debugstr_w(filename));
4667 return ERROR_SUCCESS;
4669 file->Component->Action = msi_get_component_action( package, file->Component );
4670 if (file->Component->Action != INSTALLSTATE_ABSENT)
4672 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4673 return ERROR_SUCCESS;
4676 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4677 register_dll( file->TargetPath, TRUE );
4679 uirow = MSI_CreateRecord( 2 );
4680 MSI_RecordSetStringW( uirow, 1, file->File );
4681 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4682 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4683 msiobj_release( &uirow->hdr );
4685 return ERROR_SUCCESS;
4688 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4690 MSIQUERY *view;
4691 UINT rc;
4693 if (package->script == SCRIPT_NONE)
4694 return msi_schedule_action(package, SCRIPT_INSTALL, L"SelfUnregModules");
4696 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `SelfReg`", &view );
4697 if (rc != ERROR_SUCCESS)
4698 return ERROR_SUCCESS;
4700 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4701 msiobj_release( &view->hdr );
4702 return rc;
4705 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4707 MSIFEATURE *feature;
4708 UINT rc;
4709 HKEY hkey = NULL, userdata = NULL;
4711 if (package->script == SCRIPT_NONE)
4712 return msi_schedule_action(package, SCRIPT_INSTALL, L"PublishFeatures");
4714 if (!check_publish(package))
4715 return ERROR_SUCCESS;
4717 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4718 &hkey, TRUE);
4719 if (rc != ERROR_SUCCESS)
4720 goto end;
4722 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4723 &userdata, TRUE);
4724 if (rc != ERROR_SUCCESS)
4725 goto end;
4727 /* here the guids are base 85 encoded */
4728 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4730 ComponentList *cl;
4731 LPWSTR data = NULL;
4732 GUID clsid;
4733 INT size;
4734 BOOL absent = FALSE;
4735 MSIRECORD *uirow;
4737 if (feature->Level <= 0) continue;
4738 if (feature->Action == INSTALLSTATE_UNKNOWN &&
4739 feature->Installed != INSTALLSTATE_ABSENT) continue;
4741 if (feature->Action != INSTALLSTATE_LOCAL &&
4742 feature->Action != INSTALLSTATE_SOURCE &&
4743 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4745 size = 1;
4746 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4748 size += 21;
4750 if (feature->Feature_Parent)
4751 size += lstrlenW( feature->Feature_Parent )+2;
4753 data = malloc(size * sizeof(WCHAR));
4755 data[0] = 0;
4756 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4758 MSICOMPONENT* component = cl->component;
4759 WCHAR buf[21];
4761 buf[0] = 0;
4762 if (component->ComponentId)
4764 TRACE("From %s\n",debugstr_w(component->ComponentId));
4765 CLSIDFromString(component->ComponentId, &clsid);
4766 encode_base85_guid(&clsid,buf);
4767 TRACE("to %s\n",debugstr_w(buf));
4768 lstrcatW(data,buf);
4772 if (feature->Feature_Parent)
4774 lstrcatW(data, L"\2");
4775 lstrcatW(data, feature->Feature_Parent);
4778 msi_reg_set_val_str( userdata, feature->Feature, data );
4779 free(data);
4781 size = 0;
4782 if (feature->Feature_Parent)
4783 size = lstrlenW(feature->Feature_Parent)*sizeof(WCHAR);
4784 if (!absent)
4786 size += sizeof(WCHAR);
4787 RegSetValueExW(hkey, feature->Feature, 0 ,REG_SZ,
4788 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : L""), size);
4790 else
4792 size += 2*sizeof(WCHAR);
4793 data = malloc(size);
4794 data[0] = 0x6;
4795 data[1] = 0;
4796 if (feature->Feature_Parent)
4797 lstrcpyW( &data[1], feature->Feature_Parent );
4798 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4799 (LPBYTE)data,size);
4800 free(data);
4803 /* the UI chunk */
4804 uirow = MSI_CreateRecord( 1 );
4805 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4806 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4807 msiobj_release( &uirow->hdr );
4808 /* FIXME: call msi_ui_progress? */
4811 end:
4812 RegCloseKey(hkey);
4813 RegCloseKey(userdata);
4814 return rc;
4817 static UINT unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4819 UINT r;
4820 HKEY hkey;
4821 MSIRECORD *uirow;
4823 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4825 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4826 &hkey, FALSE);
4827 if (r == ERROR_SUCCESS)
4829 RegDeleteValueW(hkey, feature->Feature);
4830 RegCloseKey(hkey);
4833 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4834 &hkey, FALSE);
4835 if (r == ERROR_SUCCESS)
4837 RegDeleteValueW(hkey, feature->Feature);
4838 RegCloseKey(hkey);
4841 uirow = MSI_CreateRecord( 1 );
4842 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4843 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4844 msiobj_release( &uirow->hdr );
4846 return ERROR_SUCCESS;
4849 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4851 MSIFEATURE *feature;
4853 if (package->script == SCRIPT_NONE)
4854 return msi_schedule_action(package, SCRIPT_INSTALL, L"UnpublishFeatures");
4856 if (!check_unpublish(package))
4857 return ERROR_SUCCESS;
4859 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4861 unpublish_feature(package, feature);
4864 return ERROR_SUCCESS;
4867 static UINT publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4869 static const WCHAR *propval[] =
4871 L"ARPAUTHORIZEDCDFPREFIX", L"AuthorizedCDFPrefix",
4872 L"ARPCONTACT", L"Contact",
4873 L"ARPCOMMENTS", L"Comments",
4874 L"ProductName", L"DisplayName",
4875 L"ARPHELPLINK", L"HelpLink",
4876 L"ARPHELPTELEPHONE", L"HelpTelephone",
4877 L"ARPINSTALLLOCATION", L"InstallLocation",
4878 L"SourceDir", L"InstallSource",
4879 L"Manufacturer", L"Publisher",
4880 L"ARPREADME", L"ReadMe",
4881 L"ARPSIZE", L"Size",
4882 L"ARPURLINFOABOUT", L"URLInfoAbout",
4883 L"ARPURLUPDATEINFO", L"URLUpdateInfo",
4884 NULL
4886 const WCHAR **p = propval;
4887 SYSTEMTIME systime;
4888 DWORD size, langid;
4889 WCHAR date[9], *val, *buffer;
4890 const WCHAR *prop, *key;
4892 while (*p)
4894 prop = *p++;
4895 key = *p++;
4896 val = msi_dup_property(package->db, prop);
4897 msi_reg_set_val_str(hkey, key, val);
4898 free(val);
4901 msi_reg_set_val_dword(hkey, L"WindowsInstaller", 1);
4902 if (msi_get_property_int( package->db, L"ARPSYSTEMCOMPONENT", 0 ))
4904 msi_reg_set_val_dword( hkey, L"SystemComponent", 1 );
4907 if (msi_get_property_int( package->db, L"ARPNOREMOVE", 0 ))
4908 msi_reg_set_val_dword( hkey, L"NoRemove", 1 );
4909 else
4911 static const WCHAR fmt_install[] = L"MsiExec.exe /I[ProductCode]";
4912 static const WCHAR fmt_uninstall[] = L"MsiExec.exe /X[ProductCode]";
4913 const WCHAR *fmt = fmt_install;
4915 if (msi_get_property_int( package->db, L"ARPNOREPAIR", 0 ))
4916 msi_reg_set_val_dword( hkey, L"NoRepair", 1 );
4918 if (msi_get_property_int( package->db, L"ARPNOMODIFY", 0 ))
4920 msi_reg_set_val_dword( hkey, L"NoModify", 1 );
4921 fmt = fmt_uninstall;
4924 size = deformat_string(package, fmt, &buffer) * sizeof(WCHAR);
4925 RegSetValueExW(hkey, L"ModifyPath", 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4926 RegSetValueExW(hkey, L"UninstallString", 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4927 free(buffer);
4930 /* FIXME: Write real Estimated Size when we have it */
4931 msi_reg_set_val_dword(hkey, L"EstimatedSize", 0);
4933 GetLocalTime(&systime);
4934 swprintf(date, ARRAY_SIZE(date), L"%d%02d%02d", systime.wYear, systime.wMonth, systime.wDay);
4935 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4937 langid = msi_get_property_int(package->db, L"ProductLanguage", 0);
4938 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4940 buffer = msi_dup_property(package->db, L"ProductVersion");
4941 msi_reg_set_val_str(hkey, L"DisplayVersion", buffer);
4942 if (buffer)
4944 DWORD verdword = msi_version_str_to_dword(buffer);
4946 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4947 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4948 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4949 free(buffer);
4952 return ERROR_SUCCESS;
4955 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4957 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
4958 MSIRECORD *uirow;
4959 HKEY hkey, props, upgrade_key;
4960 UINT rc;
4962 if (package->script == SCRIPT_NONE)
4963 return msi_schedule_action(package, SCRIPT_INSTALL, L"RegisterProduct");
4965 /* FIXME: also need to publish if the product is in advertise mode */
4966 if (!msi_get_property_int( package->db, L"ProductToBeRegistered", 0 ) && !check_publish(package))
4967 return ERROR_SUCCESS;
4969 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4970 if (rc != ERROR_SUCCESS)
4971 return rc;
4973 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4974 if (rc != ERROR_SUCCESS)
4975 goto done;
4977 rc = publish_install_properties(package, hkey);
4978 if (rc != ERROR_SUCCESS)
4979 goto done;
4981 rc = publish_install_properties(package, props);
4982 if (rc != ERROR_SUCCESS)
4983 goto done;
4985 upgrade_code = msi_dup_property(package->db, L"UpgradeCode");
4986 if (upgrade_code)
4988 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4989 if (rc == ERROR_SUCCESS)
4991 squash_guid( package->ProductCode, squashed_pc );
4992 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4993 RegCloseKey( upgrade_key );
4995 free( upgrade_code );
4997 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4998 package->delete_on_close = FALSE;
5000 done:
5001 uirow = MSI_CreateRecord( 1 );
5002 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5003 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5004 msiobj_release( &uirow->hdr );
5006 RegCloseKey(hkey);
5007 return ERROR_SUCCESS;
5010 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5012 return execute_script(package, SCRIPT_INSTALL);
5015 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5017 MSIPACKAGE *package = param;
5018 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5019 WCHAR *p, *icon_path;
5021 if (!icon) return ERROR_SUCCESS;
5022 if ((icon_path = msi_build_icon_path( package, icon )))
5024 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5025 msi_delete_file( package, icon_path );
5026 if ((p = wcsrchr( icon_path, '\\' )))
5028 *p = 0;
5029 msi_remove_directory( package, icon_path );
5031 free( icon_path );
5033 return ERROR_SUCCESS;
5036 static UINT unpublish_icons( MSIPACKAGE *package )
5038 MSIQUERY *view;
5039 UINT r;
5041 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Icon`", &view );
5042 if (r == ERROR_SUCCESS)
5044 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5045 msiobj_release( &view->hdr );
5046 if (r != ERROR_SUCCESS)
5047 return r;
5049 return ERROR_SUCCESS;
5052 static void remove_product_upgrade_code( MSIPACKAGE *package )
5054 WCHAR *code, product[SQUASHED_GUID_SIZE];
5055 HKEY hkey;
5056 LONG res;
5057 DWORD count;
5059 squash_guid( package->ProductCode, product );
5060 if (!(code = msi_dup_property( package->db, L"UpgradeCode" )))
5062 WARN( "upgrade code not found\n" );
5063 return;
5065 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5067 RegDeleteValueW( hkey, product );
5068 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5069 RegCloseKey( hkey );
5070 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5072 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5074 RegDeleteValueW( hkey, product );
5075 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5076 RegCloseKey( hkey );
5077 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5079 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5081 RegDeleteValueW( hkey, product );
5082 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5083 RegCloseKey( hkey );
5084 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5087 free( code );
5090 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5092 MSIPATCHINFO *patch;
5094 MSIREG_DeleteProductKey(package->ProductCode);
5095 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5096 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5098 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5099 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5100 MSIREG_DeleteUserProductKey(package->ProductCode);
5101 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5103 remove_product_upgrade_code( package );
5105 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5107 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5108 if (!wcscmp( package->ProductCode, patch->products ))
5110 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5111 patch->delete_on_close = TRUE;
5113 /* FIXME: remove local patch package if this is the last product */
5115 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5116 package->delete_on_close = TRUE;
5118 unpublish_icons( package );
5119 return ERROR_SUCCESS;
5122 static BOOL is_full_uninstall( MSIPACKAGE *package )
5124 MSIFEATURE *feature;
5126 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5128 if (feature->Action != INSTALLSTATE_ABSENT &&
5129 (feature->Installed != INSTALLSTATE_ABSENT || feature->Action != INSTALLSTATE_UNKNOWN))
5130 return FALSE;
5133 return TRUE;
5136 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5138 UINT rc;
5139 MSIFILE *file;
5140 MSIFILEPATCH *patch;
5142 /* first do the same as an InstallExecute */
5143 rc = execute_script(package, SCRIPT_INSTALL);
5144 if (rc != ERROR_SUCCESS)
5145 return rc;
5147 /* then handle commit actions */
5148 rc = execute_script(package, SCRIPT_COMMIT);
5149 if (rc != ERROR_SUCCESS)
5150 return rc;
5152 /* install global assemblies */
5153 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
5155 MSICOMPONENT *comp = file->Component;
5157 if (!msi_is_global_assembly( comp ) || (file->state != msifs_missing && file->state != msifs_overwrite))
5158 continue;
5160 rc = msi_install_assembly( package, comp );
5161 if (rc != ERROR_SUCCESS)
5163 ERR("Failed to install assembly\n");
5164 return ERROR_INSTALL_FAILURE;
5166 file->state = msifs_installed;
5169 /* patch global assemblies */
5170 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
5172 MSICOMPONENT *comp = patch->File->Component;
5174 if (!msi_is_global_assembly( comp ) || !patch->path) continue;
5176 rc = msi_patch_assembly( package, comp->assembly, patch );
5177 if (rc && !(patch->Attributes & msidbPatchAttributesNonVital))
5179 ERR("Failed to apply patch to file: %s\n", debugstr_w(patch->File->File));
5180 return rc;
5183 if ((rc = msi_install_assembly( package, comp )))
5185 ERR("Failed to install patched assembly\n");
5186 return rc;
5190 if (is_full_uninstall(package))
5191 rc = ACTION_UnpublishProduct(package);
5193 return rc;
5196 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5198 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5199 HKEY hkey;
5201 squash_guid( package->ProductCode, squashed_pc );
5203 GetSystemDirectoryW(sysdir, ARRAY_SIZE(sysdir));
5204 RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", &hkey);
5205 swprintf(buffer, ARRAY_SIZE(buffer), L"%s\\MsiExec.exe /@ \"%s\"", sysdir, squashed_pc);
5207 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5208 RegCloseKey(hkey);
5210 TRACE("Reboot command %s\n",debugstr_w(buffer));
5212 RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\RunOnceEntries",
5213 &hkey);
5214 swprintf( buffer, ARRAY_SIZE(buffer), L"/I \"%s\" AFTERREBOOT=1 RUNONCEENTRY=\"%s\"", package->ProductCode,
5215 squashed_pc );
5217 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5218 RegCloseKey(hkey);
5220 return ERROR_INSTALL_SUSPEND;
5223 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5225 DWORD attrib;
5226 UINT rc;
5229 * We are currently doing what should be done here in the top level Install
5230 * however for Administrative and uninstalls this step will be needed
5232 if (!package->PackagePath)
5233 return ERROR_SUCCESS;
5235 msi_set_sourcedir_props(package, TRUE);
5237 attrib = GetFileAttributesW(package->db->path);
5238 if (attrib == INVALID_FILE_ATTRIBUTES)
5240 MSIRECORD *record;
5241 LPWSTR prompt;
5242 DWORD size = 0;
5244 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5245 package->Context, MSICODE_PRODUCT,
5246 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5247 if (rc == ERROR_MORE_DATA)
5249 prompt = malloc(size * sizeof(WCHAR));
5250 MsiSourceListGetInfoW(package->ProductCode, NULL,
5251 package->Context, MSICODE_PRODUCT,
5252 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5254 else
5255 prompt = wcsdup(package->db->path);
5257 record = MSI_CreateRecord(2);
5258 MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK);
5259 MSI_RecordSetStringW(record, 2, prompt);
5260 free(prompt);
5261 while(attrib == INVALID_FILE_ATTRIBUTES)
5263 MSI_RecordSetStringW(record, 0, NULL);
5264 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record);
5265 if (rc == IDCANCEL)
5266 return ERROR_INSTALL_USEREXIT;
5267 attrib = GetFileAttributesW(package->db->path);
5269 rc = ERROR_SUCCESS;
5271 else
5272 return ERROR_SUCCESS;
5274 return rc;
5277 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5279 static const WCHAR szPropKeys[][80] =
5281 L"ProductID",
5282 L"USERNAME",
5283 L"COMPANYNAME",
5284 L"",
5286 static const WCHAR szRegKeys[][80] =
5288 L"ProductID",
5289 L"RegOwner",
5290 L"RegCompany",
5291 L"",
5293 HKEY hkey = 0;
5294 LPWSTR buffer, productid = NULL;
5295 UINT i, rc = ERROR_SUCCESS;
5296 MSIRECORD *uirow;
5298 if (package->script == SCRIPT_NONE)
5299 return msi_schedule_action(package, SCRIPT_INSTALL, L"RegisterUser");
5301 if (check_unpublish(package))
5303 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5304 goto end;
5307 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5308 if (!productid)
5309 goto end;
5311 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5312 NULL, &hkey, TRUE);
5313 if (rc != ERROR_SUCCESS)
5314 goto end;
5316 for( i = 0; szPropKeys[i][0]; i++ )
5318 buffer = msi_dup_property( package->db, szPropKeys[i] );
5319 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5320 free( buffer );
5323 end:
5324 uirow = MSI_CreateRecord( 1 );
5325 MSI_RecordSetStringW( uirow, 1, productid );
5326 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5327 msiobj_release( &uirow->hdr );
5329 free(productid);
5330 RegCloseKey(hkey);
5331 return rc;
5334 static UINT iterate_properties(MSIRECORD *record, void *param)
5336 MSIRECORD *uirow;
5338 uirow = MSI_CloneRecord(record);
5339 if (!uirow) return ERROR_OUTOFMEMORY;
5340 MSI_RecordSetStringW(uirow, 0, L"Property(S): [1] = [2]");
5341 MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
5342 msiobj_release(&uirow->hdr);
5344 return ERROR_SUCCESS;
5348 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5350 WCHAR *productname;
5351 WCHAR *action;
5352 WCHAR *info_template;
5353 MSIQUERY *view;
5354 MSIRECORD *uirow, *uirow_info;
5355 UINT rc;
5357 /* Send COMMONDATA and INFO messages. */
5358 /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
5359 uirow = MSI_CreateRecord(3);
5360 if (!uirow) return ERROR_OUTOFMEMORY;
5361 MSI_RecordSetStringW(uirow, 0, NULL);
5362 MSI_RecordSetInteger(uirow, 1, 0);
5363 MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
5364 MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
5365 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5366 /* FIXME: send INSTALLMESSAGE_PROGRESS */
5367 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5369 if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
5371 uirow_info = MSI_CreateRecord(0);
5372 if (!uirow_info)
5374 msiobj_release(&uirow->hdr);
5375 return ERROR_OUTOFMEMORY;
5377 info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
5378 MSI_RecordSetStringW(uirow_info, 0, info_template);
5379 free(info_template);
5380 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
5381 msiobj_release(&uirow_info->hdr);
5384 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5386 productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
5387 MSI_RecordSetInteger(uirow, 1, 1);
5388 MSI_RecordSetStringW(uirow, 2, productname);
5389 MSI_RecordSetStringW(uirow, 3, NULL);
5390 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5391 msiobj_release(&uirow->hdr);
5393 package->LastActionResult = MSI_NULL_INTEGER;
5395 action = msi_dup_property(package->db, L"EXECUTEACTION");
5396 if (!action) action = msi_strdupW(L"INSTALL", ARRAY_SIZE(L"INSTALL") - 1);
5398 /* Perform the action. Top-level actions trigger a sequence. */
5399 if (!wcscmp(action, L"INSTALL"))
5401 /* Send ACTIONSTART/INFO and INSTALLSTART. */
5402 ui_actionstart(package, L"INSTALL", NULL, NULL);
5403 ui_actioninfo(package, L"INSTALL", TRUE, 0);
5404 uirow = MSI_CreateRecord(2);
5405 if (!uirow)
5407 rc = ERROR_OUTOFMEMORY;
5408 goto end;
5410 MSI_RecordSetStringW(uirow, 0, NULL);
5411 MSI_RecordSetStringW(uirow, 1, productname);
5412 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5413 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
5414 msiobj_release(&uirow->hdr);
5416 /* Perform the installation. Always use the ExecuteSequence. */
5417 package->InWhatSequence |= SEQUENCE_EXEC;
5418 rc = ACTION_ProcessExecSequence(package);
5420 /* Send return value and INSTALLEND. */
5421 ui_actioninfo(package, L"INSTALL", FALSE, !rc);
5422 uirow = MSI_CreateRecord(3);
5423 if (!uirow)
5425 rc = ERROR_OUTOFMEMORY;
5426 goto end;
5428 MSI_RecordSetStringW(uirow, 0, NULL);
5429 MSI_RecordSetStringW(uirow, 1, productname);
5430 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5431 MSI_RecordSetInteger(uirow, 3, !rc);
5432 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
5433 msiobj_release(&uirow->hdr);
5435 else
5436 rc = ACTION_PerformAction(package, action);
5438 /* Send all set properties. */
5439 if (!MSI_OpenQuery(package->db, &view, L"SELECT * FROM `_Property`"))
5441 MSI_IterateRecords(view, NULL, iterate_properties, package);
5442 msiobj_release(&view->hdr);
5445 /* And finally, toggle the cancel off and on. */
5446 uirow = MSI_CreateRecord(2);
5447 if (!uirow)
5449 rc = ERROR_OUTOFMEMORY;
5450 goto end;
5452 MSI_RecordSetStringW(uirow, 0, NULL);
5453 MSI_RecordSetInteger(uirow, 1, 2);
5454 MSI_RecordSetInteger(uirow, 2, 0);
5455 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5456 MSI_RecordSetInteger(uirow, 2, 1);
5457 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5458 msiobj_release(&uirow->hdr);
5460 end:
5461 free(productname);
5462 free(action);
5463 return rc;
5466 static UINT ACTION_INSTALL(MSIPACKAGE *package)
5468 msi_set_property(package->db, L"EXECUTEACTION", L"INSTALL", -1);
5469 if (needs_ui_sequence(package) && ui_sequence_exists(package))
5471 package->InWhatSequence |= SEQUENCE_UI;
5472 return ACTION_ProcessUISequence(package);
5474 else
5475 return ACTION_ExecuteAction(package);
5478 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5480 WCHAR productid_85[21], component_85[21], *ret;
5481 GUID clsid;
5482 DWORD sz;
5484 /* > is used if there is a component GUID and < if not. */
5486 productid_85[0] = 0;
5487 component_85[0] = 0;
5488 CLSIDFromString( package->ProductCode, &clsid );
5490 encode_base85_guid( &clsid, productid_85 );
5491 if (component)
5493 CLSIDFromString( component->ComponentId, &clsid );
5494 encode_base85_guid( &clsid, component_85 );
5497 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5498 debugstr_w(component_85));
5500 sz = 20 + lstrlenW( feature ) + 20 + 3;
5501 ret = calloc( 1, sz * sizeof(WCHAR) );
5502 if (ret) swprintf( ret, sz, L"%s%s%c%s", productid_85, feature, component ? '>' : '<', component_85 );
5503 return ret;
5506 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5508 MSIPACKAGE *package = param;
5509 LPCWSTR compgroupid, component, feature, qualifier, text;
5510 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5511 HKEY hkey = NULL;
5512 UINT rc;
5513 MSICOMPONENT *comp;
5514 MSIFEATURE *feat;
5515 DWORD sz;
5516 MSIRECORD *uirow;
5517 int len;
5519 feature = MSI_RecordGetString(rec, 5);
5520 feat = msi_get_loaded_feature(package, feature);
5521 if (!feat)
5522 return ERROR_SUCCESS;
5524 feat->Action = msi_get_feature_action( package, feat );
5525 if (feat->Action != INSTALLSTATE_LOCAL &&
5526 feat->Action != INSTALLSTATE_SOURCE &&
5527 feat->Action != INSTALLSTATE_ADVERTISED)
5529 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5530 return ERROR_SUCCESS;
5533 component = MSI_RecordGetString(rec, 3);
5534 comp = msi_get_loaded_component(package, component);
5535 if (!comp)
5536 return ERROR_SUCCESS;
5538 compgroupid = MSI_RecordGetString(rec,1);
5539 qualifier = MSI_RecordGetString(rec,2);
5541 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5542 if (rc != ERROR_SUCCESS)
5543 goto end;
5545 advertise = msi_create_component_advertise_string( package, comp, feature );
5546 text = MSI_RecordGetString( rec, 4 );
5547 if (text)
5549 p = malloc( (wcslen( advertise ) + wcslen( text ) + 1) * sizeof(WCHAR) );
5550 lstrcpyW( p, advertise );
5551 lstrcatW( p, text );
5552 free( advertise );
5553 advertise = p;
5555 existing = msi_reg_get_val_str( hkey, qualifier );
5557 sz = lstrlenW( advertise ) + 1;
5558 if (existing)
5560 for (p = existing; *p; p += len)
5562 len = lstrlenW( p ) + 1;
5563 if (wcscmp( advertise, p )) sz += len;
5566 if (!(output = malloc( (sz + 1) * sizeof(WCHAR) )))
5568 rc = ERROR_OUTOFMEMORY;
5569 goto end;
5571 q = output;
5572 if (existing)
5574 for (p = existing; *p; p += len)
5576 len = lstrlenW( p ) + 1;
5577 if (wcscmp( advertise, p ))
5579 memcpy( q, p, len * sizeof(WCHAR) );
5580 q += len;
5584 lstrcpyW( q, advertise );
5585 q[lstrlenW( q ) + 1] = 0;
5587 msi_reg_set_val_multi_str( hkey, qualifier, output );
5589 end:
5590 RegCloseKey(hkey);
5591 free( output );
5592 free( advertise );
5593 free( existing );
5595 /* the UI chunk */
5596 uirow = MSI_CreateRecord( 2 );
5597 MSI_RecordSetStringW( uirow, 1, compgroupid );
5598 MSI_RecordSetStringW( uirow, 2, qualifier);
5599 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5600 msiobj_release( &uirow->hdr );
5601 /* FIXME: call ui_progress? */
5603 return rc;
5607 * At present I am ignoring the advertised components part of this and only
5608 * focusing on the qualified component sets
5610 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5612 MSIQUERY *view;
5613 UINT rc;
5615 if (package->script == SCRIPT_NONE)
5616 return msi_schedule_action(package, SCRIPT_INSTALL, L"PublishComponents");
5618 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `PublishComponent`", &view);
5619 if (rc != ERROR_SUCCESS)
5620 return ERROR_SUCCESS;
5622 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5623 msiobj_release(&view->hdr);
5624 return rc;
5627 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5629 MSIPACKAGE *package = param;
5630 LPCWSTR compgroupid, component, feature, qualifier;
5631 MSICOMPONENT *comp;
5632 MSIFEATURE *feat;
5633 MSIRECORD *uirow;
5634 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5635 LONG res;
5637 feature = MSI_RecordGetString( rec, 5 );
5638 feat = msi_get_loaded_feature( package, feature );
5639 if (!feat)
5640 return ERROR_SUCCESS;
5642 feat->Action = msi_get_feature_action( package, feat );
5643 if (feat->Action != INSTALLSTATE_ABSENT)
5645 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5646 return ERROR_SUCCESS;
5649 component = MSI_RecordGetString( rec, 3 );
5650 comp = msi_get_loaded_component( package, component );
5651 if (!comp)
5652 return ERROR_SUCCESS;
5654 compgroupid = MSI_RecordGetString( rec, 1 );
5655 qualifier = MSI_RecordGetString( rec, 2 );
5657 squash_guid( compgroupid, squashed );
5658 lstrcpyW( keypath, L"Software\\Microsoft\\Installer\\Components\\" );
5659 lstrcatW( keypath, squashed );
5661 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5662 if (res != ERROR_SUCCESS)
5664 WARN( "unable to delete component key %ld\n", res );
5667 uirow = MSI_CreateRecord( 2 );
5668 MSI_RecordSetStringW( uirow, 1, compgroupid );
5669 MSI_RecordSetStringW( uirow, 2, qualifier );
5670 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5671 msiobj_release( &uirow->hdr );
5673 return ERROR_SUCCESS;
5676 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5678 MSIQUERY *view;
5679 UINT rc;
5681 if (package->script == SCRIPT_NONE)
5682 return msi_schedule_action(package, SCRIPT_INSTALL, L"UnpublishComponents");
5684 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `PublishComponent`", &view );
5685 if (rc != ERROR_SUCCESS)
5686 return ERROR_SUCCESS;
5688 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5689 msiobj_release( &view->hdr );
5690 return rc;
5693 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5695 MSIPACKAGE *package = param;
5696 MSICOMPONENT *component;
5697 MSIRECORD *row;
5698 MSIFILE *file;
5699 SC_HANDLE hscm = NULL, service = NULL;
5700 LPCWSTR comp, key;
5701 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5702 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5703 DWORD serv_type, start_type, err_control;
5704 BOOL is_vital;
5705 SERVICE_DESCRIPTIONW sd = {NULL};
5706 UINT ret = ERROR_SUCCESS;
5708 comp = MSI_RecordGetString( rec, 12 );
5709 component = msi_get_loaded_component( package, comp );
5710 if (!component)
5712 WARN("service component not found\n");
5713 goto done;
5715 component->Action = msi_get_component_action( package, component );
5716 if (component->Action != INSTALLSTATE_LOCAL)
5718 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5719 goto done;
5721 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5722 if (!hscm)
5724 ERR("Failed to open the SC Manager!\n");
5725 goto done;
5728 start_type = MSI_RecordGetInteger(rec, 5);
5729 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5730 goto done;
5732 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5733 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5734 serv_type = MSI_RecordGetInteger(rec, 4);
5735 err_control = MSI_RecordGetInteger(rec, 6);
5736 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5737 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5738 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5739 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5740 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5741 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5743 /* Should the complete install fail if CreateService fails? */
5744 is_vital = (err_control & msidbServiceInstallErrorControlVital);
5746 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
5747 CreateService (under Windows) would fail if not. */
5748 err_control &= ~msidbServiceInstallErrorControlVital;
5750 /* fetch the service path */
5751 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `Component` WHERE `Component` = '%s'", comp);
5752 if (!row)
5754 ERR("Query failed\n");
5755 goto done;
5757 if (!(key = MSI_RecordGetString(row, 6)))
5759 msiobj_release(&row->hdr);
5760 goto done;
5762 file = msi_get_loaded_file(package, key);
5763 msiobj_release(&row->hdr);
5764 if (!file)
5766 ERR("Failed to load the service file\n");
5767 goto done;
5770 if (!args || !args[0]) image_path = file->TargetPath;
5771 else
5773 int len = lstrlenW(file->TargetPath) + lstrlenW(args) + 2;
5774 if (!(image_path = malloc(len * sizeof(WCHAR))))
5776 ret = ERROR_OUTOFMEMORY;
5777 goto done;
5780 lstrcpyW(image_path, file->TargetPath);
5781 lstrcatW(image_path, L" ");
5782 lstrcatW(image_path, args);
5784 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5785 start_type, err_control, image_path, load_order,
5786 NULL, depends, serv_name, pass);
5788 if (!service)
5790 if (GetLastError() != ERROR_SERVICE_EXISTS)
5792 WARN( "failed to create service %s (%lu)\n", debugstr_w(name), GetLastError() );
5793 if (is_vital)
5794 ret = ERROR_INSTALL_FAILURE;
5798 else if (sd.lpDescription)
5800 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5801 WARN( "failed to set service description %lu\n", GetLastError() );
5804 if (image_path != file->TargetPath) free(image_path);
5805 done:
5806 if (service) CloseServiceHandle(service);
5807 if (hscm) CloseServiceHandle(hscm);
5808 free(name);
5809 free(disp);
5810 free(sd.lpDescription);
5811 free(load_order);
5812 free(serv_name);
5813 free(pass);
5814 free(depends);
5815 free(args);
5817 return ret;
5820 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5822 MSIQUERY *view;
5823 UINT rc;
5825 if (package->script == SCRIPT_NONE)
5826 return msi_schedule_action(package, SCRIPT_INSTALL, L"InstallServices");
5828 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ServiceInstall`", &view);
5829 if (rc != ERROR_SUCCESS)
5830 return ERROR_SUCCESS;
5832 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5833 msiobj_release(&view->hdr);
5834 return rc;
5837 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5838 static const WCHAR **service_args_to_vector(WCHAR *args, DWORD *numargs)
5840 LPCWSTR *vector, *temp_vector;
5841 LPWSTR p, q;
5842 DWORD sep_len;
5844 *numargs = 0;
5845 sep_len = ARRAY_SIZE(L"[~]") - 1;
5847 if (!args)
5848 return NULL;
5850 vector = malloc(sizeof(WCHAR *));
5851 if (!vector)
5852 return NULL;
5854 p = args;
5857 (*numargs)++;
5858 vector[*numargs - 1] = p;
5860 if ((q = wcsstr(p, L"[~]")))
5862 *q = '\0';
5864 temp_vector = realloc(vector, (*numargs + 1) * sizeof(WCHAR *));
5865 if (!temp_vector)
5867 free(vector);
5868 return NULL;
5870 vector = temp_vector;
5872 p = q + sep_len;
5874 } while (q);
5876 return vector;
5879 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5881 MSIPACKAGE *package = param;
5882 MSICOMPONENT *comp;
5883 MSIRECORD *uirow;
5884 SC_HANDLE scm = NULL, service = NULL;
5885 LPCWSTR component, *vector = NULL;
5886 LPWSTR name, args, display_name = NULL;
5887 DWORD event, numargs, len, wait, dummy;
5888 UINT r = ERROR_FUNCTION_FAILED;
5889 SERVICE_STATUS_PROCESS status;
5890 ULONGLONG start_time;
5892 component = MSI_RecordGetString(rec, 6);
5893 comp = msi_get_loaded_component(package, component);
5894 if (!comp)
5895 return ERROR_SUCCESS;
5897 event = MSI_RecordGetInteger( rec, 3 );
5898 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5900 comp->Action = msi_get_component_action( package, comp );
5901 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
5902 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
5904 TRACE("not starting %s\n", debugstr_w(name));
5905 free(name);
5906 return ERROR_SUCCESS;
5909 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5910 wait = MSI_RecordGetInteger(rec, 5);
5912 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5913 if (!scm)
5915 ERR("Failed to open the service control manager\n");
5916 goto done;
5919 len = 0;
5920 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5921 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5923 if ((display_name = malloc(++len * sizeof(WCHAR))))
5924 GetServiceDisplayNameW( scm, name, display_name, &len );
5927 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5928 if (!service)
5930 ERR( "failed to open service %s (%lu)\n", debugstr_w(name), GetLastError() );
5931 goto done;
5934 vector = service_args_to_vector(args, &numargs);
5936 if (!StartServiceW(service, numargs, vector) &&
5937 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5939 ERR( "failed to start service %s (%lu)\n", debugstr_w(name), GetLastError() );
5940 goto done;
5943 r = ERROR_SUCCESS;
5944 if (wait)
5946 /* wait for at most 30 seconds for the service to be up and running */
5947 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5948 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5950 TRACE( "failed to query service status (%lu)\n", GetLastError() );
5951 goto done;
5953 start_time = GetTickCount64();
5954 while (status.dwCurrentState == SERVICE_START_PENDING)
5956 if (GetTickCount64() - start_time > 30000) break;
5957 Sleep(1000);
5958 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5959 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5961 TRACE( "failed to query service status (%lu)\n", GetLastError() );
5962 goto done;
5965 if (status.dwCurrentState != SERVICE_RUNNING)
5967 WARN( "service failed to start %lu\n", status.dwCurrentState );
5968 r = ERROR_FUNCTION_FAILED;
5972 done:
5973 uirow = MSI_CreateRecord( 2 );
5974 MSI_RecordSetStringW( uirow, 1, display_name );
5975 MSI_RecordSetStringW( uirow, 2, name );
5976 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5977 msiobj_release( &uirow->hdr );
5979 if (service) CloseServiceHandle(service);
5980 if (scm) CloseServiceHandle(scm);
5982 free(name);
5983 free(args);
5984 free(vector);
5985 free(display_name);
5986 return r;
5989 static UINT ACTION_StartServices( MSIPACKAGE *package )
5991 MSIQUERY *view;
5992 UINT rc;
5994 if (package->script == SCRIPT_NONE)
5995 return msi_schedule_action(package, SCRIPT_INSTALL, L"StartServices");
5997 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ServiceControl`", &view);
5998 if (rc != ERROR_SUCCESS)
5999 return ERROR_SUCCESS;
6001 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6002 msiobj_release(&view->hdr);
6003 return rc;
6006 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6008 DWORD i, needed, count;
6009 ENUM_SERVICE_STATUSW *dependencies;
6010 SERVICE_STATUS ss;
6011 SC_HANDLE depserv;
6012 BOOL stopped, ret = FALSE;
6014 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6015 0, &needed, &count))
6016 return TRUE;
6018 if (GetLastError() != ERROR_MORE_DATA)
6019 return FALSE;
6021 dependencies = malloc(needed);
6022 if (!dependencies)
6023 return FALSE;
6025 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6026 needed, &needed, &count))
6027 goto done;
6029 for (i = 0; i < count; i++)
6031 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6032 SERVICE_STOP | SERVICE_QUERY_STATUS);
6033 if (!depserv)
6034 goto done;
6036 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6037 CloseServiceHandle(depserv);
6038 if (!stopped)
6039 goto done;
6042 ret = TRUE;
6044 done:
6045 free(dependencies);
6046 return ret;
6049 static UINT stop_service( LPCWSTR name )
6051 SC_HANDLE scm = NULL, service = NULL;
6052 SERVICE_STATUS status;
6053 SERVICE_STATUS_PROCESS ssp;
6054 DWORD needed;
6056 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6057 if (!scm)
6059 WARN( "failed to open the SCM (%lu)\n", GetLastError() );
6060 goto done;
6063 service = OpenServiceW(scm, name, SERVICE_STOP | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS);
6064 if (!service)
6066 WARN( "failed to open service %s (%lu)\n", debugstr_w(name), GetLastError() );
6067 goto done;
6070 if (!QueryServiceStatusEx( service, SC_STATUS_PROCESS_INFO, (BYTE *)&ssp, sizeof(ssp), &needed) )
6072 WARN( "failed to query service status %s (%lu)\n", debugstr_w(name), GetLastError() );
6073 goto done;
6076 if (ssp.dwCurrentState == SERVICE_STOPPED)
6077 goto done;
6079 stop_service_dependents(scm, service);
6081 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6082 WARN( "failed to stop service %s (%lu)\n", debugstr_w(name), GetLastError() );
6084 done:
6085 if (service) CloseServiceHandle(service);
6086 if (scm) CloseServiceHandle(scm);
6088 return ERROR_SUCCESS;
6091 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6093 MSIPACKAGE *package = param;
6094 MSICOMPONENT *comp;
6095 MSIRECORD *uirow;
6096 LPCWSTR component;
6097 WCHAR *name, *display_name = NULL;
6098 DWORD event, len;
6099 SC_HANDLE scm;
6101 component = MSI_RecordGetString( rec, 6 );
6102 comp = msi_get_loaded_component( package, component );
6103 if (!comp)
6104 return ERROR_SUCCESS;
6106 event = MSI_RecordGetInteger( rec, 3 );
6107 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6109 comp->Action = msi_get_component_action( package, comp );
6110 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6111 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6113 TRACE("not stopping %s\n", debugstr_w(name));
6114 free( name );
6115 return ERROR_SUCCESS;
6118 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6119 if (!scm)
6121 ERR("Failed to open the service control manager\n");
6122 goto done;
6125 len = 0;
6126 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6127 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6129 if ((display_name = malloc( ++len * sizeof(WCHAR ))))
6130 GetServiceDisplayNameW( scm, name, display_name, &len );
6132 CloseServiceHandle( scm );
6134 stop_service( name );
6136 done:
6137 uirow = MSI_CreateRecord( 2 );
6138 MSI_RecordSetStringW( uirow, 1, display_name );
6139 MSI_RecordSetStringW( uirow, 2, name );
6140 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6141 msiobj_release( &uirow->hdr );
6143 free( name );
6144 free( display_name );
6145 return ERROR_SUCCESS;
6148 static UINT ACTION_StopServices( MSIPACKAGE *package )
6150 MSIQUERY *view;
6151 UINT rc;
6153 if (package->script == SCRIPT_NONE)
6154 return msi_schedule_action(package, SCRIPT_INSTALL, L"StopServices");
6156 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ServiceControl`", &view);
6157 if (rc != ERROR_SUCCESS)
6158 return ERROR_SUCCESS;
6160 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6161 msiobj_release(&view->hdr);
6162 return rc;
6165 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6167 MSIPACKAGE *package = param;
6168 MSICOMPONENT *comp;
6169 MSIRECORD *uirow;
6170 LPWSTR name = NULL, display_name = NULL;
6171 DWORD event, len;
6172 SC_HANDLE scm = NULL, service = NULL;
6174 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6175 if (!comp)
6176 return ERROR_SUCCESS;
6178 event = MSI_RecordGetInteger( rec, 3 );
6179 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6181 comp->Action = msi_get_component_action( package, comp );
6182 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6183 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6185 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6186 free( name );
6187 return ERROR_SUCCESS;
6189 stop_service( name );
6191 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6192 if (!scm)
6194 WARN( "failed to open the SCM (%lu)\n", GetLastError() );
6195 goto done;
6198 len = 0;
6199 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6200 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6202 if ((display_name = malloc( ++len * sizeof(WCHAR ))))
6203 GetServiceDisplayNameW( scm, name, display_name, &len );
6206 service = OpenServiceW( scm, name, DELETE );
6207 if (!service)
6209 WARN( "failed to open service %s (%lu)\n", debugstr_w(name), GetLastError() );
6210 goto done;
6213 if (!DeleteService( service ))
6214 WARN( "failed to delete service %s (%lu)\n", debugstr_w(name), GetLastError() );
6216 done:
6217 uirow = MSI_CreateRecord( 2 );
6218 MSI_RecordSetStringW( uirow, 1, display_name );
6219 MSI_RecordSetStringW( uirow, 2, name );
6220 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6221 msiobj_release( &uirow->hdr );
6223 if (service) CloseServiceHandle( service );
6224 if (scm) CloseServiceHandle( scm );
6225 free( name );
6226 free( display_name );
6228 return ERROR_SUCCESS;
6231 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6233 MSIQUERY *view;
6234 UINT rc;
6236 if (package->script == SCRIPT_NONE)
6237 return msi_schedule_action(package, SCRIPT_INSTALL, L"DeleteServices");
6239 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ServiceControl`", &view );
6240 if (rc != ERROR_SUCCESS)
6241 return ERROR_SUCCESS;
6243 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6244 msiobj_release( &view->hdr );
6245 return rc;
6248 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6250 MSIPACKAGE *package = param;
6251 LPWSTR driver, driver_path, ptr;
6252 WCHAR outpath[MAX_PATH];
6253 MSIFILE *driver_file = NULL, *setup_file = NULL;
6254 MSICOMPONENT *comp;
6255 MSIRECORD *uirow;
6256 LPCWSTR desc, file_key, component;
6257 DWORD len, usage;
6258 UINT r = ERROR_SUCCESS;
6260 component = MSI_RecordGetString( rec, 2 );
6261 comp = msi_get_loaded_component( package, component );
6262 if (!comp)
6263 return ERROR_SUCCESS;
6265 comp->Action = msi_get_component_action( package, comp );
6266 if (comp->Action != INSTALLSTATE_LOCAL)
6268 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6269 return ERROR_SUCCESS;
6271 desc = MSI_RecordGetString(rec, 3);
6273 file_key = MSI_RecordGetString( rec, 4 );
6274 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6276 file_key = MSI_RecordGetString( rec, 5 );
6277 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6279 if (!driver_file)
6281 ERR("ODBC Driver entry not found!\n");
6282 return ERROR_FUNCTION_FAILED;
6285 len = lstrlenW(desc) + lstrlenW(L"Driver=%s") + lstrlenW(driver_file->FileName);
6286 if (setup_file)
6287 len += lstrlenW(L"Setup=%s") + lstrlenW(setup_file->FileName);
6288 len += lstrlenW(L"FileUsage=1") + 2; /* \0\0 */
6290 driver = malloc(len * sizeof(WCHAR));
6291 if (!driver)
6292 return ERROR_OUTOFMEMORY;
6294 ptr = driver;
6295 lstrcpyW(ptr, desc);
6296 ptr += lstrlenW(ptr) + 1;
6298 len = swprintf(ptr, len - (ptr - driver), L"Driver=%s", driver_file->FileName);
6299 ptr += len + 1;
6301 if (setup_file)
6303 len = swprintf(ptr, len - (ptr - driver), L"Setup=%s", setup_file->FileName);
6304 ptr += len + 1;
6307 lstrcpyW(ptr, L"FileUsage=1");
6308 ptr += lstrlenW(ptr) + 1;
6309 *ptr = '\0';
6311 if (!driver_file->TargetPath)
6313 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6314 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6316 driver_path = wcsdup(driver_file->TargetPath);
6317 ptr = wcsrchr(driver_path, '\\');
6318 if (ptr) *ptr = '\0';
6320 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6321 NULL, ODBC_INSTALL_COMPLETE, &usage))
6323 ERR("Failed to install SQL driver!\n");
6324 r = ERROR_FUNCTION_FAILED;
6327 uirow = MSI_CreateRecord( 5 );
6328 MSI_RecordSetStringW( uirow, 1, desc );
6329 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6330 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6331 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6332 msiobj_release( &uirow->hdr );
6334 free(driver);
6335 free(driver_path);
6337 return r;
6340 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6342 MSIPACKAGE *package = param;
6343 LPWSTR translator, translator_path, ptr;
6344 WCHAR outpath[MAX_PATH];
6345 MSIFILE *translator_file = NULL, *setup_file = NULL;
6346 MSICOMPONENT *comp;
6347 MSIRECORD *uirow;
6348 LPCWSTR desc, file_key, component;
6349 DWORD len, usage;
6350 UINT r = ERROR_SUCCESS;
6352 component = MSI_RecordGetString( rec, 2 );
6353 comp = msi_get_loaded_component( package, component );
6354 if (!comp)
6355 return ERROR_SUCCESS;
6357 comp->Action = msi_get_component_action( package, comp );
6358 if (comp->Action != INSTALLSTATE_LOCAL)
6360 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6361 return ERROR_SUCCESS;
6363 desc = MSI_RecordGetString(rec, 3);
6365 file_key = MSI_RecordGetString( rec, 4 );
6366 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6368 file_key = MSI_RecordGetString( rec, 5 );
6369 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6371 if (!translator_file)
6373 ERR("ODBC Translator entry not found!\n");
6374 return ERROR_FUNCTION_FAILED;
6377 len = lstrlenW(desc) + lstrlenW(L"Translator=%s") + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6378 if (setup_file)
6379 len += lstrlenW(L"Setup=%s") + lstrlenW(setup_file->FileName);
6381 translator = malloc(len * sizeof(WCHAR));
6382 if (!translator)
6383 return ERROR_OUTOFMEMORY;
6385 ptr = translator;
6386 lstrcpyW(ptr, desc);
6387 ptr += lstrlenW(ptr) + 1;
6389 len = swprintf(ptr, len - (ptr - translator), L"Translator=%s", translator_file->FileName);
6390 ptr += len + 1;
6392 if (setup_file)
6394 len = swprintf(ptr, len - (ptr - translator), L"Setup=%s", setup_file->FileName);
6395 ptr += len + 1;
6397 *ptr = '\0';
6399 translator_path = wcsdup(translator_file->TargetPath);
6400 ptr = wcsrchr(translator_path, '\\');
6401 if (ptr) *ptr = '\0';
6403 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6404 NULL, ODBC_INSTALL_COMPLETE, &usage))
6406 ERR("Failed to install SQL translator!\n");
6407 r = ERROR_FUNCTION_FAILED;
6410 uirow = MSI_CreateRecord( 5 );
6411 MSI_RecordSetStringW( uirow, 1, desc );
6412 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6413 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6414 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6415 msiobj_release( &uirow->hdr );
6417 free(translator);
6418 free(translator_path);
6420 return r;
6423 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6425 MSIPACKAGE *package = param;
6426 MSICOMPONENT *comp;
6427 LPWSTR attrs;
6428 LPCWSTR desc, driver, component;
6429 WORD request = ODBC_ADD_SYS_DSN;
6430 INT registration;
6431 DWORD len;
6432 UINT r = ERROR_SUCCESS;
6433 MSIRECORD *uirow;
6435 component = MSI_RecordGetString( rec, 2 );
6436 comp = msi_get_loaded_component( package, component );
6437 if (!comp)
6438 return ERROR_SUCCESS;
6440 comp->Action = msi_get_component_action( package, comp );
6441 if (comp->Action != INSTALLSTATE_LOCAL)
6443 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6444 return ERROR_SUCCESS;
6447 desc = MSI_RecordGetString(rec, 3);
6448 driver = MSI_RecordGetString(rec, 4);
6449 registration = MSI_RecordGetInteger(rec, 5);
6451 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6452 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6454 len = lstrlenW(L"DSN=%s") + lstrlenW(desc) + 2; /* \0\0 */
6455 attrs = malloc(len * sizeof(WCHAR));
6456 if (!attrs)
6457 return ERROR_OUTOFMEMORY;
6459 len = swprintf(attrs, len, L"DSN=%s", desc);
6460 attrs[len + 1] = 0;
6462 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6463 WARN("Failed to install SQL data source!\n");
6465 uirow = MSI_CreateRecord( 5 );
6466 MSI_RecordSetStringW( uirow, 1, desc );
6467 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6468 MSI_RecordSetInteger( uirow, 3, request );
6469 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6470 msiobj_release( &uirow->hdr );
6472 free(attrs);
6474 return r;
6477 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6479 MSIQUERY *view;
6480 UINT rc;
6482 if (package->script == SCRIPT_NONE)
6483 return msi_schedule_action(package, SCRIPT_INSTALL, L"InstallODBC");
6485 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ODBCDriver`", &view);
6486 if (rc == ERROR_SUCCESS)
6488 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6489 msiobj_release(&view->hdr);
6490 if (rc != ERROR_SUCCESS)
6491 return rc;
6493 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ODBCTranslator`", &view);
6494 if (rc == ERROR_SUCCESS)
6496 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6497 msiobj_release(&view->hdr);
6498 if (rc != ERROR_SUCCESS)
6499 return rc;
6501 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ODBCDataSource`", &view);
6502 if (rc == ERROR_SUCCESS)
6504 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6505 msiobj_release(&view->hdr);
6506 if (rc != ERROR_SUCCESS)
6507 return rc;
6509 return ERROR_SUCCESS;
6512 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6514 MSIPACKAGE *package = param;
6515 MSICOMPONENT *comp;
6516 MSIRECORD *uirow;
6517 DWORD usage;
6518 LPCWSTR desc, component;
6520 component = MSI_RecordGetString( rec, 2 );
6521 comp = msi_get_loaded_component( package, component );
6522 if (!comp)
6523 return ERROR_SUCCESS;
6525 comp->Action = msi_get_component_action( package, comp );
6526 if (comp->Action != INSTALLSTATE_ABSENT)
6528 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6529 return ERROR_SUCCESS;
6532 desc = MSI_RecordGetString( rec, 3 );
6533 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6535 WARN("Failed to remove ODBC driver\n");
6537 else if (!usage)
6539 FIXME("Usage count reached 0\n");
6542 uirow = MSI_CreateRecord( 2 );
6543 MSI_RecordSetStringW( uirow, 1, desc );
6544 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6545 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6546 msiobj_release( &uirow->hdr );
6548 return ERROR_SUCCESS;
6551 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6553 MSIPACKAGE *package = param;
6554 MSICOMPONENT *comp;
6555 MSIRECORD *uirow;
6556 DWORD usage;
6557 LPCWSTR desc, component;
6559 component = MSI_RecordGetString( rec, 2 );
6560 comp = msi_get_loaded_component( package, component );
6561 if (!comp)
6562 return ERROR_SUCCESS;
6564 comp->Action = msi_get_component_action( package, comp );
6565 if (comp->Action != INSTALLSTATE_ABSENT)
6567 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6568 return ERROR_SUCCESS;
6571 desc = MSI_RecordGetString( rec, 3 );
6572 if (!SQLRemoveTranslatorW( desc, &usage ))
6574 WARN("Failed to remove ODBC translator\n");
6576 else if (!usage)
6578 FIXME("Usage count reached 0\n");
6581 uirow = MSI_CreateRecord( 2 );
6582 MSI_RecordSetStringW( uirow, 1, desc );
6583 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6584 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6585 msiobj_release( &uirow->hdr );
6587 return ERROR_SUCCESS;
6590 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6592 MSIPACKAGE *package = param;
6593 MSICOMPONENT *comp;
6594 MSIRECORD *uirow;
6595 LPWSTR attrs;
6596 LPCWSTR desc, driver, component;
6597 WORD request = ODBC_REMOVE_SYS_DSN;
6598 INT registration;
6599 DWORD len;
6601 component = MSI_RecordGetString( rec, 2 );
6602 comp = msi_get_loaded_component( package, component );
6603 if (!comp)
6604 return ERROR_SUCCESS;
6606 comp->Action = msi_get_component_action( package, comp );
6607 if (comp->Action != INSTALLSTATE_ABSENT)
6609 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6610 return ERROR_SUCCESS;
6613 desc = MSI_RecordGetString( rec, 3 );
6614 driver = MSI_RecordGetString( rec, 4 );
6615 registration = MSI_RecordGetInteger( rec, 5 );
6617 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6618 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6620 len = lstrlenW( L"DSN=%s" ) + lstrlenW( desc ) + 2; /* \0\0 */
6621 attrs = malloc( len * sizeof(WCHAR) );
6622 if (!attrs)
6623 return ERROR_OUTOFMEMORY;
6625 FIXME("Use ODBCSourceAttribute table\n");
6627 len = swprintf( attrs, len, L"DSN=%s", desc );
6628 attrs[len + 1] = 0;
6630 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6632 WARN("Failed to remove ODBC data source\n");
6634 free( attrs );
6636 uirow = MSI_CreateRecord( 3 );
6637 MSI_RecordSetStringW( uirow, 1, desc );
6638 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6639 MSI_RecordSetInteger( uirow, 3, request );
6640 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6641 msiobj_release( &uirow->hdr );
6643 return ERROR_SUCCESS;
6646 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6648 MSIQUERY *view;
6649 UINT rc;
6651 if (package->script == SCRIPT_NONE)
6652 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveODBC");
6654 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCDriver`", &view );
6655 if (rc == ERROR_SUCCESS)
6657 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6658 msiobj_release( &view->hdr );
6659 if (rc != ERROR_SUCCESS)
6660 return rc;
6662 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCTranslator`", &view );
6663 if (rc == ERROR_SUCCESS)
6665 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6666 msiobj_release( &view->hdr );
6667 if (rc != ERROR_SUCCESS)
6668 return rc;
6670 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCDataSource`", &view );
6671 if (rc == ERROR_SUCCESS)
6673 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6674 msiobj_release( &view->hdr );
6675 if (rc != ERROR_SUCCESS)
6676 return rc;
6678 return ERROR_SUCCESS;
6681 #define ENV_ACT_SETALWAYS 0x1
6682 #define ENV_ACT_SETABSENT 0x2
6683 #define ENV_ACT_REMOVE 0x4
6684 #define ENV_ACT_REMOVEMATCH 0x8
6686 #define ENV_MOD_MACHINE 0x20000000
6687 #define ENV_MOD_APPEND 0x40000000
6688 #define ENV_MOD_PREFIX 0x80000000
6689 #define ENV_MOD_MASK 0xC0000000
6691 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6693 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6695 const WCHAR *cptr = *name;
6697 *flags = 0;
6698 while (*cptr)
6700 if (*cptr == '=')
6701 *flags |= ENV_ACT_SETALWAYS;
6702 else if (*cptr == '+')
6703 *flags |= ENV_ACT_SETABSENT;
6704 else if (*cptr == '-')
6705 *flags |= ENV_ACT_REMOVE;
6706 else if (*cptr == '!')
6707 *flags |= ENV_ACT_REMOVEMATCH;
6708 else if (*cptr == '*')
6709 *flags |= ENV_MOD_MACHINE | ENV_ACT_REMOVE;
6710 else
6711 break;
6713 cptr++;
6714 (*name)++;
6717 if (!*cptr)
6719 ERR("Missing environment variable\n");
6720 return ERROR_FUNCTION_FAILED;
6723 if (*value)
6725 LPCWSTR ptr = *value;
6726 if (!wcsncmp(ptr, L"[~]", 3))
6728 if (ptr[3] == ';')
6730 *flags |= ENV_MOD_APPEND;
6731 *value += 3;
6733 else
6735 *value = NULL;
6738 else if (lstrlenW(*value) >= 3)
6740 ptr += lstrlenW(ptr) - 3;
6741 if (!wcscmp( ptr, L"[~]" ))
6743 if ((ptr-1) > *value && *(ptr-1) == ';')
6745 *flags |= ENV_MOD_PREFIX;
6746 /* the "[~]" will be removed by deformat_string */;
6748 else
6750 *value = NULL;
6756 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6757 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6758 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6759 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6761 ERR( "invalid flags: %#lx\n", *flags );
6762 return ERROR_FUNCTION_FAILED;
6765 if (!*flags)
6766 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6768 return ERROR_SUCCESS;
6771 static UINT open_env_key( DWORD flags, HKEY *key )
6773 const WCHAR *env;
6774 HKEY root;
6775 LONG res;
6777 if (flags & ENV_MOD_MACHINE)
6779 env = L"System\\CurrentControlSet\\Control\\Session Manager\\Environment";
6780 root = HKEY_LOCAL_MACHINE;
6782 else
6784 env = L"Environment";
6785 root = HKEY_CURRENT_USER;
6788 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6789 if (res != ERROR_SUCCESS)
6791 WARN( "failed to open key %s (%ld)\n", debugstr_w(env), res );
6792 return ERROR_FUNCTION_FAILED;
6795 return ERROR_SUCCESS;
6798 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6800 MSIPACKAGE *package = param;
6801 LPCWSTR name, value, component;
6802 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
6803 DWORD flags, type, size, len, len_value = 0;
6804 UINT res;
6805 HKEY env = NULL;
6806 MSICOMPONENT *comp;
6807 MSIRECORD *uirow;
6808 int action = 0, found = 0;
6810 component = MSI_RecordGetString(rec, 4);
6811 comp = msi_get_loaded_component(package, component);
6812 if (!comp)
6813 return ERROR_SUCCESS;
6815 comp->Action = msi_get_component_action( package, comp );
6816 if (comp->Action != INSTALLSTATE_LOCAL)
6818 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6819 return ERROR_SUCCESS;
6821 name = MSI_RecordGetString(rec, 2);
6822 value = MSI_RecordGetString(rec, 3);
6824 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6826 res = env_parse_flags(&name, &value, &flags);
6827 if (res != ERROR_SUCCESS || !value)
6828 goto done;
6830 if (value)
6832 DWORD len = deformat_string( package, value, &deformatted );
6833 if (!deformatted)
6835 res = ERROR_OUTOFMEMORY;
6836 goto done;
6839 if (len)
6841 value = deformatted;
6842 if (flags & ENV_MOD_PREFIX)
6844 p = wcsrchr( value, ';' );
6845 len_value = p - value;
6847 else if (flags & ENV_MOD_APPEND)
6849 value = wcschr( value, ';' ) + 1;
6850 len_value = lstrlenW( value );
6852 else len_value = lstrlenW( value );
6854 else
6856 value = NULL;
6860 res = open_env_key( flags, &env );
6861 if (res != ERROR_SUCCESS)
6862 goto done;
6864 if (flags & ENV_MOD_MACHINE)
6865 action |= 0x20000000;
6867 size = 0;
6868 type = REG_SZ;
6869 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6870 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6871 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6872 goto done;
6874 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6876 action = 0x2;
6878 /* Nothing to do. */
6879 if (!value)
6881 res = ERROR_SUCCESS;
6882 goto done;
6884 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6885 newval = wcsdup(value);
6886 if (!newval)
6888 res = ERROR_OUTOFMEMORY;
6889 goto done;
6892 else
6894 action = 0x1;
6896 /* Contrary to MSDN, +-variable to [~];path works */
6897 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6899 res = ERROR_SUCCESS;
6900 goto done;
6903 if (!(p = q = data = malloc( size )))
6905 free(deformatted);
6906 RegCloseKey(env);
6907 return ERROR_OUTOFMEMORY;
6910 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
6911 if (res != ERROR_SUCCESS)
6912 goto done;
6914 if (flags & ENV_ACT_REMOVEMATCH && (!value || !wcscmp( data, value )))
6916 action = 0x4;
6917 res = RegDeleteValueW(env, name);
6918 if (res != ERROR_SUCCESS)
6919 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6920 goto done;
6923 for (;;)
6925 while (*q && *q != ';') q++;
6926 len = q - p;
6927 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
6928 (!p[len] || p[len] == ';'))
6930 found = 1;
6931 break;
6933 if (!*q) break;
6934 p = ++q;
6937 if (found)
6939 TRACE("string already set\n");
6940 goto done;
6943 size = (len_value + 1 + lstrlenW( data ) + 1) * sizeof(WCHAR);
6944 if (!(p = newval = malloc( size )))
6946 res = ERROR_OUTOFMEMORY;
6947 goto done;
6950 if (flags & ENV_MOD_PREFIX)
6952 memcpy( newval, value, len_value * sizeof(WCHAR) );
6953 newval[len_value] = ';';
6954 p = newval + len_value + 1;
6955 action |= 0x80000000;
6958 lstrcpyW( p, data );
6960 if (flags & ENV_MOD_APPEND)
6962 p += lstrlenW( data );
6963 *p++ = ';';
6964 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
6965 action |= 0x40000000;
6968 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6969 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
6970 if (res)
6972 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6975 done:
6976 uirow = MSI_CreateRecord( 3 );
6977 MSI_RecordSetStringW( uirow, 1, name );
6978 MSI_RecordSetStringW( uirow, 2, newval );
6979 MSI_RecordSetInteger( uirow, 3, action );
6980 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6981 msiobj_release( &uirow->hdr );
6983 if (env) RegCloseKey(env);
6984 free(deformatted);
6985 free(data);
6986 free(newval);
6987 return res;
6990 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6992 MSIQUERY *view;
6993 UINT rc;
6995 if (package->script == SCRIPT_NONE)
6996 return msi_schedule_action(package, SCRIPT_INSTALL, L"WriteEnvironmentStrings");
6998 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Environment`", &view);
6999 if (rc != ERROR_SUCCESS)
7000 return ERROR_SUCCESS;
7002 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7003 msiobj_release(&view->hdr);
7004 return rc;
7007 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7009 MSIPACKAGE *package = param;
7010 LPCWSTR name, value, component;
7011 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7012 DWORD flags, type, size, len, len_value = 0, len_new_value;
7013 HKEY env = NULL;
7014 MSICOMPONENT *comp;
7015 MSIRECORD *uirow;
7016 int action = 0;
7017 LONG res;
7018 UINT r;
7020 component = MSI_RecordGetString( rec, 4 );
7021 comp = msi_get_loaded_component( package, component );
7022 if (!comp)
7023 return ERROR_SUCCESS;
7025 comp->Action = msi_get_component_action( package, comp );
7026 if (comp->Action != INSTALLSTATE_ABSENT)
7028 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7029 return ERROR_SUCCESS;
7031 name = MSI_RecordGetString( rec, 2 );
7032 value = MSI_RecordGetString( rec, 3 );
7034 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7036 r = env_parse_flags( &name, &value, &flags );
7037 if (r != ERROR_SUCCESS)
7038 return r;
7040 if (!(flags & ENV_ACT_REMOVE))
7042 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7043 return ERROR_SUCCESS;
7046 if (value)
7048 DWORD len = deformat_string( package, value, &deformatted );
7049 if (!deformatted)
7051 res = ERROR_OUTOFMEMORY;
7052 goto done;
7055 if (len)
7057 value = deformatted;
7058 if (flags & ENV_MOD_PREFIX)
7060 p = wcsrchr( value, ';' );
7061 len_value = p - value;
7063 else if (flags & ENV_MOD_APPEND)
7065 value = wcschr( value, ';' ) + 1;
7066 len_value = lstrlenW( value );
7068 else len_value = lstrlenW( value );
7070 else
7072 value = NULL;
7076 r = open_env_key( flags, &env );
7077 if (r != ERROR_SUCCESS)
7079 r = ERROR_SUCCESS;
7080 goto done;
7083 if (flags & ENV_MOD_MACHINE)
7084 action |= 0x20000000;
7086 size = 0;
7087 type = REG_SZ;
7088 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7089 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7090 goto done;
7092 if (!(new_value = malloc( size ))) goto done;
7094 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7095 if (res != ERROR_SUCCESS)
7096 goto done;
7098 len_new_value = size / sizeof(WCHAR) - 1;
7099 p = q = new_value;
7100 for (;;)
7102 while (*q && *q != ';') q++;
7103 len = q - p;
7104 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7106 if (*q == ';') q++;
7107 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7108 break;
7110 if (!*q) break;
7111 p = ++q;
7114 if (!new_value[0] || !value)
7116 TRACE("removing %s\n", debugstr_w(name));
7117 res = RegDeleteValueW( env, name );
7118 if (res != ERROR_SUCCESS)
7119 WARN( "failed to delete value %s (%ld)\n", debugstr_w(name), res );
7121 else
7123 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7124 size = (lstrlenW( new_value ) + 1) * sizeof(WCHAR);
7125 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7126 if (res != ERROR_SUCCESS)
7127 WARN( "failed to set %s to %s (%ld)\n", debugstr_w(name), debugstr_w(new_value), res );
7130 done:
7131 uirow = MSI_CreateRecord( 3 );
7132 MSI_RecordSetStringW( uirow, 1, name );
7133 MSI_RecordSetStringW( uirow, 2, value );
7134 MSI_RecordSetInteger( uirow, 3, action );
7135 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7136 msiobj_release( &uirow->hdr );
7138 if (env) RegCloseKey( env );
7139 free( deformatted );
7140 free( new_value );
7141 return r;
7144 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7146 MSIQUERY *view;
7147 UINT rc;
7149 if (package->script == SCRIPT_NONE)
7150 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveEnvironmentStrings");
7152 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Environment`", &view );
7153 if (rc != ERROR_SUCCESS)
7154 return ERROR_SUCCESS;
7156 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7157 msiobj_release( &view->hdr );
7158 return rc;
7161 UINT msi_validate_product_id( MSIPACKAGE *package )
7163 LPWSTR key, template, id;
7164 UINT r = ERROR_SUCCESS;
7166 id = msi_dup_property( package->db, L"ProductID" );
7167 if (id)
7169 free( id );
7170 return ERROR_SUCCESS;
7172 template = msi_dup_property( package->db, L"PIDTemplate" );
7173 key = msi_dup_property( package->db, L"PIDKEY" );
7174 if (key && template)
7176 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7177 r = msi_set_property( package->db, L"ProductID", key, -1 );
7179 free( template );
7180 free( key );
7181 return r;
7184 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7186 return msi_validate_product_id( package );
7189 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7191 TRACE("\n");
7192 package->need_reboot_at_end = 1;
7193 return ERROR_SUCCESS;
7196 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7198 MSIRECORD *uirow;
7199 int space = msi_get_property_int( package->db, L"AVAILABLEFREEREG", 0 );
7201 TRACE("%p %d kilobytes\n", package, space);
7203 uirow = MSI_CreateRecord( 1 );
7204 MSI_RecordSetInteger( uirow, 1, space );
7205 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7206 msiobj_release( &uirow->hdr );
7208 return ERROR_SUCCESS;
7211 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7213 TRACE("%p\n", package);
7215 msi_set_property( package->db, L"RollbackDisabled", L"1", -1 );
7216 return ERROR_SUCCESS;
7219 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7221 FIXME("%p\n", package);
7222 return ERROR_SUCCESS;
7225 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7227 MSIQUERY *view;
7228 UINT r;
7229 DWORD count;
7231 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCDriver`", &view );
7232 if (r == ERROR_SUCCESS)
7234 count = 0;
7235 r = MSI_IterateRecords( view, &count, NULL, package );
7236 msiobj_release( &view->hdr );
7237 if (r != ERROR_SUCCESS)
7238 return r;
7239 if (count) FIXME( "ignored %lu rows in ODBCDriver table\n", count );
7241 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCTranslator`", &view );
7242 if (r == ERROR_SUCCESS)
7244 count = 0;
7245 r = MSI_IterateRecords( view, &count, NULL, package );
7246 msiobj_release( &view->hdr );
7247 if (r != ERROR_SUCCESS)
7248 return r;
7249 if (count) FIXME( "ignored %lu rows in ODBCTranslator table\n", count );
7251 return ERROR_SUCCESS;
7254 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7256 MSIPACKAGE *package = param;
7257 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7258 int attrs = MSI_RecordGetInteger( rec, 5 );
7259 UINT len = ARRAY_SIZE( L"msiexec /qn /i %s REMOVE=%s" );
7260 WCHAR *product, *features, *cmd;
7261 STARTUPINFOW si;
7262 PROCESS_INFORMATION info;
7263 BOOL ret;
7265 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7266 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7268 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7270 len += lstrlenW( product );
7271 if (features)
7272 len += lstrlenW( features );
7273 else
7274 len += ARRAY_SIZE( L"ALL" );
7276 if (!(cmd = malloc( len * sizeof(WCHAR) )))
7278 free( product );
7279 free( features );
7280 return ERROR_OUTOFMEMORY;
7282 swprintf( cmd, len, L"msiexec /qn /i %s REMOVE=%s", product, features ? features : L"ALL" );
7283 free( product );
7284 free( features );
7286 memset( &si, 0, sizeof(STARTUPINFOW) );
7287 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7288 free( cmd );
7289 if (!ret) return GetLastError();
7290 CloseHandle( info.hThread );
7292 WaitForSingleObject( info.hProcess, INFINITE );
7293 CloseHandle( info.hProcess );
7294 return ERROR_SUCCESS;
7297 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7299 MSIQUERY *view;
7300 UINT r;
7302 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Upgrade`", &view );
7303 if (r == ERROR_SUCCESS)
7305 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7306 msiobj_release( &view->hdr );
7307 if (r != ERROR_SUCCESS)
7308 return r;
7310 return ERROR_SUCCESS;
7313 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7315 MSIPACKAGE *package = param;
7316 int attributes = MSI_RecordGetInteger( rec, 5 );
7318 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7320 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7321 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7322 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7323 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7324 HKEY hkey;
7325 UINT r;
7327 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7329 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7330 if (r != ERROR_SUCCESS)
7331 return ERROR_SUCCESS;
7333 else
7335 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7336 if (r != ERROR_SUCCESS)
7337 return ERROR_SUCCESS;
7339 RegCloseKey( hkey );
7341 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7342 debugstr_w(upgrade_code), debugstr_w(version_min),
7343 debugstr_w(version_max), debugstr_w(language));
7345 return ERROR_SUCCESS;
7348 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7350 MSIQUERY *view;
7351 UINT r;
7353 if (msi_get_property_int( package->db, L"Installed", 0 ))
7355 TRACE("product is installed, skipping action\n");
7356 return ERROR_SUCCESS;
7358 if (msi_get_property_int( package->db, L"Preselected", 0 ))
7360 TRACE("Preselected property is set, not migrating feature states\n");
7361 return ERROR_SUCCESS;
7363 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Upgrade`", &view );
7364 if (r == ERROR_SUCCESS)
7366 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7367 msiobj_release( &view->hdr );
7368 if (r != ERROR_SUCCESS)
7369 return r;
7371 return ERROR_SUCCESS;
7374 static void bind_image( MSIPACKAGE *package, const char *filename, const char *path )
7376 msi_disable_fs_redirection( package );
7377 if (!BindImage( filename, path, NULL )) WARN( "failed to bind image %lu\n", GetLastError() );
7378 msi_revert_fs_redirection( package );
7381 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7383 UINT i;
7384 MSIFILE *file;
7385 MSIPACKAGE *package = param;
7386 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7387 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7388 char *filenameA, *pathA;
7389 WCHAR *pathW, **path_list;
7391 if (!(file = msi_get_loaded_file( package, key )))
7393 WARN("file %s not found\n", debugstr_w(key));
7394 return ERROR_SUCCESS;
7396 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7398 path_list = msi_split_string( paths, ';' );
7399 if (!path_list) bind_image( package, filenameA, NULL );
7400 else
7402 for (i = 0; path_list[i] && path_list[i][0]; i++)
7404 deformat_string( package, path_list[i], &pathW );
7405 if ((pathA = strdupWtoA( pathW )))
7407 bind_image( package, filenameA, pathA );
7408 free( pathA );
7410 free( pathW );
7413 free( path_list );
7414 free( filenameA );
7416 return ERROR_SUCCESS;
7419 static UINT ACTION_BindImage( MSIPACKAGE *package )
7421 MSIQUERY *view;
7422 UINT r;
7424 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `BindImage`", &view );
7425 if (r == ERROR_SUCCESS)
7427 MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7428 msiobj_release( &view->hdr );
7430 return ERROR_SUCCESS;
7433 static UINT unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7435 MSIQUERY *view;
7436 DWORD count = 0;
7437 UINT r;
7439 r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `%s`", table );
7440 if (r == ERROR_SUCCESS)
7442 r = MSI_IterateRecords(view, &count, NULL, package);
7443 msiobj_release(&view->hdr);
7444 if (r != ERROR_SUCCESS)
7445 return r;
7447 if (count) FIXME( "%s: ignored %lu rows from %s\n", action, count, debugstr_w(table) );
7448 return ERROR_SUCCESS;
7451 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7453 return unimplemented_action_stub( package, "IsolateComponents", L"IsolateComponent" );
7456 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7458 return unimplemented_action_stub( package, "RMCCPSearch", L"CCPSearch" );
7461 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7463 return unimplemented_action_stub( package, "RegisterComPlus", L"Complus" );
7466 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7468 return unimplemented_action_stub( package, "UnregisterComPlus", L"Complus" );
7471 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7473 return unimplemented_action_stub( package, "InstallSFPCatalogFile", L"SFPCatalog" );
7476 static const struct
7478 const WCHAR *action;
7479 const UINT description;
7480 const UINT template;
7481 UINT (*handler)(MSIPACKAGE *);
7482 const WCHAR *action_rollback;
7484 StandardActions[] =
7486 { L"AllocateRegistrySpace", IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7487 { L"AppSearch", IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7488 { L"BindImage", IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7489 { L"CCPSearch", IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7490 { L"CostFinalize", IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7491 { L"CostInitialize", IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7492 { L"CreateFolders", IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, L"RemoveFolders" },
7493 { L"CreateShortcuts", IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, L"RemoveShortcuts" },
7494 { L"DeleteServices", IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, L"InstallServices" },
7495 { L"DisableRollback", 0, 0, ACTION_DisableRollback, NULL },
7496 { L"DuplicateFiles", IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, L"RemoveDuplicateFiles" },
7497 { L"ExecuteAction", 0, 0, ACTION_ExecuteAction, NULL },
7498 { L"FileCost", IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7499 { L"FindRelatedProducts", IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7500 { L"ForceReboot", 0, 0, ACTION_ForceReboot, NULL },
7501 { L"InstallAdminPackage", IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7502 { L"InstallExecute", 0, 0, ACTION_InstallExecute, NULL },
7503 { L"InstallExecuteAgain", 0, 0, ACTION_InstallExecute, NULL },
7504 { L"InstallFiles", IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, L"RemoveFiles" },
7505 { L"InstallFinalize", 0, 0, ACTION_InstallFinalize, NULL },
7506 { L"InstallInitialize", 0, 0, ACTION_InstallInitialize, NULL },
7507 { L"InstallODBC", IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, L"RemoveODBC" },
7508 { L"InstallServices", IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, L"DeleteServices" },
7509 { L"InstallSFPCatalogFile", IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7510 { L"InstallValidate", IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7511 { L"IsolateComponents", 0, 0, ACTION_IsolateComponents, NULL },
7512 { L"LaunchConditions", IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7513 { L"MigrateFeutureStates", IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7514 { L"MoveFiles", IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7515 { L"MsiPublishAssemblies", IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, L"MsiUnpublishAssemblies" },
7516 { L"MsiUnpublishAssemblies", IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, L"MsiPublishAssemblies" },
7517 { L"PatchFiles", IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7518 { L"ProcessComponents", IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, L"ProcessComponents" },
7519 { L"PublishComponents", IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, L"UnpublishComponents" },
7520 { L"PublishFeatures", IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, L"UnpublishFeatures" },
7521 { L"PublishProduct", IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, L"UnpublishProduct" },
7522 { L"RegisterClassInfo", IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, L"UnregisterClassInfo" },
7523 { L"RegisterComPlus", IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, L"UnregisterComPlus" },
7524 { L"RegisterExtensionInfo", IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, L"UnregisterExtensionInfo" },
7525 { L"RegisterFonts", IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, L"UnregisterFonts" },
7526 { L"RegisterMIMEInfo", IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, L"UnregisterMIMEInfo" },
7527 { L"RegisterProduct", IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
7528 { L"RegisterProgIdInfo", IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, L"UnregisterProgIdInfo" },
7529 { L"RegisterTypeLibraries", IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, L"UnregisterTypeLibraries" },
7530 { L"RegisterUser", IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
7531 { L"RemoveDuplicateFiles", IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, L"DuplicateFiles" },
7532 { L"RemoveEnvironmentStrings", IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, L"WriteEnvironmentStrings" },
7533 { L"RemoveExistingProducts", IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
7534 { L"RemoveFiles", IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, L"InstallFiles" },
7535 { L"RemoveFolders", IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, L"CreateFolders" },
7536 { L"RemoveIniValues", IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, L"WriteIniValues" },
7537 { L"RemoveODBC", IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, L"InstallODBC" },
7538 { L"RemoveRegistryValues", IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, L"WriteRegistryValues" },
7539 { L"RemoveShortcuts", IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, L"CreateShortcuts" },
7540 { L"ResolveSource", 0, 0, ACTION_ResolveSource, NULL },
7541 { L"RMCCPSearch", IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
7542 { L"ScheduleReboot", 0, 0, ACTION_ScheduleReboot, NULL },
7543 { L"SelfRegModules", IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, L"SelfUnregModules" },
7544 { L"SelfUnregModules", IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, L"SelfRegModules" },
7545 { L"SetODBCFolders", IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
7546 { L"StartServices", IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, L"StopServices" },
7547 { L"StopServices", IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, L"StartServices" },
7548 { L"UnpublishComponents", IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, L"PublishComponents" },
7549 { L"UnpublishFeatures", IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, L"PublishFeatures" },
7550 { L"UnpublishProduct", IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7551 { L"UnregisterClassInfo", IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, L"RegisterClassInfo" },
7552 { L"UnregisterComPlus", IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, L"RegisterComPlus" },
7553 { L"UnregisterExtensionInfo", IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, L"RegisterExtensionInfo" },
7554 { L"UnregisterFonts", IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, L"RegisterFonts" },
7555 { L"UnregisterMIMEInfo", IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, L"RegisterMIMEInfo" },
7556 { L"UnregisterProgIdInfo", IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, L"RegisterProgIdInfo" },
7557 { L"UnregisterTypeLibraries", IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, L"RegisterTypeLibraries" },
7558 { L"ValidateProductID", 0, 0, ACTION_ValidateProductID, NULL },
7559 { L"WriteEnvironmentStrings", IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, L"RemoveEnvironmentStrings" },
7560 { L"WriteIniValues", IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, L"RemoveIniValues" },
7561 { L"WriteRegistryValues", IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, L"RemoveRegistryValues" },
7562 { L"INSTALL", 0, 0, ACTION_INSTALL, NULL },
7563 { 0 }
7566 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
7568 UINT rc = ERROR_FUNCTION_NOT_CALLED;
7569 UINT i;
7571 i = 0;
7572 while (StandardActions[i].action != NULL)
7574 if (!wcscmp( StandardActions[i].action, action ))
7576 WCHAR description[100] = {0}, template[100] = {0};
7578 if (StandardActions[i].description != 0)
7579 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
7580 if (StandardActions[i].template != 0)
7581 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
7583 ui_actionstart(package, action, description, template);
7584 if (StandardActions[i].handler)
7586 ui_actioninfo( package, action, TRUE, 0 );
7587 rc = StandardActions[i].handler( package );
7588 ui_actioninfo( package, action, FALSE, !rc );
7590 if (StandardActions[i].action_rollback && !package->need_rollback)
7592 TRACE("scheduling rollback action\n");
7593 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7596 else
7598 FIXME("unhandled standard action %s\n", debugstr_w(action));
7599 rc = ERROR_SUCCESS;
7601 break;
7603 i++;
7606 return rc;
7609 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
7611 UINT rc;
7613 TRACE("Performing action (%s)\n", debugstr_w(action));
7615 package->action_progress_increment = 0;
7616 rc = ACTION_HandleStandardAction(package, action);
7618 if (rc == ERROR_FUNCTION_NOT_CALLED)
7619 rc = ACTION_HandleCustomAction(package, action);
7621 if (rc == ERROR_FUNCTION_NOT_CALLED)
7622 WARN("unhandled msi action %s\n", debugstr_w(action));
7624 return rc;
7627 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7629 UINT rc = ERROR_SUCCESS;
7630 MSIRECORD *row;
7632 if (needs_ui_sequence(package))
7633 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `InstallUISequence` WHERE `Sequence` = %d", seq);
7634 else
7635 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `InstallExecuteSequence` WHERE `Sequence` = %d", seq);
7637 if (row)
7639 LPCWSTR action, cond;
7641 TRACE("Running the actions\n");
7643 /* check conditions */
7644 cond = MSI_RecordGetString(row, 2);
7646 /* this is a hack to skip errors in the condition code */
7647 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7649 msiobj_release(&row->hdr);
7650 return ERROR_SUCCESS;
7653 action = MSI_RecordGetString(row, 1);
7654 if (!action)
7656 ERR("failed to fetch action\n");
7657 msiobj_release(&row->hdr);
7658 return ERROR_FUNCTION_FAILED;
7661 rc = ACTION_PerformAction(package, action);
7663 msiobj_release(&row->hdr);
7666 return rc;
7669 /****************************************************
7670 * TOP level entry points
7671 *****************************************************/
7673 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7674 LPCWSTR szCommandLine )
7676 WCHAR *reinstall = NULL, *productcode, *action;
7677 UINT rc;
7678 DWORD len = 0;
7680 if (szPackagePath)
7682 LPWSTR p, dir;
7683 LPCWSTR file;
7685 dir = wcsdup(szPackagePath);
7686 p = wcsrchr(dir, '\\');
7687 if (p)
7689 *(++p) = 0;
7690 file = szPackagePath + (p - dir);
7692 else
7694 free(dir);
7695 dir = malloc(MAX_PATH * sizeof(WCHAR));
7696 GetCurrentDirectoryW(MAX_PATH, dir);
7697 lstrcatW(dir, L"\\");
7698 file = szPackagePath;
7701 free(package->PackagePath);
7702 package->PackagePath = malloc((wcslen(dir) + wcslen(file) + 1) * sizeof(WCHAR));
7703 if (!package->PackagePath)
7705 free(dir);
7706 return ERROR_OUTOFMEMORY;
7709 lstrcpyW(package->PackagePath, dir);
7710 lstrcatW(package->PackagePath, file);
7711 free(dir);
7713 msi_set_sourcedir_props(package, FALSE);
7716 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7717 if (rc != ERROR_SUCCESS)
7718 return rc;
7720 msi_apply_transforms( package );
7721 msi_apply_patches( package );
7723 if (msi_get_property( package->db, L"ACTION", NULL, &len ))
7724 msi_set_property( package->db, L"ACTION", L"INSTALL", -1 );
7725 action = msi_dup_property( package->db, L"ACTION" );
7726 CharUpperW(action);
7728 msi_set_original_database_property( package->db, szPackagePath );
7729 msi_parse_command_line( package, szCommandLine, FALSE );
7730 msi_adjust_privilege_properties( package );
7731 msi_set_context( package );
7733 productcode = msi_dup_property( package->db, L"ProductCode" );
7734 if (wcsicmp( productcode, package->ProductCode ))
7736 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7737 free( package->ProductCode );
7738 package->ProductCode = productcode;
7740 else free( productcode );
7742 if (msi_get_property_int( package->db, L"DISABLEROLLBACK", 0 ))
7744 TRACE("disabling rollback\n");
7745 msi_set_property( package->db, L"RollbackDisabled", L"1", -1 );
7748 rc = ACTION_PerformAction(package, action);
7750 /* process the ending type action */
7751 if (rc == ERROR_SUCCESS)
7752 ACTION_PerformActionSequence(package, -1);
7753 else if (rc == ERROR_INSTALL_USEREXIT)
7754 ACTION_PerformActionSequence(package, -2);
7755 else if (rc == ERROR_INSTALL_SUSPEND)
7756 ACTION_PerformActionSequence(package, -4);
7757 else /* failed */
7759 ACTION_PerformActionSequence(package, -3);
7760 if (!msi_get_property_int( package->db, L"RollbackDisabled", 0 ))
7762 package->need_rollback = TRUE;
7766 /* finish up running custom actions */
7767 ACTION_FinishCustomActions(package);
7769 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, L"REINSTALL" )))
7771 WARN("installation failed, running rollback script\n");
7772 execute_script( package, SCRIPT_ROLLBACK );
7774 free( reinstall );
7775 free( action );
7777 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7778 return ERROR_SUCCESS_REBOOT_REQUIRED;
7780 return rc;