include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / msi / action.c
blob17f9db0c73a74be5677fc63a0b065121bb19a97d
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 += cost_from_size( 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 += cost_from_size( 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 += cost_from_size( file->FileSize - file_size );
2096 free( font_version );
2097 continue;
2100 if (file_size != file->FileSize)
2102 comp->cost += cost_from_size( 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 );
2296 msi_set_property( package->db, L"PrimaryVolumeSpaceRequired", buf, -1 );
2298 swprintf( buf, ARRAY_SIZE(buf), L"%lu", (free.QuadPart / 512) - required );
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 \"%s\"";
4585 static const WCHAR unregW[] = L"regsvr32.exe /s /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 /* install global assemblies */
5148 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
5150 MSICOMPONENT *comp = file->Component;
5152 if (!msi_is_global_assembly( comp ) || (file->state != msifs_missing && file->state != msifs_overwrite))
5153 continue;
5155 rc = msi_install_assembly( package, comp );
5156 if (rc != ERROR_SUCCESS)
5158 ERR("Failed to install assembly\n");
5159 return ERROR_INSTALL_FAILURE;
5161 file->state = msifs_installed;
5164 /* patch global assemblies */
5165 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
5167 MSICOMPONENT *comp = patch->File->Component;
5169 if (!msi_is_global_assembly( comp ) || !patch->path) continue;
5171 rc = msi_patch_assembly( package, comp->assembly, patch );
5172 if (rc && !(patch->Attributes & msidbPatchAttributesNonVital))
5174 ERR("Failed to apply patch to file: %s\n", debugstr_w(patch->File->File));
5175 return rc;
5178 if ((rc = msi_install_assembly( package, comp )))
5180 ERR("Failed to install patched assembly\n");
5181 return rc;
5185 /* then handle commit actions */
5186 rc = execute_script(package, SCRIPT_COMMIT);
5187 if (rc != ERROR_SUCCESS)
5188 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)
5267 msiobj_release(&record->hdr);
5268 return ERROR_INSTALL_USEREXIT;
5270 attrib = GetFileAttributesW(package->db->path);
5272 msiobj_release(&record->hdr);
5273 rc = ERROR_SUCCESS;
5275 else
5276 return ERROR_SUCCESS;
5278 return rc;
5281 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5283 static const WCHAR szPropKeys[][80] =
5285 L"ProductID",
5286 L"USERNAME",
5287 L"COMPANYNAME",
5288 L"",
5290 static const WCHAR szRegKeys[][80] =
5292 L"ProductID",
5293 L"RegOwner",
5294 L"RegCompany",
5295 L"",
5297 HKEY hkey = 0;
5298 LPWSTR buffer, productid = NULL;
5299 UINT i, rc = ERROR_SUCCESS;
5300 MSIRECORD *uirow;
5302 if (package->script == SCRIPT_NONE)
5303 return msi_schedule_action(package, SCRIPT_INSTALL, L"RegisterUser");
5305 if (check_unpublish(package))
5307 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5308 goto end;
5311 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5312 if (!productid)
5313 goto end;
5315 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5316 NULL, &hkey, TRUE);
5317 if (rc != ERROR_SUCCESS)
5318 goto end;
5320 for( i = 0; szPropKeys[i][0]; i++ )
5322 buffer = msi_dup_property( package->db, szPropKeys[i] );
5323 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5324 free( buffer );
5327 end:
5328 uirow = MSI_CreateRecord( 1 );
5329 MSI_RecordSetStringW( uirow, 1, productid );
5330 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5331 msiobj_release( &uirow->hdr );
5333 free(productid);
5334 RegCloseKey(hkey);
5335 return rc;
5338 static UINT iterate_properties(MSIRECORD *record, void *param)
5340 MSIRECORD *uirow;
5342 uirow = MSI_CloneRecord(record);
5343 if (!uirow) return ERROR_OUTOFMEMORY;
5344 MSI_RecordSetStringW(uirow, 0, L"Property(S): [1] = [2]");
5345 MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
5346 msiobj_release(&uirow->hdr);
5348 return ERROR_SUCCESS;
5352 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5354 WCHAR *productname;
5355 WCHAR *action;
5356 WCHAR *info_template;
5357 MSIQUERY *view;
5358 MSIRECORD *uirow, *uirow_info;
5359 UINT rc;
5361 /* Send COMMONDATA and INFO messages. */
5362 /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
5363 uirow = MSI_CreateRecord(3);
5364 if (!uirow) return ERROR_OUTOFMEMORY;
5365 MSI_RecordSetStringW(uirow, 0, NULL);
5366 MSI_RecordSetInteger(uirow, 1, 0);
5367 MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
5368 MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
5369 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5370 /* FIXME: send INSTALLMESSAGE_PROGRESS */
5371 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5373 if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
5375 uirow_info = MSI_CreateRecord(0);
5376 if (!uirow_info)
5378 msiobj_release(&uirow->hdr);
5379 return ERROR_OUTOFMEMORY;
5381 info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
5382 MSI_RecordSetStringW(uirow_info, 0, info_template);
5383 free(info_template);
5384 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
5385 msiobj_release(&uirow_info->hdr);
5388 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5390 productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
5391 MSI_RecordSetInteger(uirow, 1, 1);
5392 MSI_RecordSetStringW(uirow, 2, productname);
5393 MSI_RecordSetStringW(uirow, 3, NULL);
5394 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5395 msiobj_release(&uirow->hdr);
5397 package->LastActionResult = MSI_NULL_INTEGER;
5399 action = msi_dup_property(package->db, L"EXECUTEACTION");
5400 if (!action) action = msi_strdupW(L"INSTALL", ARRAY_SIZE(L"INSTALL") - 1);
5402 /* Perform the action. Top-level actions trigger a sequence. */
5403 if (!wcscmp(action, L"INSTALL"))
5405 /* Send ACTIONSTART/INFO and INSTALLSTART. */
5406 ui_actionstart(package, L"INSTALL", NULL, NULL);
5407 ui_actioninfo(package, L"INSTALL", TRUE, 0);
5408 uirow = MSI_CreateRecord(2);
5409 if (!uirow)
5411 rc = ERROR_OUTOFMEMORY;
5412 goto end;
5414 MSI_RecordSetStringW(uirow, 0, NULL);
5415 MSI_RecordSetStringW(uirow, 1, productname);
5416 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5417 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
5418 msiobj_release(&uirow->hdr);
5420 /* Perform the installation. Always use the ExecuteSequence. */
5421 package->InWhatSequence |= SEQUENCE_EXEC;
5422 rc = ACTION_ProcessExecSequence(package);
5424 /* Send return value and INSTALLEND. */
5425 ui_actioninfo(package, L"INSTALL", FALSE, !rc);
5426 uirow = MSI_CreateRecord(3);
5427 if (!uirow)
5429 rc = ERROR_OUTOFMEMORY;
5430 goto end;
5432 MSI_RecordSetStringW(uirow, 0, NULL);
5433 MSI_RecordSetStringW(uirow, 1, productname);
5434 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5435 MSI_RecordSetInteger(uirow, 3, !rc);
5436 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
5437 msiobj_release(&uirow->hdr);
5439 else
5440 rc = ACTION_PerformAction(package, action);
5442 /* Send all set properties. */
5443 if (!MSI_OpenQuery(package->db, &view, L"SELECT * FROM `_Property`"))
5445 MSI_IterateRecords(view, NULL, iterate_properties, package);
5446 msiobj_release(&view->hdr);
5449 /* And finally, toggle the cancel off and on. */
5450 uirow = MSI_CreateRecord(2);
5451 if (!uirow)
5453 rc = ERROR_OUTOFMEMORY;
5454 goto end;
5456 MSI_RecordSetStringW(uirow, 0, NULL);
5457 MSI_RecordSetInteger(uirow, 1, 2);
5458 MSI_RecordSetInteger(uirow, 2, 0);
5459 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5460 MSI_RecordSetInteger(uirow, 2, 1);
5461 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5462 msiobj_release(&uirow->hdr);
5464 end:
5465 free(productname);
5466 free(action);
5467 return rc;
5470 static UINT ACTION_INSTALL(MSIPACKAGE *package)
5472 msi_set_property(package->db, L"EXECUTEACTION", L"INSTALL", -1);
5473 if (needs_ui_sequence(package) && ui_sequence_exists(package))
5475 package->InWhatSequence |= SEQUENCE_UI;
5476 return ACTION_ProcessUISequence(package);
5478 else
5479 return ACTION_ExecuteAction(package);
5482 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5484 WCHAR productid_85[21], component_85[21], *ret;
5485 GUID clsid;
5486 DWORD sz;
5488 /* > is used if there is a component GUID and < if not. */
5490 productid_85[0] = 0;
5491 component_85[0] = 0;
5492 CLSIDFromString( package->ProductCode, &clsid );
5494 encode_base85_guid( &clsid, productid_85 );
5495 if (component)
5497 CLSIDFromString( component->ComponentId, &clsid );
5498 encode_base85_guid( &clsid, component_85 );
5501 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5502 debugstr_w(component_85));
5504 sz = 20 + lstrlenW( feature ) + 20 + 3;
5505 ret = calloc( 1, sz * sizeof(WCHAR) );
5506 if (ret) swprintf( ret, sz, L"%s%s%c%s", productid_85, feature, component ? '>' : '<', component_85 );
5507 return ret;
5510 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5512 MSIPACKAGE *package = param;
5513 LPCWSTR compgroupid, component, feature, qualifier, text;
5514 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5515 HKEY hkey = NULL;
5516 UINT rc;
5517 MSICOMPONENT *comp;
5518 MSIFEATURE *feat;
5519 DWORD sz;
5520 MSIRECORD *uirow;
5521 int len;
5523 feature = MSI_RecordGetString(rec, 5);
5524 feat = msi_get_loaded_feature(package, feature);
5525 if (!feat)
5526 return ERROR_SUCCESS;
5528 feat->Action = msi_get_feature_action( package, feat );
5529 if (feat->Action != INSTALLSTATE_LOCAL &&
5530 feat->Action != INSTALLSTATE_SOURCE &&
5531 feat->Action != INSTALLSTATE_ADVERTISED)
5533 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5534 return ERROR_SUCCESS;
5537 component = MSI_RecordGetString(rec, 3);
5538 comp = msi_get_loaded_component(package, component);
5539 if (!comp)
5540 return ERROR_SUCCESS;
5542 compgroupid = MSI_RecordGetString(rec,1);
5543 qualifier = MSI_RecordGetString(rec,2);
5545 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5546 if (rc != ERROR_SUCCESS)
5547 goto end;
5549 advertise = msi_create_component_advertise_string( package, comp, feature );
5550 text = MSI_RecordGetString( rec, 4 );
5551 if (text)
5553 p = malloc( (wcslen( advertise ) + wcslen( text ) + 1) * sizeof(WCHAR) );
5554 lstrcpyW( p, advertise );
5555 lstrcatW( p, text );
5556 free( advertise );
5557 advertise = p;
5559 existing = msi_reg_get_val_str( hkey, qualifier );
5561 sz = lstrlenW( advertise ) + 1;
5562 if (existing)
5564 for (p = existing; *p; p += len)
5566 len = lstrlenW( p ) + 1;
5567 if (wcscmp( advertise, p )) sz += len;
5570 if (!(output = malloc( (sz + 1) * sizeof(WCHAR) )))
5572 rc = ERROR_OUTOFMEMORY;
5573 goto end;
5575 q = output;
5576 if (existing)
5578 for (p = existing; *p; p += len)
5580 len = lstrlenW( p ) + 1;
5581 if (wcscmp( advertise, p ))
5583 memcpy( q, p, len * sizeof(WCHAR) );
5584 q += len;
5588 lstrcpyW( q, advertise );
5589 q[lstrlenW( q ) + 1] = 0;
5591 msi_reg_set_val_multi_str( hkey, qualifier, output );
5593 end:
5594 RegCloseKey(hkey);
5595 free( output );
5596 free( advertise );
5597 free( existing );
5599 /* the UI chunk */
5600 uirow = MSI_CreateRecord( 2 );
5601 MSI_RecordSetStringW( uirow, 1, compgroupid );
5602 MSI_RecordSetStringW( uirow, 2, qualifier);
5603 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5604 msiobj_release( &uirow->hdr );
5605 /* FIXME: call ui_progress? */
5607 return rc;
5611 * At present I am ignoring the advertised components part of this and only
5612 * focusing on the qualified component sets
5614 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5616 MSIQUERY *view;
5617 UINT rc;
5619 if (package->script == SCRIPT_NONE)
5620 return msi_schedule_action(package, SCRIPT_INSTALL, L"PublishComponents");
5622 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `PublishComponent`", &view);
5623 if (rc != ERROR_SUCCESS)
5624 return ERROR_SUCCESS;
5626 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5627 msiobj_release(&view->hdr);
5628 return rc;
5631 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5633 MSIPACKAGE *package = param;
5634 LPCWSTR compgroupid, component, feature, qualifier;
5635 MSICOMPONENT *comp;
5636 MSIFEATURE *feat;
5637 MSIRECORD *uirow;
5638 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5639 LONG res;
5641 feature = MSI_RecordGetString( rec, 5 );
5642 feat = msi_get_loaded_feature( package, feature );
5643 if (!feat)
5644 return ERROR_SUCCESS;
5646 feat->Action = msi_get_feature_action( package, feat );
5647 if (feat->Action != INSTALLSTATE_ABSENT)
5649 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5650 return ERROR_SUCCESS;
5653 component = MSI_RecordGetString( rec, 3 );
5654 comp = msi_get_loaded_component( package, component );
5655 if (!comp)
5656 return ERROR_SUCCESS;
5658 compgroupid = MSI_RecordGetString( rec, 1 );
5659 qualifier = MSI_RecordGetString( rec, 2 );
5661 squash_guid( compgroupid, squashed );
5662 lstrcpyW( keypath, L"Software\\Microsoft\\Installer\\Components\\" );
5663 lstrcatW( keypath, squashed );
5665 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5666 if (res != ERROR_SUCCESS)
5668 WARN( "unable to delete component key %ld\n", res );
5671 uirow = MSI_CreateRecord( 2 );
5672 MSI_RecordSetStringW( uirow, 1, compgroupid );
5673 MSI_RecordSetStringW( uirow, 2, qualifier );
5674 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5675 msiobj_release( &uirow->hdr );
5677 return ERROR_SUCCESS;
5680 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5682 MSIQUERY *view;
5683 UINT rc;
5685 if (package->script == SCRIPT_NONE)
5686 return msi_schedule_action(package, SCRIPT_INSTALL, L"UnpublishComponents");
5688 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `PublishComponent`", &view );
5689 if (rc != ERROR_SUCCESS)
5690 return ERROR_SUCCESS;
5692 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5693 msiobj_release( &view->hdr );
5694 return rc;
5697 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5699 MSIPACKAGE *package = param;
5700 MSICOMPONENT *component;
5701 MSIRECORD *row;
5702 MSIFILE *file;
5703 SC_HANDLE hscm = NULL, service = NULL;
5704 LPCWSTR comp, key;
5705 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5706 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5707 DWORD serv_type, start_type, err_control;
5708 BOOL is_vital;
5709 SERVICE_DESCRIPTIONW sd = {NULL};
5710 UINT ret = ERROR_SUCCESS;
5712 comp = MSI_RecordGetString( rec, 12 );
5713 component = msi_get_loaded_component( package, comp );
5714 if (!component)
5716 WARN("service component not found\n");
5717 goto done;
5719 component->Action = msi_get_component_action( package, component );
5720 if (component->Action != INSTALLSTATE_LOCAL)
5722 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5723 goto done;
5725 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5726 if (!hscm)
5728 ERR("Failed to open the SC Manager!\n");
5729 goto done;
5732 start_type = MSI_RecordGetInteger(rec, 5);
5733 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5734 goto done;
5736 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5737 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5738 serv_type = MSI_RecordGetInteger(rec, 4);
5739 err_control = MSI_RecordGetInteger(rec, 6);
5740 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5741 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5742 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5743 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5744 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5745 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5747 /* Should the complete install fail if CreateService fails? */
5748 is_vital = (err_control & msidbServiceInstallErrorControlVital);
5750 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
5751 CreateService (under Windows) would fail if not. */
5752 err_control &= ~msidbServiceInstallErrorControlVital;
5754 /* fetch the service path */
5755 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `Component` WHERE `Component` = '%s'", comp);
5756 if (!row)
5758 ERR("Query failed\n");
5759 goto done;
5761 if (!(key = MSI_RecordGetString(row, 6)))
5763 msiobj_release(&row->hdr);
5764 goto done;
5766 file = msi_get_loaded_file(package, key);
5767 msiobj_release(&row->hdr);
5768 if (!file)
5770 ERR("Failed to load the service file\n");
5771 goto done;
5774 if (!args || !args[0]) image_path = file->TargetPath;
5775 else
5777 int len = lstrlenW(file->TargetPath) + lstrlenW(args) + 2;
5778 if (!(image_path = malloc(len * sizeof(WCHAR))))
5780 ret = ERROR_OUTOFMEMORY;
5781 goto done;
5784 lstrcpyW(image_path, file->TargetPath);
5785 lstrcatW(image_path, L" ");
5786 lstrcatW(image_path, args);
5788 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5789 start_type, err_control, image_path, load_order,
5790 NULL, depends, serv_name, pass);
5792 if (!service)
5794 if (GetLastError() != ERROR_SERVICE_EXISTS)
5796 WARN( "failed to create service %s (%lu)\n", debugstr_w(name), GetLastError() );
5797 if (is_vital)
5798 ret = ERROR_INSTALL_FAILURE;
5802 else if (sd.lpDescription)
5804 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5805 WARN( "failed to set service description %lu\n", GetLastError() );
5808 if (image_path != file->TargetPath) free(image_path);
5809 done:
5810 if (service) CloseServiceHandle(service);
5811 if (hscm) CloseServiceHandle(hscm);
5812 free(name);
5813 free(disp);
5814 free(sd.lpDescription);
5815 free(load_order);
5816 free(serv_name);
5817 free(pass);
5818 free(depends);
5819 free(args);
5821 return ret;
5824 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5826 MSIQUERY *view;
5827 UINT rc;
5829 if (package->script == SCRIPT_NONE)
5830 return msi_schedule_action(package, SCRIPT_INSTALL, L"InstallServices");
5832 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ServiceInstall`", &view);
5833 if (rc != ERROR_SUCCESS)
5834 return ERROR_SUCCESS;
5836 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5837 msiobj_release(&view->hdr);
5838 return rc;
5841 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5842 static const WCHAR **service_args_to_vector(WCHAR *args, DWORD *numargs)
5844 LPCWSTR *vector, *temp_vector;
5845 LPWSTR p, q;
5846 DWORD sep_len;
5848 *numargs = 0;
5849 sep_len = ARRAY_SIZE(L"[~]") - 1;
5851 if (!args)
5852 return NULL;
5854 vector = malloc(sizeof(WCHAR *));
5855 if (!vector)
5856 return NULL;
5858 p = args;
5861 (*numargs)++;
5862 vector[*numargs - 1] = p;
5864 if ((q = wcsstr(p, L"[~]")))
5866 *q = '\0';
5868 temp_vector = realloc(vector, (*numargs + 1) * sizeof(WCHAR *));
5869 if (!temp_vector)
5871 free(vector);
5872 return NULL;
5874 vector = temp_vector;
5876 p = q + sep_len;
5878 } while (q);
5880 return vector;
5883 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5885 MSIPACKAGE *package = param;
5886 MSICOMPONENT *comp;
5887 MSIRECORD *uirow;
5888 SC_HANDLE scm = NULL, service = NULL;
5889 LPCWSTR component, *vector = NULL;
5890 LPWSTR name, args, display_name = NULL;
5891 DWORD event, numargs, len, wait, dummy;
5892 UINT r = ERROR_FUNCTION_FAILED;
5893 SERVICE_STATUS_PROCESS status;
5894 ULONGLONG start_time;
5896 component = MSI_RecordGetString(rec, 6);
5897 comp = msi_get_loaded_component(package, component);
5898 if (!comp)
5899 return ERROR_SUCCESS;
5901 event = MSI_RecordGetInteger( rec, 3 );
5902 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5904 comp->Action = msi_get_component_action( package, comp );
5905 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
5906 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
5908 TRACE("not starting %s\n", debugstr_w(name));
5909 free(name);
5910 return ERROR_SUCCESS;
5913 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5914 wait = MSI_RecordGetInteger(rec, 5);
5916 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5917 if (!scm)
5919 ERR("Failed to open the service control manager\n");
5920 goto done;
5923 len = 0;
5924 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5925 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5927 if ((display_name = malloc(++len * sizeof(WCHAR))))
5928 GetServiceDisplayNameW( scm, name, display_name, &len );
5931 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5932 if (!service)
5934 ERR( "failed to open service %s (%lu)\n", debugstr_w(name), GetLastError() );
5935 goto done;
5938 vector = service_args_to_vector(args, &numargs);
5940 if (!StartServiceW(service, numargs, vector) &&
5941 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5943 ERR( "failed to start service %s (%lu)\n", debugstr_w(name), GetLastError() );
5944 goto done;
5947 r = ERROR_SUCCESS;
5948 if (wait)
5950 /* wait for at most 30 seconds for the service to be up and running */
5951 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5952 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5954 TRACE( "failed to query service status (%lu)\n", GetLastError() );
5955 goto done;
5957 start_time = GetTickCount64();
5958 while (status.dwCurrentState == SERVICE_START_PENDING)
5960 if (GetTickCount64() - start_time > 30000) break;
5961 Sleep(1000);
5962 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5963 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5965 TRACE( "failed to query service status (%lu)\n", GetLastError() );
5966 goto done;
5969 if (status.dwCurrentState != SERVICE_RUNNING)
5971 WARN( "service failed to start %lu\n", status.dwCurrentState );
5972 r = ERROR_FUNCTION_FAILED;
5976 done:
5977 uirow = MSI_CreateRecord( 2 );
5978 MSI_RecordSetStringW( uirow, 1, display_name );
5979 MSI_RecordSetStringW( uirow, 2, name );
5980 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5981 msiobj_release( &uirow->hdr );
5983 if (service) CloseServiceHandle(service);
5984 if (scm) CloseServiceHandle(scm);
5986 free(name);
5987 free(args);
5988 free(vector);
5989 free(display_name);
5990 return r;
5993 static UINT ACTION_StartServices( MSIPACKAGE *package )
5995 MSIQUERY *view;
5996 UINT rc;
5998 if (package->script == SCRIPT_NONE)
5999 return msi_schedule_action(package, SCRIPT_INSTALL, L"StartServices");
6001 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ServiceControl`", &view);
6002 if (rc != ERROR_SUCCESS)
6003 return ERROR_SUCCESS;
6005 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6006 msiobj_release(&view->hdr);
6007 return rc;
6010 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6012 DWORD i, needed, count;
6013 ENUM_SERVICE_STATUSW *dependencies;
6014 SERVICE_STATUS ss;
6015 SC_HANDLE depserv;
6016 BOOL stopped, ret = FALSE;
6018 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6019 0, &needed, &count))
6020 return TRUE;
6022 if (GetLastError() != ERROR_MORE_DATA)
6023 return FALSE;
6025 dependencies = malloc(needed);
6026 if (!dependencies)
6027 return FALSE;
6029 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6030 needed, &needed, &count))
6031 goto done;
6033 for (i = 0; i < count; i++)
6035 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6036 SERVICE_STOP | SERVICE_QUERY_STATUS);
6037 if (!depserv)
6038 goto done;
6040 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6041 CloseServiceHandle(depserv);
6042 if (!stopped)
6043 goto done;
6046 ret = TRUE;
6048 done:
6049 free(dependencies);
6050 return ret;
6053 static UINT stop_service( LPCWSTR name )
6055 SC_HANDLE scm = NULL, service = NULL;
6056 SERVICE_STATUS status;
6057 SERVICE_STATUS_PROCESS ssp;
6058 DWORD needed;
6060 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6061 if (!scm)
6063 WARN( "failed to open the SCM (%lu)\n", GetLastError() );
6064 goto done;
6067 service = OpenServiceW(scm, name, SERVICE_STOP | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS);
6068 if (!service)
6070 WARN( "failed to open service %s (%lu)\n", debugstr_w(name), GetLastError() );
6071 goto done;
6074 if (!QueryServiceStatusEx( service, SC_STATUS_PROCESS_INFO, (BYTE *)&ssp, sizeof(ssp), &needed) )
6076 WARN( "failed to query service status %s (%lu)\n", debugstr_w(name), GetLastError() );
6077 goto done;
6080 if (ssp.dwCurrentState == SERVICE_STOPPED)
6081 goto done;
6083 stop_service_dependents(scm, service);
6085 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6086 WARN( "failed to stop service %s (%lu)\n", debugstr_w(name), GetLastError() );
6088 done:
6089 if (service) CloseServiceHandle(service);
6090 if (scm) CloseServiceHandle(scm);
6092 return ERROR_SUCCESS;
6095 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6097 MSIPACKAGE *package = param;
6098 MSICOMPONENT *comp;
6099 MSIRECORD *uirow;
6100 LPCWSTR component;
6101 WCHAR *name, *display_name = NULL;
6102 DWORD event, len;
6103 SC_HANDLE scm;
6105 component = MSI_RecordGetString( rec, 6 );
6106 comp = msi_get_loaded_component( package, component );
6107 if (!comp)
6108 return ERROR_SUCCESS;
6110 event = MSI_RecordGetInteger( rec, 3 );
6111 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6113 comp->Action = msi_get_component_action( package, comp );
6114 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6115 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6117 TRACE("not stopping %s\n", debugstr_w(name));
6118 free( name );
6119 return ERROR_SUCCESS;
6122 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6123 if (!scm)
6125 ERR("Failed to open the service control manager\n");
6126 goto done;
6129 len = 0;
6130 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6131 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6133 if ((display_name = malloc( ++len * sizeof(WCHAR ))))
6134 GetServiceDisplayNameW( scm, name, display_name, &len );
6136 CloseServiceHandle( scm );
6138 stop_service( name );
6140 done:
6141 uirow = MSI_CreateRecord( 2 );
6142 MSI_RecordSetStringW( uirow, 1, display_name );
6143 MSI_RecordSetStringW( uirow, 2, name );
6144 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6145 msiobj_release( &uirow->hdr );
6147 free( name );
6148 free( display_name );
6149 return ERROR_SUCCESS;
6152 static UINT ACTION_StopServices( MSIPACKAGE *package )
6154 MSIQUERY *view;
6155 UINT rc;
6157 if (package->script == SCRIPT_NONE)
6158 return msi_schedule_action(package, SCRIPT_INSTALL, L"StopServices");
6160 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ServiceControl`", &view);
6161 if (rc != ERROR_SUCCESS)
6162 return ERROR_SUCCESS;
6164 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6165 msiobj_release(&view->hdr);
6166 return rc;
6169 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6171 MSIPACKAGE *package = param;
6172 MSICOMPONENT *comp;
6173 MSIRECORD *uirow;
6174 LPWSTR name = NULL, display_name = NULL;
6175 DWORD event, len;
6176 SC_HANDLE scm = NULL, service = NULL;
6178 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6179 if (!comp)
6180 return ERROR_SUCCESS;
6182 event = MSI_RecordGetInteger( rec, 3 );
6183 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6185 comp->Action = msi_get_component_action( package, comp );
6186 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6187 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6189 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6190 free( name );
6191 return ERROR_SUCCESS;
6193 stop_service( name );
6195 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6196 if (!scm)
6198 WARN( "failed to open the SCM (%lu)\n", GetLastError() );
6199 goto done;
6202 len = 0;
6203 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6204 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6206 if ((display_name = malloc( ++len * sizeof(WCHAR ))))
6207 GetServiceDisplayNameW( scm, name, display_name, &len );
6210 service = OpenServiceW( scm, name, DELETE );
6211 if (!service)
6213 WARN( "failed to open service %s (%lu)\n", debugstr_w(name), GetLastError() );
6214 goto done;
6217 if (!DeleteService( service ))
6218 WARN( "failed to delete service %s (%lu)\n", debugstr_w(name), GetLastError() );
6220 done:
6221 uirow = MSI_CreateRecord( 2 );
6222 MSI_RecordSetStringW( uirow, 1, display_name );
6223 MSI_RecordSetStringW( uirow, 2, name );
6224 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6225 msiobj_release( &uirow->hdr );
6227 if (service) CloseServiceHandle( service );
6228 if (scm) CloseServiceHandle( scm );
6229 free( name );
6230 free( display_name );
6232 return ERROR_SUCCESS;
6235 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6237 MSIQUERY *view;
6238 UINT rc;
6240 if (package->script == SCRIPT_NONE)
6241 return msi_schedule_action(package, SCRIPT_INSTALL, L"DeleteServices");
6243 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ServiceControl`", &view );
6244 if (rc != ERROR_SUCCESS)
6245 return ERROR_SUCCESS;
6247 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6248 msiobj_release( &view->hdr );
6249 return rc;
6252 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6254 MSIPACKAGE *package = param;
6255 LPWSTR driver, driver_path, ptr;
6256 WCHAR outpath[MAX_PATH];
6257 MSIFILE *driver_file = NULL, *setup_file = NULL;
6258 MSICOMPONENT *comp;
6259 MSIRECORD *uirow;
6260 LPCWSTR desc, file_key, component;
6261 DWORD len, usage;
6262 UINT r = ERROR_SUCCESS;
6264 component = MSI_RecordGetString( rec, 2 );
6265 comp = msi_get_loaded_component( package, component );
6266 if (!comp)
6267 return ERROR_SUCCESS;
6269 comp->Action = msi_get_component_action( package, comp );
6270 if (comp->Action != INSTALLSTATE_LOCAL)
6272 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6273 return ERROR_SUCCESS;
6275 desc = MSI_RecordGetString(rec, 3);
6277 file_key = MSI_RecordGetString( rec, 4 );
6278 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6280 file_key = MSI_RecordGetString( rec, 5 );
6281 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6283 if (!driver_file)
6285 ERR("ODBC Driver entry not found!\n");
6286 return ERROR_FUNCTION_FAILED;
6289 len = lstrlenW(desc) + lstrlenW(L"Driver=%s") + lstrlenW(driver_file->FileName);
6290 if (setup_file)
6291 len += lstrlenW(L"Setup=%s") + lstrlenW(setup_file->FileName);
6292 len += lstrlenW(L"FileUsage=1") + 2; /* \0\0 */
6294 driver = malloc(len * sizeof(WCHAR));
6295 if (!driver)
6296 return ERROR_OUTOFMEMORY;
6298 ptr = driver;
6299 lstrcpyW(ptr, desc);
6300 ptr += lstrlenW(ptr) + 1;
6302 len = swprintf(ptr, len - (ptr - driver), L"Driver=%s", driver_file->FileName);
6303 ptr += len + 1;
6305 if (setup_file)
6307 len = swprintf(ptr, len - (ptr - driver), L"Setup=%s", setup_file->FileName);
6308 ptr += len + 1;
6311 lstrcpyW(ptr, L"FileUsage=1");
6312 ptr += lstrlenW(ptr) + 1;
6313 *ptr = '\0';
6315 if (!driver_file->TargetPath)
6317 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6318 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6320 driver_path = wcsdup(driver_file->TargetPath);
6321 ptr = wcsrchr(driver_path, '\\');
6322 if (ptr) *ptr = '\0';
6324 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6325 NULL, ODBC_INSTALL_COMPLETE, &usage))
6327 ERR("Failed to install SQL driver!\n");
6328 r = ERROR_FUNCTION_FAILED;
6331 uirow = MSI_CreateRecord( 5 );
6332 MSI_RecordSetStringW( uirow, 1, desc );
6333 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6334 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6335 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6336 msiobj_release( &uirow->hdr );
6338 free(driver);
6339 free(driver_path);
6341 return r;
6344 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6346 MSIPACKAGE *package = param;
6347 LPWSTR translator, translator_path, ptr;
6348 WCHAR outpath[MAX_PATH];
6349 MSIFILE *translator_file = NULL, *setup_file = NULL;
6350 MSICOMPONENT *comp;
6351 MSIRECORD *uirow;
6352 LPCWSTR desc, file_key, component;
6353 DWORD len, usage;
6354 UINT r = ERROR_SUCCESS;
6356 component = MSI_RecordGetString( rec, 2 );
6357 comp = msi_get_loaded_component( package, component );
6358 if (!comp)
6359 return ERROR_SUCCESS;
6361 comp->Action = msi_get_component_action( package, comp );
6362 if (comp->Action != INSTALLSTATE_LOCAL)
6364 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6365 return ERROR_SUCCESS;
6367 desc = MSI_RecordGetString(rec, 3);
6369 file_key = MSI_RecordGetString( rec, 4 );
6370 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6372 file_key = MSI_RecordGetString( rec, 5 );
6373 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6375 if (!translator_file)
6377 ERR("ODBC Translator entry not found!\n");
6378 return ERROR_FUNCTION_FAILED;
6381 len = lstrlenW(desc) + lstrlenW(L"Translator=%s") + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6382 if (setup_file)
6383 len += lstrlenW(L"Setup=%s") + lstrlenW(setup_file->FileName);
6385 translator = malloc(len * sizeof(WCHAR));
6386 if (!translator)
6387 return ERROR_OUTOFMEMORY;
6389 ptr = translator;
6390 lstrcpyW(ptr, desc);
6391 ptr += lstrlenW(ptr) + 1;
6393 len = swprintf(ptr, len - (ptr - translator), L"Translator=%s", translator_file->FileName);
6394 ptr += len + 1;
6396 if (setup_file)
6398 len = swprintf(ptr, len - (ptr - translator), L"Setup=%s", setup_file->FileName);
6399 ptr += len + 1;
6401 *ptr = '\0';
6403 translator_path = wcsdup(translator_file->TargetPath);
6404 ptr = wcsrchr(translator_path, '\\');
6405 if (ptr) *ptr = '\0';
6407 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6408 NULL, ODBC_INSTALL_COMPLETE, &usage))
6410 ERR("Failed to install SQL translator!\n");
6411 r = ERROR_FUNCTION_FAILED;
6414 uirow = MSI_CreateRecord( 5 );
6415 MSI_RecordSetStringW( uirow, 1, desc );
6416 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6417 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6418 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6419 msiobj_release( &uirow->hdr );
6421 free(translator);
6422 free(translator_path);
6424 return r;
6427 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6429 MSIPACKAGE *package = param;
6430 MSICOMPONENT *comp;
6431 LPWSTR attrs;
6432 LPCWSTR desc, driver, component;
6433 WORD request = ODBC_ADD_SYS_DSN;
6434 INT registration;
6435 DWORD len;
6436 UINT r = ERROR_SUCCESS;
6437 MSIRECORD *uirow;
6439 component = MSI_RecordGetString( rec, 2 );
6440 comp = msi_get_loaded_component( package, component );
6441 if (!comp)
6442 return ERROR_SUCCESS;
6444 comp->Action = msi_get_component_action( package, comp );
6445 if (comp->Action != INSTALLSTATE_LOCAL)
6447 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6448 return ERROR_SUCCESS;
6451 desc = MSI_RecordGetString(rec, 3);
6452 driver = MSI_RecordGetString(rec, 4);
6453 registration = MSI_RecordGetInteger(rec, 5);
6455 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6456 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6458 len = lstrlenW(L"DSN=%s") + lstrlenW(desc) + 2; /* \0\0 */
6459 attrs = malloc(len * sizeof(WCHAR));
6460 if (!attrs)
6461 return ERROR_OUTOFMEMORY;
6463 len = swprintf(attrs, len, L"DSN=%s", desc);
6464 attrs[len + 1] = 0;
6466 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6467 WARN("Failed to install SQL data source!\n");
6469 uirow = MSI_CreateRecord( 5 );
6470 MSI_RecordSetStringW( uirow, 1, desc );
6471 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6472 MSI_RecordSetInteger( uirow, 3, request );
6473 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6474 msiobj_release( &uirow->hdr );
6476 free(attrs);
6478 return r;
6481 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6483 MSIQUERY *view;
6484 UINT rc;
6486 if (package->script == SCRIPT_NONE)
6487 return msi_schedule_action(package, SCRIPT_INSTALL, L"InstallODBC");
6489 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ODBCDriver`", &view);
6490 if (rc == ERROR_SUCCESS)
6492 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6493 msiobj_release(&view->hdr);
6494 if (rc != ERROR_SUCCESS)
6495 return rc;
6497 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ODBCTranslator`", &view);
6498 if (rc == ERROR_SUCCESS)
6500 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6501 msiobj_release(&view->hdr);
6502 if (rc != ERROR_SUCCESS)
6503 return rc;
6505 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `ODBCDataSource`", &view);
6506 if (rc == ERROR_SUCCESS)
6508 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6509 msiobj_release(&view->hdr);
6510 if (rc != ERROR_SUCCESS)
6511 return rc;
6513 return ERROR_SUCCESS;
6516 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6518 MSIPACKAGE *package = param;
6519 MSICOMPONENT *comp;
6520 MSIRECORD *uirow;
6521 DWORD usage;
6522 LPCWSTR desc, component;
6524 component = MSI_RecordGetString( rec, 2 );
6525 comp = msi_get_loaded_component( package, component );
6526 if (!comp)
6527 return ERROR_SUCCESS;
6529 comp->Action = msi_get_component_action( package, comp );
6530 if (comp->Action != INSTALLSTATE_ABSENT)
6532 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6533 return ERROR_SUCCESS;
6536 desc = MSI_RecordGetString( rec, 3 );
6537 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6539 WARN("Failed to remove ODBC driver\n");
6541 else if (!usage)
6543 FIXME("Usage count reached 0\n");
6546 uirow = MSI_CreateRecord( 2 );
6547 MSI_RecordSetStringW( uirow, 1, desc );
6548 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6549 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6550 msiobj_release( &uirow->hdr );
6552 return ERROR_SUCCESS;
6555 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6557 MSIPACKAGE *package = param;
6558 MSICOMPONENT *comp;
6559 MSIRECORD *uirow;
6560 DWORD usage;
6561 LPCWSTR desc, component;
6563 component = MSI_RecordGetString( rec, 2 );
6564 comp = msi_get_loaded_component( package, component );
6565 if (!comp)
6566 return ERROR_SUCCESS;
6568 comp->Action = msi_get_component_action( package, comp );
6569 if (comp->Action != INSTALLSTATE_ABSENT)
6571 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6572 return ERROR_SUCCESS;
6575 desc = MSI_RecordGetString( rec, 3 );
6576 if (!SQLRemoveTranslatorW( desc, &usage ))
6578 WARN("Failed to remove ODBC translator\n");
6580 else if (!usage)
6582 FIXME("Usage count reached 0\n");
6585 uirow = MSI_CreateRecord( 2 );
6586 MSI_RecordSetStringW( uirow, 1, desc );
6587 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6588 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6589 msiobj_release( &uirow->hdr );
6591 return ERROR_SUCCESS;
6594 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6596 MSIPACKAGE *package = param;
6597 MSICOMPONENT *comp;
6598 MSIRECORD *uirow;
6599 LPWSTR attrs;
6600 LPCWSTR desc, driver, component;
6601 WORD request = ODBC_REMOVE_SYS_DSN;
6602 INT registration;
6603 DWORD len;
6605 component = MSI_RecordGetString( rec, 2 );
6606 comp = msi_get_loaded_component( package, component );
6607 if (!comp)
6608 return ERROR_SUCCESS;
6610 comp->Action = msi_get_component_action( package, comp );
6611 if (comp->Action != INSTALLSTATE_ABSENT)
6613 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6614 return ERROR_SUCCESS;
6617 desc = MSI_RecordGetString( rec, 3 );
6618 driver = MSI_RecordGetString( rec, 4 );
6619 registration = MSI_RecordGetInteger( rec, 5 );
6621 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6622 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6624 len = lstrlenW( L"DSN=%s" ) + lstrlenW( desc ) + 2; /* \0\0 */
6625 attrs = malloc( len * sizeof(WCHAR) );
6626 if (!attrs)
6627 return ERROR_OUTOFMEMORY;
6629 FIXME("Use ODBCSourceAttribute table\n");
6631 len = swprintf( attrs, len, L"DSN=%s", desc );
6632 attrs[len + 1] = 0;
6634 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6636 WARN("Failed to remove ODBC data source\n");
6638 free( attrs );
6640 uirow = MSI_CreateRecord( 3 );
6641 MSI_RecordSetStringW( uirow, 1, desc );
6642 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6643 MSI_RecordSetInteger( uirow, 3, request );
6644 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6645 msiobj_release( &uirow->hdr );
6647 return ERROR_SUCCESS;
6650 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6652 MSIQUERY *view;
6653 UINT rc;
6655 if (package->script == SCRIPT_NONE)
6656 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveODBC");
6658 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCDriver`", &view );
6659 if (rc == ERROR_SUCCESS)
6661 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6662 msiobj_release( &view->hdr );
6663 if (rc != ERROR_SUCCESS)
6664 return rc;
6666 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCTranslator`", &view );
6667 if (rc == ERROR_SUCCESS)
6669 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6670 msiobj_release( &view->hdr );
6671 if (rc != ERROR_SUCCESS)
6672 return rc;
6674 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCDataSource`", &view );
6675 if (rc == ERROR_SUCCESS)
6677 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6678 msiobj_release( &view->hdr );
6679 if (rc != ERROR_SUCCESS)
6680 return rc;
6682 return ERROR_SUCCESS;
6685 #define ENV_ACT_SETALWAYS 0x1
6686 #define ENV_ACT_SETABSENT 0x2
6687 #define ENV_ACT_REMOVE 0x4
6688 #define ENV_ACT_REMOVEMATCH 0x8
6690 #define ENV_MOD_MACHINE 0x20000000
6691 #define ENV_MOD_APPEND 0x40000000
6692 #define ENV_MOD_PREFIX 0x80000000
6693 #define ENV_MOD_MASK 0xC0000000
6695 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6697 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6699 const WCHAR *cptr = *name;
6701 *flags = 0;
6702 while (*cptr)
6704 if (*cptr == '=')
6705 *flags |= ENV_ACT_SETALWAYS;
6706 else if (*cptr == '+')
6707 *flags |= ENV_ACT_SETABSENT;
6708 else if (*cptr == '-')
6709 *flags |= ENV_ACT_REMOVE;
6710 else if (*cptr == '!')
6711 *flags |= ENV_ACT_REMOVEMATCH;
6712 else if (*cptr == '*')
6713 *flags |= ENV_MOD_MACHINE | ENV_ACT_REMOVE;
6714 else
6715 break;
6717 cptr++;
6718 (*name)++;
6721 if (!*cptr)
6723 ERR("Missing environment variable\n");
6724 return ERROR_FUNCTION_FAILED;
6727 if (*value)
6729 LPCWSTR ptr = *value;
6730 if (!wcsncmp(ptr, L"[~]", 3))
6732 if (ptr[3] == ';')
6734 *flags |= ENV_MOD_APPEND;
6735 *value += 3;
6737 else
6739 *value = NULL;
6742 else if (lstrlenW(*value) >= 3)
6744 ptr += lstrlenW(ptr) - 3;
6745 if (!wcscmp( ptr, L"[~]" ))
6747 if ((ptr-1) > *value && *(ptr-1) == ';')
6749 *flags |= ENV_MOD_PREFIX;
6750 /* the "[~]" will be removed by deformat_string */;
6752 else
6754 *value = NULL;
6760 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6761 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6762 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6763 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6765 ERR( "invalid flags: %#lx\n", *flags );
6766 return ERROR_FUNCTION_FAILED;
6769 if (!*flags)
6770 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6772 return ERROR_SUCCESS;
6775 static UINT open_env_key( DWORD flags, HKEY *key )
6777 const WCHAR *env;
6778 HKEY root;
6779 LONG res;
6781 if (flags & ENV_MOD_MACHINE)
6783 env = L"System\\CurrentControlSet\\Control\\Session Manager\\Environment";
6784 root = HKEY_LOCAL_MACHINE;
6786 else
6788 env = L"Environment";
6789 root = HKEY_CURRENT_USER;
6792 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6793 if (res != ERROR_SUCCESS)
6795 WARN( "failed to open key %s (%ld)\n", debugstr_w(env), res );
6796 return ERROR_FUNCTION_FAILED;
6799 return ERROR_SUCCESS;
6802 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6804 MSIPACKAGE *package = param;
6805 LPCWSTR name, value, component;
6806 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
6807 DWORD flags, type, size, len, len_value = 0;
6808 UINT res;
6809 HKEY env = NULL;
6810 MSICOMPONENT *comp;
6811 MSIRECORD *uirow;
6812 int action = 0, found = 0;
6814 component = MSI_RecordGetString(rec, 4);
6815 comp = msi_get_loaded_component(package, component);
6816 if (!comp)
6817 return ERROR_SUCCESS;
6819 comp->Action = msi_get_component_action( package, comp );
6820 if (comp->Action != INSTALLSTATE_LOCAL)
6822 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6823 return ERROR_SUCCESS;
6825 name = MSI_RecordGetString(rec, 2);
6826 value = MSI_RecordGetString(rec, 3);
6828 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6830 res = env_parse_flags(&name, &value, &flags);
6831 if (res != ERROR_SUCCESS || !value)
6832 goto done;
6834 if (value)
6836 DWORD len = deformat_string( package, value, &deformatted );
6837 if (!deformatted)
6839 res = ERROR_OUTOFMEMORY;
6840 goto done;
6843 if (len)
6845 value = deformatted;
6846 if (flags & ENV_MOD_PREFIX)
6848 p = wcsrchr( value, ';' );
6849 len_value = p - value;
6851 else if (flags & ENV_MOD_APPEND)
6853 value = wcschr( value, ';' ) + 1;
6854 len_value = lstrlenW( value );
6856 else len_value = lstrlenW( value );
6858 else
6860 value = NULL;
6864 res = open_env_key( flags, &env );
6865 if (res != ERROR_SUCCESS)
6866 goto done;
6868 if (flags & ENV_MOD_MACHINE)
6869 action |= 0x20000000;
6871 size = 0;
6872 type = REG_SZ;
6873 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6874 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6875 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6876 goto done;
6878 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6880 action = 0x2;
6882 /* Nothing to do. */
6883 if (!value)
6885 res = ERROR_SUCCESS;
6886 goto done;
6888 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6889 newval = wcsdup(value);
6890 if (!newval)
6892 res = ERROR_OUTOFMEMORY;
6893 goto done;
6896 else
6898 action = 0x1;
6900 /* Contrary to MSDN, +-variable to [~];path works */
6901 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6903 res = ERROR_SUCCESS;
6904 goto done;
6907 if (!(p = q = data = malloc( size )))
6909 free(deformatted);
6910 RegCloseKey(env);
6911 return ERROR_OUTOFMEMORY;
6914 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
6915 if (res != ERROR_SUCCESS)
6916 goto done;
6918 if (flags & ENV_ACT_REMOVEMATCH && (!value || !wcscmp( data, value )))
6920 action = 0x4;
6921 res = RegDeleteValueW(env, name);
6922 if (res != ERROR_SUCCESS)
6923 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6924 goto done;
6927 for (;;)
6929 while (*q && *q != ';') q++;
6930 len = q - p;
6931 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
6932 (!p[len] || p[len] == ';'))
6934 found = 1;
6935 break;
6937 if (!*q) break;
6938 p = ++q;
6941 if (found)
6943 TRACE("string already set\n");
6944 goto done;
6947 size = (len_value + 1 + lstrlenW( data ) + 1) * sizeof(WCHAR);
6948 if (!(p = newval = malloc( size )))
6950 res = ERROR_OUTOFMEMORY;
6951 goto done;
6954 if (flags & ENV_MOD_PREFIX)
6956 memcpy( newval, value, len_value * sizeof(WCHAR) );
6957 newval[len_value] = ';';
6958 p = newval + len_value + 1;
6959 action |= 0x80000000;
6962 lstrcpyW( p, data );
6964 if (flags & ENV_MOD_APPEND)
6966 p += lstrlenW( data );
6967 *p++ = ';';
6968 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
6969 action |= 0x40000000;
6972 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6973 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
6974 if (res)
6976 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6979 done:
6980 uirow = MSI_CreateRecord( 3 );
6981 MSI_RecordSetStringW( uirow, 1, name );
6982 MSI_RecordSetStringW( uirow, 2, newval );
6983 MSI_RecordSetInteger( uirow, 3, action );
6984 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6985 msiobj_release( &uirow->hdr );
6987 if (env) RegCloseKey(env);
6988 free(deformatted);
6989 free(data);
6990 free(newval);
6991 return res;
6994 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6996 MSIQUERY *view;
6997 UINT rc;
6999 if (package->script == SCRIPT_NONE)
7000 return msi_schedule_action(package, SCRIPT_INSTALL, L"WriteEnvironmentStrings");
7002 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Environment`", &view);
7003 if (rc != ERROR_SUCCESS)
7004 return ERROR_SUCCESS;
7006 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7007 msiobj_release(&view->hdr);
7008 return rc;
7011 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7013 MSIPACKAGE *package = param;
7014 LPCWSTR name, value, component;
7015 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7016 DWORD flags, type, size, len, len_value = 0, len_new_value;
7017 HKEY env = NULL;
7018 MSICOMPONENT *comp;
7019 MSIRECORD *uirow;
7020 int action = 0;
7021 LONG res;
7022 UINT r;
7024 component = MSI_RecordGetString( rec, 4 );
7025 comp = msi_get_loaded_component( package, component );
7026 if (!comp)
7027 return ERROR_SUCCESS;
7029 comp->Action = msi_get_component_action( package, comp );
7030 if (comp->Action != INSTALLSTATE_ABSENT)
7032 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7033 return ERROR_SUCCESS;
7035 name = MSI_RecordGetString( rec, 2 );
7036 value = MSI_RecordGetString( rec, 3 );
7038 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7040 r = env_parse_flags( &name, &value, &flags );
7041 if (r != ERROR_SUCCESS)
7042 return r;
7044 if (!(flags & ENV_ACT_REMOVE))
7046 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7047 return ERROR_SUCCESS;
7050 if (value)
7052 DWORD len = deformat_string( package, value, &deformatted );
7053 if (!deformatted)
7055 res = ERROR_OUTOFMEMORY;
7056 goto done;
7059 if (len)
7061 value = deformatted;
7062 if (flags & ENV_MOD_PREFIX)
7064 p = wcsrchr( value, ';' );
7065 len_value = p - value;
7067 else if (flags & ENV_MOD_APPEND)
7069 value = wcschr( value, ';' ) + 1;
7070 len_value = lstrlenW( value );
7072 else len_value = lstrlenW( value );
7074 else
7076 value = NULL;
7080 r = open_env_key( flags, &env );
7081 if (r != ERROR_SUCCESS)
7083 r = ERROR_SUCCESS;
7084 goto done;
7087 if (flags & ENV_MOD_MACHINE)
7088 action |= 0x20000000;
7090 size = 0;
7091 type = REG_SZ;
7092 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7093 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7094 goto done;
7096 if (!(new_value = malloc( size ))) goto done;
7098 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7099 if (res != ERROR_SUCCESS)
7100 goto done;
7102 len_new_value = size / sizeof(WCHAR) - 1;
7103 p = q = new_value;
7104 for (;;)
7106 while (*q && *q != ';') q++;
7107 len = q - p;
7108 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7110 if (*q == ';') q++;
7111 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7112 break;
7114 if (!*q) break;
7115 p = ++q;
7118 if (!new_value[0] || !value)
7120 TRACE("removing %s\n", debugstr_w(name));
7121 res = RegDeleteValueW( env, name );
7122 if (res != ERROR_SUCCESS)
7123 WARN( "failed to delete value %s (%ld)\n", debugstr_w(name), res );
7125 else
7127 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7128 size = (lstrlenW( new_value ) + 1) * sizeof(WCHAR);
7129 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7130 if (res != ERROR_SUCCESS)
7131 WARN( "failed to set %s to %s (%ld)\n", debugstr_w(name), debugstr_w(new_value), res );
7134 done:
7135 uirow = MSI_CreateRecord( 3 );
7136 MSI_RecordSetStringW( uirow, 1, name );
7137 MSI_RecordSetStringW( uirow, 2, value );
7138 MSI_RecordSetInteger( uirow, 3, action );
7139 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7140 msiobj_release( &uirow->hdr );
7142 if (env) RegCloseKey( env );
7143 free( deformatted );
7144 free( new_value );
7145 return r;
7148 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7150 MSIQUERY *view;
7151 UINT rc;
7153 if (package->script == SCRIPT_NONE)
7154 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveEnvironmentStrings");
7156 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Environment`", &view );
7157 if (rc != ERROR_SUCCESS)
7158 return ERROR_SUCCESS;
7160 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7161 msiobj_release( &view->hdr );
7162 return rc;
7165 UINT msi_validate_product_id( MSIPACKAGE *package )
7167 LPWSTR key, template, id;
7168 UINT r = ERROR_SUCCESS;
7170 id = msi_dup_property( package->db, L"ProductID" );
7171 if (id)
7173 free( id );
7174 return ERROR_SUCCESS;
7176 template = msi_dup_property( package->db, L"PIDTemplate" );
7177 key = msi_dup_property( package->db, L"PIDKEY" );
7178 if (key && template)
7180 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7181 r = msi_set_property( package->db, L"ProductID", key, -1 );
7183 free( template );
7184 free( key );
7185 return r;
7188 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7190 return msi_validate_product_id( package );
7193 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7195 TRACE("\n");
7196 package->need_reboot_at_end = 1;
7197 return ERROR_SUCCESS;
7200 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7202 MSIRECORD *uirow;
7203 int space = msi_get_property_int( package->db, L"AVAILABLEFREEREG", 0 );
7205 TRACE("%p %d kilobytes\n", package, space);
7207 uirow = MSI_CreateRecord( 1 );
7208 MSI_RecordSetInteger( uirow, 1, space );
7209 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7210 msiobj_release( &uirow->hdr );
7212 return ERROR_SUCCESS;
7215 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7217 TRACE("%p\n", package);
7219 msi_set_property( package->db, L"RollbackDisabled", L"1", -1 );
7220 return ERROR_SUCCESS;
7223 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7225 FIXME("%p\n", package);
7226 return ERROR_SUCCESS;
7229 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7231 MSIQUERY *view;
7232 UINT r;
7233 DWORD count;
7235 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCDriver`", &view );
7236 if (r == ERROR_SUCCESS)
7238 count = 0;
7239 r = MSI_IterateRecords( view, &count, NULL, package );
7240 msiobj_release( &view->hdr );
7241 if (r != ERROR_SUCCESS)
7242 return r;
7243 if (count) FIXME( "ignored %lu rows in ODBCDriver table\n", count );
7245 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `ODBCTranslator`", &view );
7246 if (r == ERROR_SUCCESS)
7248 count = 0;
7249 r = MSI_IterateRecords( view, &count, NULL, package );
7250 msiobj_release( &view->hdr );
7251 if (r != ERROR_SUCCESS)
7252 return r;
7253 if (count) FIXME( "ignored %lu rows in ODBCTranslator table\n", count );
7255 return ERROR_SUCCESS;
7258 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7260 MSIPACKAGE *package = param;
7261 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7262 int attrs = MSI_RecordGetInteger( rec, 5 );
7263 UINT len = ARRAY_SIZE( L"msiexec /qn /i %s REMOVE=%s" );
7264 WCHAR *product, *features, *cmd;
7265 STARTUPINFOW si;
7266 PROCESS_INFORMATION info;
7267 BOOL ret;
7269 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7270 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7272 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7274 len += lstrlenW( product );
7275 if (features)
7276 len += lstrlenW( features );
7277 else
7278 len += ARRAY_SIZE( L"ALL" );
7280 if (!(cmd = malloc( len * sizeof(WCHAR) )))
7282 free( product );
7283 free( features );
7284 return ERROR_OUTOFMEMORY;
7286 swprintf( cmd, len, L"msiexec /qn /i %s REMOVE=%s", product, features ? features : L"ALL" );
7287 free( product );
7288 free( features );
7290 memset( &si, 0, sizeof(STARTUPINFOW) );
7291 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7292 free( cmd );
7293 if (!ret) return GetLastError();
7294 CloseHandle( info.hThread );
7296 WaitForSingleObject( info.hProcess, INFINITE );
7297 CloseHandle( info.hProcess );
7298 return ERROR_SUCCESS;
7301 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7303 MSIQUERY *view;
7304 UINT r;
7306 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Upgrade`", &view );
7307 if (r == ERROR_SUCCESS)
7309 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7310 msiobj_release( &view->hdr );
7311 if (r != ERROR_SUCCESS)
7312 return r;
7314 return ERROR_SUCCESS;
7317 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7319 MSIPACKAGE *package = param;
7320 int attributes = MSI_RecordGetInteger( rec, 5 );
7322 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7324 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7325 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7326 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7327 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7328 HKEY hkey;
7329 UINT r;
7331 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7333 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7334 if (r != ERROR_SUCCESS)
7335 return ERROR_SUCCESS;
7337 else
7339 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7340 if (r != ERROR_SUCCESS)
7341 return ERROR_SUCCESS;
7343 RegCloseKey( hkey );
7345 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7346 debugstr_w(upgrade_code), debugstr_w(version_min),
7347 debugstr_w(version_max), debugstr_w(language));
7349 return ERROR_SUCCESS;
7352 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7354 MSIQUERY *view;
7355 UINT r;
7357 if (msi_get_property_int( package->db, L"Installed", 0 ))
7359 TRACE("product is installed, skipping action\n");
7360 return ERROR_SUCCESS;
7362 if (msi_get_property_int( package->db, L"Preselected", 0 ))
7364 TRACE("Preselected property is set, not migrating feature states\n");
7365 return ERROR_SUCCESS;
7367 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Upgrade`", &view );
7368 if (r == ERROR_SUCCESS)
7370 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7371 msiobj_release( &view->hdr );
7372 if (r != ERROR_SUCCESS)
7373 return r;
7375 return ERROR_SUCCESS;
7378 static void bind_image( MSIPACKAGE *package, const char *filename, const char *path )
7380 msi_disable_fs_redirection( package );
7381 if (!BindImage( filename, path, NULL )) WARN( "failed to bind image %lu\n", GetLastError() );
7382 msi_revert_fs_redirection( package );
7385 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7387 UINT i;
7388 MSIFILE *file;
7389 MSIPACKAGE *package = param;
7390 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7391 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7392 char *filenameA, *pathA;
7393 WCHAR *pathW, **path_list;
7395 if (!(file = msi_get_loaded_file( package, key )))
7397 WARN("file %s not found\n", debugstr_w(key));
7398 return ERROR_SUCCESS;
7400 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7402 path_list = msi_split_string( paths, ';' );
7403 if (!path_list) bind_image( package, filenameA, NULL );
7404 else
7406 for (i = 0; path_list[i] && path_list[i][0]; i++)
7408 deformat_string( package, path_list[i], &pathW );
7409 if ((pathA = strdupWtoA( pathW )))
7411 bind_image( package, filenameA, pathA );
7412 free( pathA );
7414 free( pathW );
7417 free( path_list );
7418 free( filenameA );
7420 return ERROR_SUCCESS;
7423 static UINT ACTION_BindImage( MSIPACKAGE *package )
7425 MSIQUERY *view;
7426 UINT r;
7428 r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `BindImage`", &view );
7429 if (r == ERROR_SUCCESS)
7431 MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7432 msiobj_release( &view->hdr );
7434 return ERROR_SUCCESS;
7437 static UINT unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7439 MSIQUERY *view;
7440 DWORD count = 0;
7441 UINT r;
7443 r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `%s`", table );
7444 if (r == ERROR_SUCCESS)
7446 r = MSI_IterateRecords(view, &count, NULL, package);
7447 msiobj_release(&view->hdr);
7448 if (r != ERROR_SUCCESS)
7449 return r;
7451 if (count) FIXME( "%s: ignored %lu rows from %s\n", action, count, debugstr_w(table) );
7452 return ERROR_SUCCESS;
7455 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7457 return unimplemented_action_stub( package, "IsolateComponents", L"IsolateComponent" );
7460 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7462 return unimplemented_action_stub( package, "RMCCPSearch", L"CCPSearch" );
7465 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7467 return unimplemented_action_stub( package, "RegisterComPlus", L"Complus" );
7470 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7472 return unimplemented_action_stub( package, "UnregisterComPlus", L"Complus" );
7475 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7477 return unimplemented_action_stub( package, "InstallSFPCatalogFile", L"SFPCatalog" );
7480 static const struct
7482 const WCHAR *action;
7483 const UINT description;
7484 const UINT template;
7485 UINT (*handler)(MSIPACKAGE *);
7486 const WCHAR *action_rollback;
7488 StandardActions[] =
7490 { L"AllocateRegistrySpace", IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7491 { L"AppSearch", IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7492 { L"BindImage", IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7493 { L"CCPSearch", IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7494 { L"CostFinalize", IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7495 { L"CostInitialize", IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7496 { L"CreateFolders", IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, L"RemoveFolders" },
7497 { L"CreateShortcuts", IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, L"RemoveShortcuts" },
7498 { L"DeleteServices", IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, L"InstallServices" },
7499 { L"DisableRollback", 0, 0, ACTION_DisableRollback, NULL },
7500 { L"DuplicateFiles", IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, L"RemoveDuplicateFiles" },
7501 { L"ExecuteAction", 0, 0, ACTION_ExecuteAction, NULL },
7502 { L"FileCost", IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7503 { L"FindRelatedProducts", IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7504 { L"ForceReboot", 0, 0, ACTION_ForceReboot, NULL },
7505 { L"InstallAdminPackage", IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7506 { L"InstallExecute", 0, 0, ACTION_InstallExecute, NULL },
7507 { L"InstallExecuteAgain", 0, 0, ACTION_InstallExecute, NULL },
7508 { L"InstallFiles", IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, L"RemoveFiles" },
7509 { L"InstallFinalize", 0, 0, ACTION_InstallFinalize, NULL },
7510 { L"InstallInitialize", 0, 0, ACTION_InstallInitialize, NULL },
7511 { L"InstallODBC", IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, L"RemoveODBC" },
7512 { L"InstallServices", IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, L"DeleteServices" },
7513 { L"InstallSFPCatalogFile", IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7514 { L"InstallValidate", IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7515 { L"IsolateComponents", 0, 0, ACTION_IsolateComponents, NULL },
7516 { L"LaunchConditions", IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7517 { L"MigrateFeutureStates", IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7518 { L"MoveFiles", IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7519 { L"MsiPublishAssemblies", IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, L"MsiUnpublishAssemblies" },
7520 { L"MsiUnpublishAssemblies", IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, L"MsiPublishAssemblies" },
7521 { L"PatchFiles", IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7522 { L"ProcessComponents", IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, L"ProcessComponents" },
7523 { L"PublishComponents", IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, L"UnpublishComponents" },
7524 { L"PublishFeatures", IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, L"UnpublishFeatures" },
7525 { L"PublishProduct", IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, L"UnpublishProduct" },
7526 { L"RegisterClassInfo", IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, L"UnregisterClassInfo" },
7527 { L"RegisterComPlus", IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, L"UnregisterComPlus" },
7528 { L"RegisterExtensionInfo", IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, L"UnregisterExtensionInfo" },
7529 { L"RegisterFonts", IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, L"UnregisterFonts" },
7530 { L"RegisterMIMEInfo", IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, L"UnregisterMIMEInfo" },
7531 { L"RegisterProduct", IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
7532 { L"RegisterProgIdInfo", IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, L"UnregisterProgIdInfo" },
7533 { L"RegisterTypeLibraries", IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, L"UnregisterTypeLibraries" },
7534 { L"RegisterUser", IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
7535 { L"RemoveDuplicateFiles", IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, L"DuplicateFiles" },
7536 { L"RemoveEnvironmentStrings", IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, L"WriteEnvironmentStrings" },
7537 { L"RemoveExistingProducts", IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
7538 { L"RemoveFiles", IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, L"InstallFiles" },
7539 { L"RemoveFolders", IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, L"CreateFolders" },
7540 { L"RemoveIniValues", IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, L"WriteIniValues" },
7541 { L"RemoveODBC", IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, L"InstallODBC" },
7542 { L"RemoveRegistryValues", IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, L"WriteRegistryValues" },
7543 { L"RemoveShortcuts", IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, L"CreateShortcuts" },
7544 { L"ResolveSource", 0, 0, ACTION_ResolveSource, NULL },
7545 { L"RMCCPSearch", IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
7546 { L"ScheduleReboot", 0, 0, ACTION_ScheduleReboot, NULL },
7547 { L"SelfRegModules", IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, L"SelfUnregModules" },
7548 { L"SelfUnregModules", IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, L"SelfRegModules" },
7549 { L"SetODBCFolders", IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
7550 { L"StartServices", IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, L"StopServices" },
7551 { L"StopServices", IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, L"StartServices" },
7552 { L"UnpublishComponents", IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, L"PublishComponents" },
7553 { L"UnpublishFeatures", IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, L"PublishFeatures" },
7554 { L"UnpublishProduct", IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7555 { L"UnregisterClassInfo", IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, L"RegisterClassInfo" },
7556 { L"UnregisterComPlus", IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, L"RegisterComPlus" },
7557 { L"UnregisterExtensionInfo", IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, L"RegisterExtensionInfo" },
7558 { L"UnregisterFonts", IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, L"RegisterFonts" },
7559 { L"UnregisterMIMEInfo", IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, L"RegisterMIMEInfo" },
7560 { L"UnregisterProgIdInfo", IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, L"RegisterProgIdInfo" },
7561 { L"UnregisterTypeLibraries", IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, L"RegisterTypeLibraries" },
7562 { L"ValidateProductID", 0, 0, ACTION_ValidateProductID, NULL },
7563 { L"WriteEnvironmentStrings", IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, L"RemoveEnvironmentStrings" },
7564 { L"WriteIniValues", IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, L"RemoveIniValues" },
7565 { L"WriteRegistryValues", IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, L"RemoveRegistryValues" },
7566 { L"INSTALL", 0, 0, ACTION_INSTALL, NULL },
7567 { 0 }
7570 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
7572 UINT rc = ERROR_FUNCTION_NOT_CALLED;
7573 UINT i;
7575 i = 0;
7576 while (StandardActions[i].action != NULL)
7578 if (!wcscmp( StandardActions[i].action, action ))
7580 WCHAR description[100] = {0}, template[100] = {0};
7582 if (StandardActions[i].description != 0)
7583 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
7584 if (StandardActions[i].template != 0)
7585 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
7587 ui_actionstart(package, action, description, template);
7588 if (StandardActions[i].handler)
7590 ui_actioninfo( package, action, TRUE, 0 );
7591 rc = StandardActions[i].handler( package );
7592 ui_actioninfo( package, action, FALSE, !rc );
7594 if (StandardActions[i].action_rollback && !package->need_rollback)
7596 TRACE("scheduling rollback action\n");
7597 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7600 else
7602 FIXME("unhandled standard action %s\n", debugstr_w(action));
7603 rc = ERROR_SUCCESS;
7605 break;
7607 i++;
7610 return rc;
7613 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
7615 UINT rc;
7617 TRACE("Performing action (%s)\n", debugstr_w(action));
7619 package->action_progress_increment = 0;
7620 rc = ACTION_HandleStandardAction(package, action);
7622 if (rc == ERROR_FUNCTION_NOT_CALLED)
7623 rc = ACTION_HandleCustomAction(package, action);
7625 if (rc == ERROR_FUNCTION_NOT_CALLED)
7626 WARN("unhandled msi action %s\n", debugstr_w(action));
7628 return rc;
7631 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7633 UINT rc = ERROR_SUCCESS;
7634 MSIRECORD *row;
7636 if (needs_ui_sequence(package))
7637 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `InstallUISequence` WHERE `Sequence` = %d", seq);
7638 else
7639 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `InstallExecuteSequence` WHERE `Sequence` = %d", seq);
7641 if (row)
7643 LPCWSTR action, cond;
7645 TRACE("Running the actions\n");
7647 /* check conditions */
7648 cond = MSI_RecordGetString(row, 2);
7650 /* this is a hack to skip errors in the condition code */
7651 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7653 msiobj_release(&row->hdr);
7654 return ERROR_SUCCESS;
7657 action = MSI_RecordGetString(row, 1);
7658 if (!action)
7660 ERR("failed to fetch action\n");
7661 msiobj_release(&row->hdr);
7662 return ERROR_FUNCTION_FAILED;
7665 rc = ACTION_PerformAction(package, action);
7667 msiobj_release(&row->hdr);
7670 return rc;
7673 /****************************************************
7674 * TOP level entry points
7675 *****************************************************/
7677 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7678 LPCWSTR szCommandLine )
7680 WCHAR *reinstall = NULL, *productcode, *action;
7681 UINT rc;
7682 DWORD len = 0;
7684 if (szPackagePath)
7686 LPWSTR p, dir;
7687 LPCWSTR file;
7689 dir = wcsdup(szPackagePath);
7690 p = wcsrchr(dir, '\\');
7691 if (p)
7693 *(++p) = 0;
7694 file = szPackagePath + (p - dir);
7696 else
7698 free(dir);
7699 dir = malloc(MAX_PATH * sizeof(WCHAR));
7700 GetCurrentDirectoryW(MAX_PATH, dir);
7701 lstrcatW(dir, L"\\");
7702 file = szPackagePath;
7705 free(package->PackagePath);
7706 package->PackagePath = malloc((wcslen(dir) + wcslen(file) + 1) * sizeof(WCHAR));
7707 if (!package->PackagePath)
7709 free(dir);
7710 return ERROR_OUTOFMEMORY;
7713 lstrcpyW(package->PackagePath, dir);
7714 lstrcatW(package->PackagePath, file);
7715 free(dir);
7717 msi_set_sourcedir_props(package, FALSE);
7720 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7721 if (rc != ERROR_SUCCESS)
7722 return rc;
7724 msi_apply_transforms( package );
7725 msi_apply_patches( package );
7727 if (msi_get_property( package->db, L"ACTION", NULL, &len ))
7728 msi_set_property( package->db, L"ACTION", L"INSTALL", -1 );
7729 action = msi_dup_property( package->db, L"ACTION" );
7730 CharUpperW(action);
7732 msi_set_original_database_property( package->db, szPackagePath );
7733 msi_parse_command_line( package, szCommandLine, FALSE );
7734 msi_adjust_privilege_properties( package );
7735 msi_set_context( package );
7737 productcode = msi_dup_property( package->db, L"ProductCode" );
7738 if (wcsicmp( productcode, package->ProductCode ))
7740 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7741 free( package->ProductCode );
7742 package->ProductCode = productcode;
7744 else free( productcode );
7746 if (msi_get_property_int( package->db, L"DISABLEROLLBACK", 0 ))
7748 TRACE("disabling rollback\n");
7749 msi_set_property( package->db, L"RollbackDisabled", L"1", -1 );
7752 rc = ACTION_PerformAction(package, action);
7754 /* process the ending type action */
7755 if (rc == ERROR_SUCCESS)
7756 ACTION_PerformActionSequence(package, -1);
7757 else if (rc == ERROR_INSTALL_USEREXIT)
7758 ACTION_PerformActionSequence(package, -2);
7759 else if (rc == ERROR_INSTALL_SUSPEND)
7760 ACTION_PerformActionSequence(package, -4);
7761 else /* failed */
7763 ACTION_PerformActionSequence(package, -3);
7764 if (!msi_get_property_int( package->db, L"RollbackDisabled", 0 ))
7766 package->need_rollback = TRUE;
7770 /* finish up running custom actions */
7771 ACTION_FinishCustomActions(package);
7773 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, L"REINSTALL" )))
7775 WARN("installation failed, running rollback script\n");
7776 execute_script( package, SCRIPT_ROLLBACK );
7778 free( reinstall );
7779 free( action );
7781 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7782 return ERROR_SUCCESS_REBOOT_REQUIRED;
7784 return rc;