winealsa: Fix AudioRenderClient Get/ReleaseBuffer protocol.
[wine] / dlls / msi / action.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004,2005 Aric Stewart for CodeWeavers
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
42
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
45
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
47
48 static const WCHAR szCreateFolders[] =
49     {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51     {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53     {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] = 
55     {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] = 
57     {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] = 
59     {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] = 
61     {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] = 
63     {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] = 
65     {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] = 
67     {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] = 
69     {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] = 
71     {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] = 
73     {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] = 
75     {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] = 
77     {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] = 
79     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] = 
81     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] = 
83     {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] = 
85     {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87     {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] = 
89     {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] = 
91     {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] = 
93     {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] = 
95     {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] = 
97     {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] = 
99     {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] = 
101     {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] = 
103     {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105     {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] = 
107     {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] = 
109     {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] = 
111     {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] = 
113     {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115     {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117     {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119     {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121     {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123     {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125     {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127     {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129     {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131     {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133     {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135     {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137     {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139     {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141     {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143     {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145     {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147     {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149     {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151     {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153     {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155     {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
156
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
158 {
159     static const WCHAR Query_t[] = 
160         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161          '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162          'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=', 
163          ' ','\'','%','s','\'',0};
164     MSIRECORD * row;
165
166     row = MSI_QueryGetRecord( package->db, Query_t, action );
167     if (!row)
168         return;
169     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170     msiobj_release(&row->hdr);
171 }
172
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, 
174                           UINT rc)
175 {
176     MSIRECORD * row;
177     static const WCHAR template_s[]=
178         {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179          '%','s', '.',0};
180     static const WCHAR template_e[]=
181         {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182          '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183          '%','i','.',0};
184     static const WCHAR format[] = 
185         {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186     WCHAR message[1024];
187     WCHAR timet[0x100];
188
189     GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190     if (start)
191         sprintfW(message,template_s,timet,action);
192     else
193         sprintfW(message,template_e,timet,action,rc);
194     
195     row = MSI_CreateRecord(1);
196     MSI_RecordSetStringW(row,1,message);
197  
198     MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199     msiobj_release(&row->hdr);
200 }
201
202 enum parse_state
203 {
204     state_whitespace,
205     state_token,
206     state_quote
207 };
208
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
210 {
211     enum parse_state state = state_quote;
212     const WCHAR *p;
213     WCHAR *out = value;
214     int ignore, in_quotes = 0, count = 0, len = 0;
215
216     for (p = str; *p; p++)
217     {
218         ignore = 0;
219         switch (state)
220         {
221         case state_whitespace:
222             switch (*p)
223             {
224             case ' ':
225                 if (!count) goto done;
226                 in_quotes = 1;
227                 ignore = 1;
228                 len++;
229                 break;
230             case '"':
231                 state = state_quote;
232                 if (in_quotes && p[1] != '\"') count--;
233                 else count++;
234                 break;
235             default:
236                 state = state_token;
237                 if (!count) in_quotes = 0;
238                 else in_quotes = 1;
239                 len++;
240                 break;
241             }
242             break;
243
244         case state_token:
245             switch (*p)
246             {
247             case '"':
248                 state = state_quote;
249                 if (in_quotes) count--;
250                 else count++;
251                 break;
252             case ' ':
253                 state = state_whitespace;
254                 if (!count) goto done;
255                 in_quotes = 1;
256                 len++;
257                 break;
258             default:
259                 if (!count) in_quotes = 0;
260                 else in_quotes = 1;
261                 len++;
262                 break;
263             }
264             break;
265
266         case state_quote:
267             switch (*p)
268             {
269             case '"':
270                 if (in_quotes && p[1] != '\"') count--;
271                 else count++;
272                 break;
273             case ' ':
274                 state = state_whitespace;
275                 if (!count || (count > 1 && !len)) goto done;
276                 in_quotes = 1;
277                 len++;
278                 break;
279             default:
280                 state = state_token;
281                 if (!count) in_quotes = 0;
282                 else in_quotes = 1;
283                 len++;
284                 break;
285             }
286             break;
287
288         default: break;
289         }
290         if (!ignore) *out++ = *p;
291     }
292
293 done:
294     if (!len) *value = 0;
295     else *out = 0;
296
297     *quotes = count;
298     return p - str;
299 }
300
301 static void remove_quotes( WCHAR *str )
302 {
303     WCHAR *p = str;
304     int len = strlenW( str );
305
306     while ((p = strchrW( p, '"' )))
307     {
308         memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
309         p++;
310     }
311 }
312
313 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
314                              BOOL preserve_case )
315 {
316     LPCWSTR ptr, ptr2;
317     int num_quotes;
318     DWORD len;
319     WCHAR *prop, *val;
320     UINT r;
321
322     if (!szCommandLine)
323         return ERROR_SUCCESS;
324
325     ptr = szCommandLine;
326     while (*ptr)
327     {
328         while (*ptr == ' ') ptr++;
329         if (!*ptr) break;
330
331         ptr2 = strchrW( ptr, '=' );
332         if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
333  
334         len = ptr2 - ptr;
335         if (!len) return ERROR_INVALID_COMMAND_LINE;
336
337         prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
338         memcpy( prop, ptr, len * sizeof(WCHAR) );
339         prop[len] = 0;
340         if (!preserve_case) struprW( prop );
341
342         ptr2++;
343         while (*ptr2 == ' ') ptr2++;
344
345         num_quotes = 0;
346         val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
347         len = parse_prop( ptr2, val, &num_quotes );
348         if (num_quotes % 2)
349         {
350             WARN("unbalanced quotes\n");
351             msi_free( val );
352             msi_free( prop );
353             return ERROR_INVALID_COMMAND_LINE;
354         }
355         remove_quotes( val );
356         TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
357
358         r = msi_set_property( package->db, prop, val );
359         if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
360             msi_reset_folders( package, TRUE );
361
362         msi_free( val );
363         msi_free( prop );
364
365         ptr = ptr2 + len;
366     }
367
368     return ERROR_SUCCESS;
369 }
370
371 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
372 {
373     LPCWSTR pc;
374     LPWSTR p, *ret = NULL;
375     UINT count = 0;
376
377     if (!str)
378         return ret;
379
380     /* count the number of substrings */
381     for ( pc = str, count = 0; pc; count++ )
382     {
383         pc = strchrW( pc, sep );
384         if (pc)
385             pc++;
386     }
387
388     /* allocate space for an array of substring pointers and the substrings */
389     ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
390                      (lstrlenW(str)+1) * sizeof(WCHAR) );
391     if (!ret)
392         return ret;
393
394     /* copy the string and set the pointers */
395     p = (LPWSTR) &ret[count+1];
396     lstrcpyW( p, str );
397     for( count = 0; (ret[count] = p); count++ )
398     {
399         p = strchrW( p, sep );
400         if (p)
401             *p++ = 0;
402     }
403
404     return ret;
405 }
406
407 static BOOL ui_sequence_exists( MSIPACKAGE *package )
408 {
409     static const WCHAR query [] = {
410         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
411         '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
412         'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
413         'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
414     MSIQUERY *view;
415     UINT rc;
416
417     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
418     if (rc == ERROR_SUCCESS)
419     {
420         msiobj_release(&view->hdr);
421         return TRUE;
422     }
423     return FALSE;
424 }
425
426 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
427 {
428     LPWSTR source, check;
429
430     if (msi_get_property_int( package->db, szInstalled, 0 ))
431     {
432         HKEY hkey;
433
434         MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
435         source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
436         RegCloseKey( hkey );
437     }
438     else
439     {
440         LPWSTR p, db;
441         DWORD len;
442
443         db = msi_dup_property( package->db, szOriginalDatabase );
444         if (!db)
445             return ERROR_OUTOFMEMORY;
446
447         p = strrchrW( db, '\\' );
448         if (!p)
449         {
450             p = strrchrW( db, '/' );
451             if (!p)
452             {
453                 msi_free(db);
454                 return ERROR_SUCCESS;
455             }
456         }
457
458         len = p - db + 2;
459         source = msi_alloc( len * sizeof(WCHAR) );
460         lstrcpynW( source, db, len );
461         msi_free( db );
462     }
463
464     check = msi_dup_property( package->db, szSourceDir );
465     if (!check || replace)
466     {
467         UINT r = msi_set_property( package->db, szSourceDir, source );
468         if (r == ERROR_SUCCESS)
469             msi_reset_folders( package, TRUE );
470     }
471     msi_free( check );
472
473     check = msi_dup_property( package->db, szSOURCEDIR );
474     if (!check || replace)
475         msi_set_property( package->db, szSOURCEDIR, source );
476
477     msi_free( check );
478     msi_free( source );
479
480     return ERROR_SUCCESS;
481 }
482
483 static BOOL needs_ui_sequence(MSIPACKAGE *package)
484 {
485     INT level = msi_get_property_int(package->db, szUILevel, 0);
486     return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
487 }
488
489 UINT msi_set_context(MSIPACKAGE *package)
490 {
491     UINT r = msi_locate_product( package->ProductCode, &package->Context );
492     if (r != ERROR_SUCCESS)
493     {
494         int num = msi_get_property_int( package->db, szAllUsers, 0 );
495         if (num == 1 || num == 2)
496             package->Context = MSIINSTALLCONTEXT_MACHINE;
497         else
498             package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
499     }
500     return ERROR_SUCCESS;
501 }
502
503 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
504 {
505     UINT rc;
506     LPCWSTR cond, action;
507     MSIPACKAGE *package = param;
508
509     action = MSI_RecordGetString(row,1);
510     if (!action)
511     {
512         ERR("Error is retrieving action name\n");
513         return ERROR_FUNCTION_FAILED;
514     }
515
516     /* check conditions */
517     cond = MSI_RecordGetString(row,2);
518
519     /* this is a hack to skip errors in the condition code */
520     if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
521     {
522         TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
523         return ERROR_SUCCESS;
524     }
525
526     if (needs_ui_sequence(package))
527         rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
528     else
529         rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
530
531     msi_dialog_check_messages( NULL );
532
533     if (package->CurrentInstallState != ERROR_SUCCESS)
534         rc = package->CurrentInstallState;
535
536     if (rc == ERROR_FUNCTION_NOT_CALLED)
537         rc = ERROR_SUCCESS;
538
539     if (rc != ERROR_SUCCESS)
540         ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
541
542     return rc;
543 }
544
545 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
546 {
547     static const WCHAR query[] = {
548         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
549          ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
550          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
551          '`','S','e','q','u','e','n','c','e','`',0};
552     MSIQUERY *view;
553     UINT r;
554
555     TRACE("%p %s\n", package, debugstr_w(table));
556
557     r = MSI_OpenQuery( package->db, &view, query, table );
558     if (r == ERROR_SUCCESS)
559     {
560         r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
561         msiobj_release(&view->hdr);
562     }
563     return r;
564 }
565
566 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
567 {
568     static const WCHAR query[] = {
569         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
570         '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
571         'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
572         '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
573         'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
574     static const WCHAR query_validate[] = {
575         'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
576         ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
577         'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
578         'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
579         ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
580     MSIQUERY *view;
581     INT seq = 0;
582     UINT rc;
583
584     if (package->script->ExecuteSequenceRun)
585     {
586         TRACE("Execute Sequence already Run\n");
587         return ERROR_SUCCESS;
588     }
589
590     package->script->ExecuteSequenceRun = TRUE;
591
592     /* get the sequence number */
593     if (UIran)
594     {
595         MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
596         if (!row) return ERROR_FUNCTION_FAILED;
597         seq = MSI_RecordGetInteger(row,1);
598         msiobj_release(&row->hdr);
599     }
600     rc = MSI_OpenQuery(package->db, &view, query, seq);
601     if (rc == ERROR_SUCCESS)
602     {
603         TRACE("Running the actions\n");
604
605         msi_set_property(package->db, szSourceDir, NULL);
606         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
607         msiobj_release(&view->hdr);
608     }
609     return rc;
610 }
611
612 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
613 {
614     static const WCHAR query[] = {
615         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
616         '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
617         'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
618         'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
619     MSIQUERY *view;
620     UINT rc;
621
622     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
623     if (rc == ERROR_SUCCESS)
624     {
625         TRACE("Running the actions\n"); 
626         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
627         msiobj_release(&view->hdr);
628     }
629     return rc;
630 }
631
632 /********************************************************
633  * ACTION helper functions and functions that perform the actions
634  *******************************************************/
635 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
636                                        UINT* rc, UINT script, BOOL force )
637 {
638     BOOL ret=FALSE;
639     UINT arc;
640
641     arc = ACTION_CustomAction(package, action, script, force);
642
643     if (arc != ERROR_CALL_NOT_IMPLEMENTED)
644     {
645         *rc = arc;
646         ret = TRUE;
647     }
648     return ret;
649 }
650
651 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
652 {
653     MSICOMPONENT *comp;
654
655     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
656     {
657         if (!strcmpW( Component, comp->Component )) return comp;
658     }
659     return NULL;
660 }
661
662 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
663 {
664     MSIFEATURE *feature;
665
666     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
667     {
668         if (!strcmpW( Feature, feature->Feature )) return feature;
669     }
670     return NULL;
671 }
672
673 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
674 {
675     MSIFILE *file;
676
677     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
678     {
679         if (!strcmpW( key, file->File )) return file;
680     }
681     return NULL;
682 }
683
684 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
685 {
686     MSIFILEPATCH *patch;
687
688     /* FIXME: There might be more than one patch */
689     LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
690     {
691         if (!strcmpW( key, patch->File->File )) return patch;
692     }
693     return NULL;
694 }
695
696 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
697 {
698     MSIFOLDER *folder;
699
700     LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
701     {
702         if (!strcmpW( dir, folder->Directory )) return folder;
703     }
704     return NULL;
705 }
706
707 /*
708  * Recursively create all directories in the path.
709  * shamelessly stolen from setupapi/queue.c
710  */
711 BOOL msi_create_full_path( const WCHAR *path )
712 {
713     BOOL ret = TRUE;
714     WCHAR *new_path;
715     int len;
716
717     new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
718     strcpyW( new_path, path );
719
720     while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
721     new_path[len - 1] = 0;
722
723     while (!CreateDirectoryW( new_path, NULL ))
724     {
725         WCHAR *slash;
726         DWORD last_error = GetLastError();
727         if (last_error == ERROR_ALREADY_EXISTS) break;
728         if (last_error != ERROR_PATH_NOT_FOUND)
729         {
730             ret = FALSE;
731             break;
732         }
733         if (!(slash = strrchrW( new_path, '\\' )))
734         {
735             ret = FALSE;
736             break;
737         }
738         len = slash - new_path;
739         new_path[len] = 0;
740         if (!msi_create_full_path( new_path ))
741         {
742             ret = FALSE;
743             break;
744         }
745         new_path[len] = '\\';
746     }
747     msi_free( new_path );
748     return ret;
749 }
750
751 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
752 {
753     MSIRECORD *row;
754
755     row = MSI_CreateRecord( 4 );
756     MSI_RecordSetInteger( row, 1, a );
757     MSI_RecordSetInteger( row, 2, b );
758     MSI_RecordSetInteger( row, 3, c );
759     MSI_RecordSetInteger( row, 4, d );
760     MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
761     msiobj_release( &row->hdr );
762
763     msi_dialog_check_messages( NULL );
764 }
765
766 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
767 {
768     static const WCHAR query[] =
769         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
770          '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
771          'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
772     WCHAR message[1024];
773     MSIRECORD *row = 0;
774     DWORD size;
775
776     if (!package->LastAction || strcmpW( package->LastAction, action ))
777     {
778         if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
779
780         if (MSI_RecordIsNull( row, 3 ))
781         {
782             msiobj_release( &row->hdr );
783             return;
784         }
785         /* update the cached action format */
786         msi_free( package->ActionFormat );
787         package->ActionFormat = msi_dup_record_field( row, 3 );
788         msi_free( package->LastAction );
789         package->LastAction = strdupW( action );
790         msiobj_release( &row->hdr );
791     }
792     size = 1024;
793     MSI_RecordSetStringW( record, 0, package->ActionFormat );
794     MSI_FormatRecordW( package, record, message, &size );
795     row = MSI_CreateRecord( 1 );
796     MSI_RecordSetStringW( row, 1, message );
797     MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
798     msiobj_release( &row->hdr );
799 }
800
801 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
802 {
803     if (!comp->Enabled)
804     {
805         TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
806         return INSTALLSTATE_UNKNOWN;
807     }
808     if (package->need_rollback) return comp->Installed;
809     return comp->ActionRequest;
810 }
811
812 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
813 {
814     if (package->need_rollback) return feature->Installed;
815     return feature->ActionRequest;
816 }
817
818 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
819 {
820     MSIPACKAGE *package = param;
821     LPCWSTR dir, component, full_path;
822     MSIRECORD *uirow;
823     MSIFOLDER *folder;
824     MSICOMPONENT *comp;
825
826     component = MSI_RecordGetString(row, 2);
827     if (!component)
828         return ERROR_SUCCESS;
829
830     comp = msi_get_loaded_component(package, component);
831     if (!comp)
832         return ERROR_SUCCESS;
833
834     comp->Action = msi_get_component_action( package, comp );
835     if (comp->Action != INSTALLSTATE_LOCAL)
836     {
837         TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
838         return ERROR_SUCCESS;
839     }
840
841     dir = MSI_RecordGetString(row,1);
842     if (!dir)
843     {
844         ERR("Unable to get folder id\n");
845         return ERROR_SUCCESS;
846     }
847
848     uirow = MSI_CreateRecord(1);
849     MSI_RecordSetStringW(uirow, 1, dir);
850     msi_ui_actiondata(package, szCreateFolders, uirow);
851     msiobj_release(&uirow->hdr);
852
853     full_path = msi_get_target_folder( package, dir );
854     if (!full_path)
855     {
856         ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
857         return ERROR_SUCCESS;
858     }
859     TRACE("folder is %s\n", debugstr_w(full_path));
860
861     folder = msi_get_loaded_folder( package, dir );
862     if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
863     folder->State = FOLDER_STATE_CREATED;
864     return ERROR_SUCCESS;
865 }
866
867 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
868 {
869     static const WCHAR query[] = {
870         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
871         '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
872     MSIQUERY *view;
873     UINT rc;
874
875     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
876     if (rc != ERROR_SUCCESS)
877         return ERROR_SUCCESS;
878
879     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
880     msiobj_release(&view->hdr);
881     return rc;
882 }
883
884 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
885 {
886     MSIPACKAGE *package = param;
887     LPCWSTR dir, component, full_path;
888     MSIRECORD *uirow;
889     MSIFOLDER *folder;
890     MSICOMPONENT *comp;
891
892     component = MSI_RecordGetString(row, 2);
893     if (!component)
894         return ERROR_SUCCESS;
895
896     comp = msi_get_loaded_component(package, component);
897     if (!comp)
898         return ERROR_SUCCESS;
899
900     comp->Action = msi_get_component_action( package, comp );
901     if (comp->Action != INSTALLSTATE_ABSENT)
902     {
903         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
904         return ERROR_SUCCESS;
905     }
906
907     dir = MSI_RecordGetString( row, 1 );
908     if (!dir)
909     {
910         ERR("Unable to get folder id\n");
911         return ERROR_SUCCESS;
912     }
913
914     full_path = msi_get_target_folder( package, dir );
915     if (!full_path)
916     {
917         ERR("Unable to resolve folder %s\n", debugstr_w(dir));
918         return ERROR_SUCCESS;
919     }
920     TRACE("folder is %s\n", debugstr_w(full_path));
921
922     uirow = MSI_CreateRecord( 1 );
923     MSI_RecordSetStringW( uirow, 1, dir );
924     msi_ui_actiondata( package, szRemoveFolders, uirow );
925     msiobj_release( &uirow->hdr );
926
927     RemoveDirectoryW( full_path );
928     folder = msi_get_loaded_folder( package, dir );
929     folder->State = FOLDER_STATE_REMOVED;
930     return ERROR_SUCCESS;
931 }
932
933 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
934 {
935     static const WCHAR query[] = {
936         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
937         '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
938     MSIQUERY *view;
939     UINT rc;
940
941     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
942     if (rc != ERROR_SUCCESS)
943         return ERROR_SUCCESS;
944
945     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
946     msiobj_release( &view->hdr );
947     return rc;
948 }
949
950 static UINT load_component( MSIRECORD *row, LPVOID param )
951 {
952     MSIPACKAGE *package = param;
953     MSICOMPONENT *comp;
954
955     comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
956     if (!comp)
957         return ERROR_FUNCTION_FAILED;
958
959     list_add_tail( &package->components, &comp->entry );
960
961     /* fill in the data */
962     comp->Component = msi_dup_record_field( row, 1 );
963
964     TRACE("Loading Component %s\n", debugstr_w(comp->Component));
965
966     comp->ComponentId = msi_dup_record_field( row, 2 );
967     comp->Directory = msi_dup_record_field( row, 3 );
968     comp->Attributes = MSI_RecordGetInteger(row,4);
969     comp->Condition = msi_dup_record_field( row, 5 );
970     comp->KeyPath = msi_dup_record_field( row, 6 );
971
972     comp->Installed = INSTALLSTATE_UNKNOWN;
973     comp->Action = INSTALLSTATE_UNKNOWN;
974     comp->ActionRequest = INSTALLSTATE_UNKNOWN;
975
976     comp->assembly = msi_load_assembly( package, comp );
977     return ERROR_SUCCESS;
978 }
979
980 UINT msi_load_all_components( MSIPACKAGE *package )
981 {
982     static const WCHAR query[] = {
983         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
984         '`','C','o','m','p','o','n','e','n','t','`',0};
985     MSIQUERY *view;
986     UINT r;
987
988     if (!list_empty(&package->components))
989         return ERROR_SUCCESS;
990
991     r = MSI_DatabaseOpenViewW( package->db, query, &view );
992     if (r != ERROR_SUCCESS)
993         return r;
994
995     if (!msi_init_assembly_caches( package ))
996     {
997         ERR("can't initialize assembly caches\n");
998         msiobj_release( &view->hdr );
999         return ERROR_FUNCTION_FAILED;
1000     }
1001
1002     r = MSI_IterateRecords(view, NULL, load_component, package);
1003     msiobj_release(&view->hdr);
1004     msi_destroy_assembly_caches( package );
1005     return r;
1006 }
1007
1008 typedef struct {
1009     MSIPACKAGE *package;
1010     MSIFEATURE *feature;
1011 } _ilfs;
1012
1013 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1014 {
1015     ComponentList *cl;
1016
1017     cl = msi_alloc( sizeof (*cl) );
1018     if ( !cl )
1019         return ERROR_NOT_ENOUGH_MEMORY;
1020     cl->component = comp;
1021     list_add_tail( &feature->Components, &cl->entry );
1022
1023     return ERROR_SUCCESS;
1024 }
1025
1026 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1027 {
1028     FeatureList *fl;
1029
1030     fl = msi_alloc( sizeof(*fl) );
1031     if ( !fl )
1032         return ERROR_NOT_ENOUGH_MEMORY;
1033     fl->feature = child;
1034     list_add_tail( &parent->Children, &fl->entry );
1035
1036     return ERROR_SUCCESS;
1037 }
1038
1039 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1040 {
1041     _ilfs* ilfs = param;
1042     LPCWSTR component;
1043     MSICOMPONENT *comp;
1044
1045     component = MSI_RecordGetString(row,1);
1046
1047     /* check to see if the component is already loaded */
1048     comp = msi_get_loaded_component( ilfs->package, component );
1049     if (!comp)
1050     {
1051         WARN("ignoring unknown component %s\n", debugstr_w(component));
1052         return ERROR_SUCCESS;
1053     }
1054     add_feature_component( ilfs->feature, comp );
1055     comp->Enabled = TRUE;
1056
1057     return ERROR_SUCCESS;
1058 }
1059
1060 static UINT load_feature(MSIRECORD * row, LPVOID param)
1061 {
1062     static const WCHAR query[] = {
1063         'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1064          ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1065          'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1066          '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1067     MSIPACKAGE *package = param;
1068     MSIFEATURE *feature;
1069     MSIQUERY *view;
1070     _ilfs ilfs;
1071     UINT rc;
1072
1073     /* fill in the data */
1074
1075     feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1076     if (!feature)
1077         return ERROR_NOT_ENOUGH_MEMORY;
1078
1079     list_init( &feature->Children );
1080     list_init( &feature->Components );
1081     
1082     feature->Feature = msi_dup_record_field( row, 1 );
1083
1084     TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1085
1086     feature->Feature_Parent = msi_dup_record_field( row, 2 );
1087     feature->Title = msi_dup_record_field( row, 3 );
1088     feature->Description = msi_dup_record_field( row, 4 );
1089
1090     if (!MSI_RecordIsNull(row,5))
1091         feature->Display = MSI_RecordGetInteger(row,5);
1092   
1093     feature->Level= MSI_RecordGetInteger(row,6);
1094     feature->Directory = msi_dup_record_field( row, 7 );
1095     feature->Attributes = MSI_RecordGetInteger(row,8);
1096
1097     feature->Installed = INSTALLSTATE_UNKNOWN;
1098     feature->Action = INSTALLSTATE_UNKNOWN;
1099     feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1100
1101     list_add_tail( &package->features, &feature->entry );
1102
1103     /* load feature components */
1104
1105     rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1106     if (rc != ERROR_SUCCESS)
1107         return ERROR_SUCCESS;
1108
1109     ilfs.package = package;
1110     ilfs.feature = feature;
1111
1112     rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1113     msiobj_release(&view->hdr);
1114     return rc;
1115 }
1116
1117 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1118 {
1119     MSIPACKAGE *package = param;
1120     MSIFEATURE *parent, *child;
1121
1122     child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1123     if (!child)
1124         return ERROR_FUNCTION_FAILED;
1125
1126     if (!child->Feature_Parent)
1127         return ERROR_SUCCESS;
1128
1129     parent = msi_get_loaded_feature( package, child->Feature_Parent );
1130     if (!parent)
1131         return ERROR_FUNCTION_FAILED;
1132
1133     add_feature_child( parent, child );
1134     return ERROR_SUCCESS;
1135 }
1136
1137 UINT msi_load_all_features( MSIPACKAGE *package )
1138 {
1139     static const WCHAR query[] = {
1140         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1141         '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1142         '`','D','i','s','p','l','a','y','`',0};
1143     MSIQUERY *view;
1144     UINT r;
1145
1146     if (!list_empty(&package->features))
1147         return ERROR_SUCCESS;
1148  
1149     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1150     if (r != ERROR_SUCCESS)
1151         return r;
1152
1153     r = MSI_IterateRecords( view, NULL, load_feature, package );
1154     if (r != ERROR_SUCCESS)
1155     {
1156         msiobj_release( &view->hdr );
1157         return r;
1158     }
1159     r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1160     msiobj_release( &view->hdr );
1161     return r;
1162 }
1163
1164 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1165 {
1166     if (!p)
1167         return p;
1168     p = strchrW(p, ch);
1169     if (!p)
1170         return p;
1171     *p = 0;
1172     return p+1;
1173 }
1174
1175 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1176 {
1177     static const WCHAR query[] = {
1178         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1179         '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1180         'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1181     MSIQUERY *view = NULL;
1182     MSIRECORD *row = NULL;
1183     UINT r;
1184
1185     TRACE("%s\n", debugstr_w(file->File));
1186
1187     r = MSI_OpenQuery(package->db, &view, query, file->File);
1188     if (r != ERROR_SUCCESS)
1189         goto done;
1190
1191     r = MSI_ViewExecute(view, NULL);
1192     if (r != ERROR_SUCCESS)
1193         goto done;
1194
1195     r = MSI_ViewFetch(view, &row);
1196     if (r != ERROR_SUCCESS)
1197         goto done;
1198
1199     file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1200     file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1201     file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1202     file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1203     file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1204
1205 done:
1206     if (view) msiobj_release(&view->hdr);
1207     if (row) msiobj_release(&row->hdr);
1208     return r;
1209 }
1210
1211 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1212 {
1213     MSIRECORD *row;
1214     static const WCHAR query[] = {
1215         'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1216         '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1217         '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1218
1219     row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1220     if (!row)
1221     {
1222         WARN("query failed\n");
1223         return ERROR_FUNCTION_FAILED;
1224     }
1225
1226     file->disk_id = MSI_RecordGetInteger( row, 1 );
1227     msiobj_release( &row->hdr );
1228     return ERROR_SUCCESS;
1229 }
1230
1231 static UINT load_file(MSIRECORD *row, LPVOID param)
1232 {
1233     MSIPACKAGE* package = param;
1234     LPCWSTR component;
1235     MSIFILE *file;
1236
1237     /* fill in the data */
1238
1239     file = msi_alloc_zero( sizeof (MSIFILE) );
1240     if (!file)
1241         return ERROR_NOT_ENOUGH_MEMORY;
1242  
1243     file->File = msi_dup_record_field( row, 1 );
1244
1245     component = MSI_RecordGetString( row, 2 );
1246     file->Component = msi_get_loaded_component( package, component );
1247
1248     if (!file->Component)
1249     {
1250         WARN("Component not found: %s\n", debugstr_w(component));
1251         msi_free(file->File);
1252         msi_free(file);
1253         return ERROR_SUCCESS;
1254     }
1255
1256     file->FileName = msi_dup_record_field( row, 3 );
1257     msi_reduce_to_long_filename( file->FileName );
1258
1259     file->ShortName = msi_dup_record_field( row, 3 );
1260     file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1261     
1262     file->FileSize = MSI_RecordGetInteger( row, 4 );
1263     file->Version = msi_dup_record_field( row, 5 );
1264     file->Language = msi_dup_record_field( row, 6 );
1265     file->Attributes = MSI_RecordGetInteger( row, 7 );
1266     file->Sequence = MSI_RecordGetInteger( row, 8 );
1267
1268     file->state = msifs_invalid;
1269
1270     /* if the compressed bits are not set in the file attributes,
1271      * then read the information from the package word count property
1272      */
1273     if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1274     {
1275         file->IsCompressed = FALSE;
1276     }
1277     else if (file->Attributes &
1278              (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1279     {
1280         file->IsCompressed = TRUE;
1281     }
1282     else if (file->Attributes & msidbFileAttributesNoncompressed)
1283     {
1284         file->IsCompressed = FALSE;
1285     }
1286     else
1287     {
1288         file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1289     }
1290
1291     load_file_hash(package, file);
1292     load_file_disk_id(package, file);
1293
1294     TRACE("File Loaded (%s)\n",debugstr_w(file->File));  
1295
1296     list_add_tail( &package->files, &file->entry );
1297  
1298     return ERROR_SUCCESS;
1299 }
1300
1301 static UINT load_all_files(MSIPACKAGE *package)
1302 {
1303     static const WCHAR query[] = {
1304         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1305         '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1306         '`','S','e','q','u','e','n','c','e','`', 0};
1307     MSIQUERY *view;
1308     UINT rc;
1309
1310     if (!list_empty(&package->files))
1311         return ERROR_SUCCESS;
1312
1313     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1314     if (rc != ERROR_SUCCESS)
1315         return ERROR_SUCCESS;
1316
1317     rc = MSI_IterateRecords(view, NULL, load_file, package);
1318     msiobj_release(&view->hdr);
1319     return rc;
1320 }
1321
1322 static UINT load_media( MSIRECORD *row, LPVOID param )
1323 {
1324     MSIPACKAGE *package = param;
1325     UINT disk_id = MSI_RecordGetInteger( row, 1 );
1326     const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1327
1328     /* FIXME: load external cabinets and directory sources too */
1329     if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1330     msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1331     return ERROR_SUCCESS;
1332 }
1333
1334 static UINT load_all_media( MSIPACKAGE *package )
1335 {
1336     static const WCHAR query[] = {
1337         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1338         'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1339         '`','D','i','s','k','I','d','`',0};
1340     MSIQUERY *view;
1341     UINT r;
1342
1343     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1344     if (r != ERROR_SUCCESS)
1345         return ERROR_SUCCESS;
1346
1347     r = MSI_IterateRecords( view, NULL, load_media, package );
1348     msiobj_release( &view->hdr );
1349     return r;
1350 }
1351
1352 static UINT load_patch(MSIRECORD *row, LPVOID param)
1353 {
1354     MSIPACKAGE *package = param;
1355     MSIFILEPATCH *patch;
1356     LPWSTR file_key;
1357
1358     patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1359     if (!patch)
1360         return ERROR_NOT_ENOUGH_MEMORY;
1361
1362     file_key = msi_dup_record_field( row, 1 );
1363     patch->File = msi_get_loaded_file( package, file_key );
1364     msi_free(file_key);
1365
1366     if( !patch->File )
1367     {
1368         ERR("Failed to find target for patch in File table\n");
1369         msi_free(patch);
1370         return ERROR_FUNCTION_FAILED;
1371     }
1372
1373     patch->Sequence = MSI_RecordGetInteger( row, 2 );
1374
1375     /* FIXME: The database should be properly transformed */
1376     patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1377
1378     patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1379     patch->Attributes = MSI_RecordGetInteger( row, 4 );
1380     patch->IsApplied = FALSE;
1381
1382     /* FIXME:
1383      * Header field - for patch validation.
1384      * _StreamRef   - External key into MsiPatchHeaders (instead of the header field)
1385      */
1386
1387     TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1388
1389     list_add_tail( &package->filepatches, &patch->entry );
1390
1391     return ERROR_SUCCESS;
1392 }
1393
1394 static UINT load_all_patches(MSIPACKAGE *package)
1395 {
1396     static const WCHAR query[] = {
1397         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1398         '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1399         '`','S','e','q','u','e','n','c','e','`',0};
1400     MSIQUERY *view;
1401     UINT rc;
1402
1403     if (!list_empty(&package->filepatches))
1404         return ERROR_SUCCESS;
1405
1406     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1407     if (rc != ERROR_SUCCESS)
1408         return ERROR_SUCCESS;
1409
1410     rc = MSI_IterateRecords(view, NULL, load_patch, package);
1411     msiobj_release(&view->hdr);
1412     return rc;
1413 }
1414
1415 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1416 {
1417     static const WCHAR query[] = {
1418         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1419         '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1420         '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1421     MSIQUERY *view;
1422
1423     folder->persistent = FALSE;
1424     if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1425     {
1426         if (!MSI_ViewExecute( view, NULL ))
1427         {
1428             MSIRECORD *rec;
1429             if (!MSI_ViewFetch( view, &rec ))
1430             {
1431                 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1432                 folder->persistent = TRUE;
1433                 msiobj_release( &rec->hdr );
1434             }
1435         }
1436         msiobj_release( &view->hdr );
1437     }
1438     return ERROR_SUCCESS;
1439 }
1440
1441 static UINT load_folder( MSIRECORD *row, LPVOID param )
1442 {
1443     MSIPACKAGE *package = param;
1444     static WCHAR szEmpty[] = { 0 };
1445     LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1446     MSIFOLDER *folder;
1447
1448     if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1449     list_init( &folder->children );
1450     folder->Directory = msi_dup_record_field( row, 1 );
1451     folder->Parent = msi_dup_record_field( row, 2 );
1452     p = msi_dup_record_field(row, 3);
1453
1454     TRACE("%s\n", debugstr_w(folder->Directory));
1455
1456     /* split src and target dir */
1457     tgt_short = p;
1458     src_short = folder_split_path( p, ':' );
1459
1460     /* split the long and short paths */
1461     tgt_long = folder_split_path( tgt_short, '|' );
1462     src_long = folder_split_path( src_short, '|' );
1463
1464     /* check for no-op dirs */
1465     if (tgt_short && !strcmpW( szDot, tgt_short ))
1466         tgt_short = szEmpty;
1467     if (src_short && !strcmpW( szDot, src_short ))
1468         src_short = szEmpty;
1469
1470     if (!tgt_long)
1471         tgt_long = tgt_short;
1472
1473     if (!src_short) {
1474         src_short = tgt_short;
1475         src_long = tgt_long;
1476     }
1477
1478     if (!src_long)
1479         src_long = src_short;
1480
1481     /* FIXME: use the target short path too */
1482     folder->TargetDefault = strdupW(tgt_long);
1483     folder->SourceShortPath = strdupW(src_short);
1484     folder->SourceLongPath = strdupW(src_long);
1485     msi_free(p);
1486
1487     TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1488     TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1489     TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1490
1491     load_folder_persistence( package, folder );
1492
1493     list_add_tail( &package->folders, &folder->entry );
1494     return ERROR_SUCCESS;
1495 }
1496
1497 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1498 {
1499     FolderList *fl;
1500
1501     if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1502     fl->folder = child;
1503     list_add_tail( &parent->children, &fl->entry );
1504     return ERROR_SUCCESS;
1505 }
1506
1507 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1508 {
1509     MSIPACKAGE *package = param;
1510     MSIFOLDER *parent, *child;
1511
1512     if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1513         return ERROR_FUNCTION_FAILED;
1514
1515     if (!child->Parent) return ERROR_SUCCESS;
1516
1517     if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1518         return ERROR_FUNCTION_FAILED;
1519
1520     return add_folder_child( parent, child );
1521 }
1522
1523 static UINT load_all_folders( MSIPACKAGE *package )
1524 {
1525     static const WCHAR query[] = {
1526         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1527         '`','D','i','r','e','c','t','o','r','y','`',0};
1528     MSIQUERY *view;
1529     UINT r;
1530
1531     if (!list_empty(&package->folders))
1532         return ERROR_SUCCESS;
1533
1534     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1535     if (r != ERROR_SUCCESS)
1536         return r;
1537
1538     r = MSI_IterateRecords( view, NULL, load_folder, package );
1539     if (r != ERROR_SUCCESS)
1540     {
1541         msiobj_release( &view->hdr );
1542         return r;
1543     }
1544     r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1545     msiobj_release( &view->hdr );
1546     return r;
1547 }
1548
1549 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1550 {
1551     msi_set_property( package->db, szCostingComplete, szZero );
1552     msi_set_property( package->db, szRootDrive, szCRoot );
1553
1554     load_all_folders( package );
1555     msi_load_all_components( package );
1556     msi_load_all_features( package );
1557     load_all_files( package );
1558     load_all_patches( package );
1559     load_all_media( package );
1560
1561     return ERROR_SUCCESS;
1562 }
1563
1564 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1565 {
1566     const WCHAR *action = package->script->Actions[script][index];
1567     ui_actionstart( package, action );
1568     TRACE("executing %s\n", debugstr_w(action));
1569     return ACTION_PerformAction( package, action, script );
1570 }
1571
1572 static UINT execute_script( MSIPACKAGE *package, UINT script )
1573 {
1574     UINT i, rc = ERROR_SUCCESS;
1575
1576     TRACE("executing script %u\n", script);
1577
1578     if (!package->script)
1579     {
1580         ERR("no script!\n");
1581         return ERROR_FUNCTION_FAILED;
1582     }
1583     if (script == SCRIPT_ROLLBACK)
1584     {
1585         for (i = package->script->ActionCount[script]; i > 0; i--)
1586         {
1587             rc = execute_script_action( package, script, i - 1 );
1588             if (rc != ERROR_SUCCESS) break;
1589         }
1590     }
1591     else
1592     {
1593         for (i = 0; i < package->script->ActionCount[script]; i++)
1594         {
1595             rc = execute_script_action( package, script, i );
1596             if (rc != ERROR_SUCCESS) break;
1597         }
1598     }
1599     msi_free_action_script(package, script);
1600     return rc;
1601 }
1602
1603 static UINT ACTION_FileCost(MSIPACKAGE *package)
1604 {
1605     return ERROR_SUCCESS;
1606 }
1607
1608 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1609 {
1610     MSICOMPONENT *comp;
1611     UINT r;
1612
1613     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1614     {
1615         if (!comp->ComponentId) continue;
1616
1617         r = MsiQueryComponentStateW( package->ProductCode, NULL,
1618                                      MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1619                                      &comp->Installed );
1620         if (r != ERROR_SUCCESS)
1621             r = MsiQueryComponentStateW( package->ProductCode, NULL,
1622                                          MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1623                                          &comp->Installed );
1624         if (r != ERROR_SUCCESS)
1625             r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626                                          MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1627                                          &comp->Installed );
1628         if (r != ERROR_SUCCESS)
1629             comp->Installed = INSTALLSTATE_ABSENT;
1630     }
1631 }
1632
1633 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1634 {
1635     MSIFEATURE *feature;
1636
1637     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1638     {
1639         INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1640
1641         if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1642             feature->Installed = INSTALLSTATE_ABSENT;
1643         else
1644             feature->Installed = state;
1645     }
1646 }
1647
1648 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1649 {
1650     return (feature->Level > 0 && feature->Level <= level);
1651 }
1652
1653 static BOOL process_state_property(MSIPACKAGE* package, int level,
1654                                    LPCWSTR property, INSTALLSTATE state)
1655 {
1656     LPWSTR override;
1657     MSIFEATURE *feature;
1658
1659     override = msi_dup_property( package->db, property );
1660     if (!override)
1661         return FALSE;
1662
1663     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1664     {
1665         if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1666             continue;
1667
1668         if (!strcmpW(property, szReinstall)) state = feature->Installed;
1669
1670         if (!strcmpiW( override, szAll ))
1671         {
1672             if (feature->Installed != state)
1673             {
1674                 feature->Action = state;
1675                 feature->ActionRequest = state;
1676             }
1677         }
1678         else
1679         {
1680             LPWSTR ptr = override;
1681             LPWSTR ptr2 = strchrW(override,',');
1682
1683             while (ptr)
1684             {
1685                 int len = ptr2 - ptr;
1686
1687                 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1688                     || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1689                 {
1690                     if (feature->Installed != state)
1691                     {
1692                         feature->Action = state;
1693                         feature->ActionRequest = state;
1694                     }
1695                     break;
1696                 }
1697                 if (ptr2)
1698                 {
1699                     ptr=ptr2+1;
1700                     ptr2 = strchrW(ptr,',');
1701                 }
1702                 else
1703                     break;
1704             }
1705         }
1706     }
1707     msi_free(override);
1708     return TRUE;
1709 }
1710
1711 static BOOL process_overrides( MSIPACKAGE *package, int level )
1712 {
1713     static const WCHAR szAddLocal[] =
1714         {'A','D','D','L','O','C','A','L',0};
1715     static const WCHAR szAddSource[] =
1716         {'A','D','D','S','O','U','R','C','E',0};
1717     static const WCHAR szAdvertise[] =
1718         {'A','D','V','E','R','T','I','S','E',0};
1719     BOOL ret = FALSE;
1720
1721     /* all these activation/deactivation things happen in order and things
1722      * later on the list override things earlier on the list.
1723      *
1724      *  0  INSTALLLEVEL processing
1725      *  1  ADDLOCAL
1726      *  2  REMOVE
1727      *  3  ADDSOURCE
1728      *  4  ADDDEFAULT
1729      *  5  REINSTALL
1730      *  6  ADVERTISE
1731      *  7  COMPADDLOCAL
1732      *  8  COMPADDSOURCE
1733      *  9  FILEADDLOCAL
1734      * 10  FILEADDSOURCE
1735      * 11  FILEADDDEFAULT
1736      */
1737     ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1738     ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1739     ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1740     ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1741     ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1742
1743     if (ret)
1744         msi_set_property( package->db, szPreselected, szOne );
1745
1746     return ret;
1747 }
1748
1749 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1750 {
1751     int level;
1752     MSICOMPONENT* component;
1753     MSIFEATURE *feature;
1754
1755     TRACE("Checking Install Level\n");
1756
1757     level = msi_get_property_int(package->db, szInstallLevel, 1);
1758
1759     if (!msi_get_property_int( package->db, szPreselected, 0 ))
1760     {
1761         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1762         {
1763             if (!is_feature_selected( feature, level )) continue;
1764
1765             if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1766             {
1767                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1768                 {
1769                     feature->Action = INSTALLSTATE_SOURCE;
1770                     feature->ActionRequest = INSTALLSTATE_SOURCE;
1771                 }
1772                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1773                 {
1774                     feature->Action = INSTALLSTATE_ADVERTISED;
1775                     feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1776                 }
1777                 else
1778                 {
1779                     feature->Action = INSTALLSTATE_LOCAL;
1780                     feature->ActionRequest = INSTALLSTATE_LOCAL;
1781                 }
1782             }
1783         }
1784         /* disable child features of unselected parent or follow parent */
1785         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1786         {
1787             FeatureList *fl;
1788
1789             LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1790             {
1791                 if (!is_feature_selected( feature, level ))
1792                 {
1793                     fl->feature->Action = INSTALLSTATE_UNKNOWN;
1794                     fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1795                 }
1796                 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1797                 {
1798                     fl->feature->Action = feature->Action;
1799                     fl->feature->ActionRequest = feature->ActionRequest;
1800                 }
1801             }
1802         }
1803     }
1804     else /* preselected */
1805     {
1806         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1807         {
1808             if (!is_feature_selected( feature, level )) continue;
1809
1810             if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1811             {
1812                 if (feature->Installed == INSTALLSTATE_ABSENT)
1813                 {
1814                     feature->Action = INSTALLSTATE_UNKNOWN;
1815                     feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1816                 }
1817                 else
1818                 {
1819                     feature->Action = feature->Installed;
1820                     feature->ActionRequest = feature->Installed;
1821                 }
1822             }
1823         }
1824         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1825         {
1826             FeatureList *fl;
1827
1828             if (!is_feature_selected( feature, level )) continue;
1829
1830             LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1831             {
1832                 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1833                 {
1834                     fl->feature->Action = feature->Action;
1835                     fl->feature->ActionRequest = feature->ActionRequest;
1836                 }
1837             }
1838         }
1839     }
1840
1841     /* now we want to set component state based based on feature state */
1842     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1843     {
1844         ComponentList *cl;
1845
1846         TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1847               debugstr_w(feature->Feature), feature->Level, feature->Installed,
1848               feature->ActionRequest, feature->Action);
1849
1850         if (!is_feature_selected( feature, level )) continue;
1851
1852         /* features with components that have compressed files are made local */
1853         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1854         {
1855             if (cl->component->ForceLocalState &&
1856                 feature->ActionRequest == INSTALLSTATE_SOURCE)
1857             {
1858                 feature->Action = INSTALLSTATE_LOCAL;
1859                 feature->ActionRequest = INSTALLSTATE_LOCAL;
1860                 break;
1861             }
1862         }
1863
1864         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1865         {
1866             component = cl->component;
1867
1868             switch (feature->ActionRequest)
1869             {
1870             case INSTALLSTATE_ABSENT:
1871                 component->anyAbsent = 1;
1872                 break;
1873             case INSTALLSTATE_ADVERTISED:
1874                 component->hasAdvertiseFeature = 1;
1875                 break;
1876             case INSTALLSTATE_SOURCE:
1877                 component->hasSourceFeature = 1;
1878                 break;
1879             case INSTALLSTATE_LOCAL:
1880                 component->hasLocalFeature = 1;
1881                 break;
1882             case INSTALLSTATE_DEFAULT:
1883                 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1884                     component->hasAdvertiseFeature = 1;
1885                 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1886                     component->hasSourceFeature = 1;
1887                 else
1888                     component->hasLocalFeature = 1;
1889                 break;
1890             default:
1891                 break;
1892             }
1893         }
1894     }
1895
1896     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1897     {
1898         /* check if it's local or source */
1899         if (!(component->Attributes & msidbComponentAttributesOptional) &&
1900              (component->hasLocalFeature || component->hasSourceFeature))
1901         {
1902             if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1903                  !component->ForceLocalState)
1904             {
1905                 component->Action = INSTALLSTATE_SOURCE;
1906                 component->ActionRequest = INSTALLSTATE_SOURCE;
1907             }
1908             else
1909             {
1910                 component->Action = INSTALLSTATE_LOCAL;
1911                 component->ActionRequest = INSTALLSTATE_LOCAL;
1912             }
1913             continue;
1914         }
1915
1916         /* if any feature is local, the component must be local too */
1917         if (component->hasLocalFeature)
1918         {
1919             component->Action = INSTALLSTATE_LOCAL;
1920             component->ActionRequest = INSTALLSTATE_LOCAL;
1921             continue;
1922         }
1923         if (component->hasSourceFeature)
1924         {
1925             component->Action = INSTALLSTATE_SOURCE;
1926             component->ActionRequest = INSTALLSTATE_SOURCE;
1927             continue;
1928         }
1929         if (component->hasAdvertiseFeature)
1930         {
1931             component->Action = INSTALLSTATE_ADVERTISED;
1932             component->ActionRequest = INSTALLSTATE_ADVERTISED;
1933             continue;
1934         }
1935         TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1936         if (component->anyAbsent &&
1937             (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1938         {
1939             component->Action = INSTALLSTATE_ABSENT;
1940             component->ActionRequest = INSTALLSTATE_ABSENT;
1941         }
1942     }
1943
1944     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1945     {
1946         if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1947         {
1948             TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1949             component->Action = INSTALLSTATE_LOCAL;
1950             component->ActionRequest = INSTALLSTATE_LOCAL;
1951         }
1952
1953         if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1954             component->Installed == INSTALLSTATE_SOURCE &&
1955             component->hasSourceFeature)
1956         {
1957             component->Action = INSTALLSTATE_UNKNOWN;
1958             component->ActionRequest = INSTALLSTATE_UNKNOWN;
1959         }
1960
1961         TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1962               debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1963     }
1964
1965     return ERROR_SUCCESS;
1966 }
1967
1968 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1969 {
1970     MSIPACKAGE *package = param;
1971     LPCWSTR name;
1972     MSIFEATURE *feature;
1973
1974     name = MSI_RecordGetString( row, 1 );
1975
1976     feature = msi_get_loaded_feature( package, name );
1977     if (!feature)
1978         ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1979     else
1980     {
1981         LPCWSTR Condition;
1982         Condition = MSI_RecordGetString(row,3);
1983
1984         if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1985         {
1986             int level = MSI_RecordGetInteger(row,2);
1987             TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1988             feature->Level = level;
1989         }
1990     }
1991     return ERROR_SUCCESS;
1992 }
1993
1994 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1995 {
1996     static const WCHAR name[] = {'\\',0};
1997     VS_FIXEDFILEINFO *ptr, *ret;
1998     LPVOID version;
1999     DWORD versize, handle;
2000     UINT sz;
2001
2002     versize = GetFileVersionInfoSizeW( filename, &handle );
2003     if (!versize)
2004         return NULL;
2005
2006     version = msi_alloc( versize );
2007     if (!version)
2008         return NULL;
2009
2010     GetFileVersionInfoW( filename, 0, versize, version );
2011
2012     if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2013     {
2014         msi_free( version );
2015         return NULL;
2016     }
2017
2018     ret = msi_alloc( sz );
2019     memcpy( ret, ptr, sz );
2020
2021     msi_free( version );
2022     return ret;
2023 }
2024
2025 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2026 {
2027     DWORD ms, ls;
2028
2029     msi_parse_version_string( version, &ms, &ls );
2030
2031     if (fi->dwFileVersionMS > ms) return 1;
2032     else if (fi->dwFileVersionMS < ms) return -1;
2033     else if (fi->dwFileVersionLS > ls) return 1;
2034     else if (fi->dwFileVersionLS < ls) return -1;
2035     return 0;
2036 }
2037
2038 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2039 {
2040     DWORD ms1, ms2;
2041
2042     msi_parse_version_string( ver1, &ms1, NULL );
2043     msi_parse_version_string( ver2, &ms2, NULL );
2044
2045     if (ms1 > ms2) return 1;
2046     else if (ms1 < ms2) return -1;
2047     return 0;
2048 }
2049
2050 DWORD msi_get_disk_file_size( LPCWSTR filename )
2051 {
2052     HANDLE file;
2053     DWORD size;
2054
2055     file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2056     if (file == INVALID_HANDLE_VALUE)
2057         return INVALID_FILE_SIZE;
2058
2059     size = GetFileSize( file, NULL );
2060     TRACE("size is %u\n", size);
2061     CloseHandle( file );
2062     return size;
2063 }
2064
2065 BOOL msi_file_hash_matches( MSIFILE *file )
2066 {
2067     UINT r;
2068     MSIFILEHASHINFO hash;
2069
2070     hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2071     r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2072     if (r != ERROR_SUCCESS)
2073         return FALSE;
2074
2075     return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2076 }
2077
2078 static WCHAR *get_temp_dir( void )
2079 {
2080     static UINT id;
2081     WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2082
2083     GetTempPathW( MAX_PATH, tmp );
2084     for (;;)
2085     {
2086         if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2087         if (CreateDirectoryW( dir, NULL )) break;
2088     }
2089     return strdupW( dir );
2090 }
2091
2092 /*
2093  *  msi_build_directory_name()
2094  *
2095  *  This function is to save messing round with directory names
2096  *  It handles adding backslashes between path segments,
2097  *  and can add \ at the end of the directory name if told to.
2098  *
2099  *  It takes a variable number of arguments.
2100  *  It always allocates a new string for the result, so make sure
2101  *  to free the return value when finished with it.
2102  *
2103  *  The first arg is the number of path segments that follow.
2104  *  The arguments following count are a list of path segments.
2105  *  A path segment may be NULL.
2106  *
2107  *  Path segments will be added with a \ separating them.
2108  *  A \ will not be added after the last segment, however if the
2109  *  last segment is NULL, then the last character will be a \
2110  */
2111 WCHAR *msi_build_directory_name( DWORD count, ... )
2112 {
2113     DWORD sz = 1, i;
2114     WCHAR *dir;
2115     va_list va;
2116
2117     va_start( va, count );
2118     for (i = 0; i < count; i++)
2119     {
2120         const WCHAR *str = va_arg( va, const WCHAR * );
2121         if (str) sz += strlenW( str ) + 1;
2122     }
2123     va_end( va );
2124
2125     dir = msi_alloc( sz * sizeof(WCHAR) );
2126     dir[0] = 0;
2127
2128     va_start( va, count );
2129     for (i = 0; i < count; i++)
2130     {
2131         const WCHAR *str = va_arg( va, const WCHAR * );
2132         if (!str) continue;
2133         strcatW( dir, str );
2134         if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2135     }
2136     va_end( va );
2137     return dir;
2138 }
2139
2140 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2141 {
2142     MSIASSEMBLY *assembly = file->Component->assembly;
2143
2144     TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2145
2146     msi_free( file->TargetPath );
2147     if (assembly && !assembly->application)
2148     {
2149         if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2150         file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2151         msi_track_tempfile( package, file->TargetPath );
2152     }
2153     else
2154     {
2155         const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2156         file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2157     }
2158
2159     TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2160 }
2161
2162 static UINT calculate_file_cost( MSIPACKAGE *package )
2163 {
2164     VS_FIXEDFILEINFO *file_version;
2165     WCHAR *font_version;
2166     MSIFILE *file;
2167
2168     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2169     {
2170         MSICOMPONENT *comp = file->Component;
2171         DWORD file_size;
2172
2173         if (!comp->Enabled) continue;
2174
2175         if (file->IsCompressed)
2176             comp->ForceLocalState = TRUE;
2177
2178         set_target_path( package, file );
2179
2180         if ((comp->assembly && !comp->assembly->installed) ||
2181             GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2182         {
2183             comp->Cost += file->FileSize;
2184             continue;
2185         }
2186         file_size = msi_get_disk_file_size( file->TargetPath );
2187
2188         if (file->Version)
2189         {
2190             if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2191             {
2192                 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2193                 {
2194                     comp->Cost += file->FileSize - file_size;
2195                 }
2196                 msi_free( file_version );
2197                 continue;
2198             }
2199             else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2200             {
2201                 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2202                 {
2203                     comp->Cost += file->FileSize - file_size;
2204                 }
2205                 msi_free( font_version );
2206                 continue;
2207             }
2208         }
2209         if (file_size != file->FileSize)
2210         {
2211             comp->Cost += file->FileSize - file_size;
2212         }
2213     }
2214     return ERROR_SUCCESS;
2215 }
2216
2217 void msi_clean_path( WCHAR *p )
2218 {
2219     WCHAR *q = p;
2220     int n, len = 0;
2221
2222     while (1)
2223     {
2224         /* copy until the end of the string or a space */
2225         while (*p != ' ' && (*q = *p))
2226         {
2227             p++, len++;
2228             /* reduce many backslashes to one */
2229             if (*p != '\\' || *q != '\\')
2230                 q++;
2231         }
2232
2233         /* quit at the end of the string */
2234         if (!*p)
2235             break;
2236
2237         /* count the number of spaces */
2238         n = 0;
2239         while (p[n] == ' ')
2240             n++;
2241
2242         /* if it's leading or trailing space, skip it */
2243         if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2244             p += n;
2245         else  /* copy n spaces */
2246             while (n && (*q++ = *p++)) n--;
2247     }
2248 }
2249
2250 static WCHAR *get_target_dir_property( MSIDATABASE *db )
2251 {
2252     int len;
2253     WCHAR *path, *target_dir = msi_dup_property( db, szTargetDir );
2254
2255     if (!target_dir) return NULL;
2256
2257     len = strlenW( target_dir );
2258     if (target_dir[len - 1] == '\\') return target_dir;
2259     if ((path = msi_alloc( (len + 2) * sizeof(WCHAR) )))
2260     {
2261         strcpyW( path, target_dir );
2262         path[len] = '\\';
2263         path[len + 1] = 0;
2264     }
2265     msi_free( target_dir );
2266     return path;
2267 }
2268
2269 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2270 {
2271     FolderList *fl;
2272     MSIFOLDER *folder, *parent, *child;
2273     WCHAR *path;
2274
2275     TRACE("resolving %s\n", debugstr_w(name));
2276
2277     if (!(folder = msi_get_loaded_folder( package, name ))) return;
2278
2279     if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2280     {
2281         if (!load_prop || !(path = get_target_dir_property( package->db )))
2282         {
2283             path = msi_dup_property( package->db, szRootDrive );
2284         }
2285     }
2286     else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2287     {
2288         if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2289         {
2290             parent = msi_get_loaded_folder( package, folder->Parent );
2291             path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2292         }
2293         else
2294             path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2295     }
2296     msi_clean_path( path );
2297     if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
2298     {
2299         TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2300         msi_free( path );
2301         return;
2302     }
2303     msi_set_property( package->db, folder->Directory, path );
2304     msi_free( folder->ResolvedTarget );
2305     folder->ResolvedTarget = path;
2306
2307     LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2308     {
2309         child = fl->folder;
2310         msi_resolve_target_folder( package, child->Directory, load_prop );
2311     }
2312     TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2313 }
2314
2315 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2316 {
2317     static const WCHAR query[] = {
2318         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2319         '`','C','o','n','d','i','t','i','o','n','`',0};
2320     static const WCHAR szOutOfDiskSpace[] = {
2321         'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2322     MSICOMPONENT *comp;
2323     MSIQUERY *view;
2324     LPWSTR level;
2325     UINT rc;
2326
2327     TRACE("Building directory properties\n");
2328     msi_resolve_target_folder( package, szTargetDir, TRUE );
2329
2330     TRACE("Evaluating component conditions\n");
2331     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2332     {
2333         if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2334         {
2335             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2336             comp->Enabled = FALSE;
2337         }
2338         else
2339             comp->Enabled = TRUE;
2340     }
2341
2342     /* read components states from the registry */
2343     ACTION_GetComponentInstallStates(package);
2344     ACTION_GetFeatureInstallStates(package);
2345
2346     if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2347     {
2348         TRACE("Evaluating feature conditions\n");
2349
2350         rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2351         if (rc == ERROR_SUCCESS)
2352         {
2353             rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2354             msiobj_release( &view->hdr );
2355             if (rc != ERROR_SUCCESS)
2356                 return rc;
2357         }
2358     }
2359
2360     TRACE("Calculating file cost\n");
2361     calculate_file_cost( package );
2362
2363     msi_set_property( package->db, szCostingComplete, szOne );
2364     /* set default run level if not set */
2365     level = msi_dup_property( package->db, szInstallLevel );
2366     if (!level)
2367         msi_set_property( package->db, szInstallLevel, szOne );
2368     msi_free(level);
2369
2370     /* FIXME: check volume disk space */
2371     msi_set_property( package->db, szOutOfDiskSpace, szZero );
2372
2373     return MSI_SetFeatureStates(package);
2374 }
2375
2376 /* OK this value is "interpreted" and then formatted based on the 
2377    first few characters */
2378 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2379                          DWORD *size)
2380 {
2381     LPSTR data = NULL;
2382
2383     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2384     {
2385         if (value[1]=='x')
2386         {
2387             LPWSTR ptr;
2388             CHAR byte[5];
2389             LPWSTR deformated = NULL;
2390             int count;
2391
2392             deformat_string(package, &value[2], &deformated);
2393
2394             /* binary value type */
2395             ptr = deformated;
2396             *type = REG_BINARY;
2397             if (strlenW(ptr)%2)
2398                 *size = (strlenW(ptr)/2)+1;
2399             else
2400                 *size = strlenW(ptr)/2;
2401
2402             data = msi_alloc(*size);
2403
2404             byte[0] = '0'; 
2405             byte[1] = 'x'; 
2406             byte[4] = 0; 
2407             count = 0;
2408             /* if uneven pad with a zero in front */
2409             if (strlenW(ptr)%2)
2410             {
2411                 byte[2]= '0';
2412                 byte[3]= *ptr;
2413                 ptr++;
2414                 data[count] = (BYTE)strtol(byte,NULL,0);
2415                 count ++;
2416                 TRACE("Uneven byte count\n");
2417             }
2418             while (*ptr)
2419             {
2420                 byte[2]= *ptr;
2421                 ptr++;
2422                 byte[3]= *ptr;
2423                 ptr++;
2424                 data[count] = (BYTE)strtol(byte,NULL,0);
2425                 count ++;
2426             }
2427             msi_free(deformated);
2428
2429             TRACE("Data %i bytes(%i)\n",*size,count);
2430         }
2431         else
2432         {
2433             LPWSTR deformated;
2434             LPWSTR p;
2435             DWORD d = 0;
2436             deformat_string(package, &value[1], &deformated);
2437
2438             *type=REG_DWORD; 
2439             *size = sizeof(DWORD);
2440             data = msi_alloc(*size);
2441             p = deformated;
2442             if (*p == '-')
2443                 p++;
2444             while (*p)
2445             {
2446                 if ( (*p < '0') || (*p > '9') )
2447                     break;
2448                 d *= 10;
2449                 d += (*p - '0');
2450                 p++;
2451             }
2452             if (deformated[0] == '-')
2453                 d = -d;
2454             *(LPDWORD)data = d;
2455             TRACE("DWORD %i\n",*(LPDWORD)data);
2456
2457             msi_free(deformated);
2458         }
2459     }
2460     else
2461     {
2462         static const WCHAR szMulti[] = {'[','~',']',0};
2463         LPCWSTR ptr;
2464         *type=REG_SZ;
2465
2466         if (value[0]=='#')
2467         {
2468             if (value[1]=='%')
2469             {
2470                 ptr = &value[2];
2471                 *type=REG_EXPAND_SZ;
2472             }
2473             else
2474                 ptr = &value[1];
2475          }
2476          else
2477             ptr=value;
2478
2479         if (strstrW(value, szMulti))
2480             *type = REG_MULTI_SZ;
2481
2482         /* remove initial delimiter */
2483         if (!strncmpW(value, szMulti, 3))
2484             ptr = value + 3;
2485
2486         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2487
2488         /* add double NULL terminator */
2489         if (*type == REG_MULTI_SZ)
2490         {
2491             *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2492             data = msi_realloc_zero(data, *size);
2493         }
2494     }
2495     return data;
2496 }
2497
2498 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2499 {
2500     const WCHAR *ret;
2501
2502     switch (root)
2503     {
2504     case -1:
2505         if (msi_get_property_int( package->db, szAllUsers, 0 ))
2506         {
2507             *root_key = HKEY_LOCAL_MACHINE;
2508             ret = szHLM;
2509         }
2510         else
2511         {
2512             *root_key = HKEY_CURRENT_USER;
2513             ret = szHCU;
2514         }
2515         break;
2516     case 0:
2517         *root_key = HKEY_CLASSES_ROOT;
2518         ret = szHCR;
2519         break;
2520     case 1:
2521         *root_key = HKEY_CURRENT_USER;
2522         ret = szHCU;
2523         break;
2524     case 2:
2525         *root_key = HKEY_LOCAL_MACHINE;
2526         ret = szHLM;
2527         break;
2528     case 3:
2529         *root_key = HKEY_USERS;
2530         ret = szHU;
2531         break;
2532     default:
2533         ERR("Unknown root %i\n", root);
2534         return NULL;
2535     }
2536
2537     return ret;
2538 }
2539
2540 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2541 {
2542     static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2543     static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2544
2545     if (is_64bit && package->platform == PLATFORM_INTEL &&
2546         root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2547     {
2548         UINT size;
2549         WCHAR *path_32node;
2550
2551         size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2552         if (!(path_32node = msi_alloc( size ))) return NULL;
2553
2554         memcpy( path_32node, path, len * sizeof(WCHAR) );
2555         strcpyW( path_32node + len, szWow6432Node );
2556         strcatW( path_32node, szBackSlash );
2557         strcatW( path_32node, path + len );
2558         return path_32node;
2559     }
2560
2561     return strdupW( path );
2562 }
2563
2564 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2565 {
2566     MSIPACKAGE *package = param;
2567     LPSTR value_data = NULL;
2568     HKEY  root_key, hkey;
2569     DWORD type,size;
2570     LPWSTR deformated, uikey, keypath;
2571     LPCWSTR szRoot, component, name, key, value;
2572     MSICOMPONENT *comp;
2573     MSIRECORD * uirow;
2574     INT   root;
2575     BOOL check_first = FALSE;
2576     UINT rc;
2577
2578     msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2579
2580     component = MSI_RecordGetString(row, 6);
2581     comp = msi_get_loaded_component(package,component);
2582     if (!comp)
2583         return ERROR_SUCCESS;
2584
2585     comp->Action = msi_get_component_action( package, comp );
2586     if (comp->Action != INSTALLSTATE_LOCAL)
2587     {
2588         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2589         return ERROR_SUCCESS;
2590     }
2591
2592     name = MSI_RecordGetString(row, 4);
2593     if( MSI_RecordIsNull(row,5) && name )
2594     {
2595         /* null values can have special meanings */
2596         if (name[0]=='-' && name[1] == 0)
2597                 return ERROR_SUCCESS;
2598         else if ((name[0]=='+' && name[1] == 0) || 
2599                  (name[0] == '*' && name[1] == 0))
2600                 name = NULL;
2601         check_first = TRUE;
2602     }
2603
2604     root = MSI_RecordGetInteger(row,2);
2605     key = MSI_RecordGetString(row, 3);
2606
2607     szRoot = get_root_key( package, root, &root_key );
2608     if (!szRoot)
2609         return ERROR_SUCCESS;
2610
2611     deformat_string(package, key , &deformated);
2612     size = strlenW(deformated) + strlenW(szRoot) + 1;
2613     uikey = msi_alloc(size*sizeof(WCHAR));
2614     strcpyW(uikey,szRoot);
2615     strcatW(uikey,deformated);
2616
2617     keypath = get_keypath( package, root_key, deformated );
2618     msi_free( deformated );
2619     if (RegCreateKeyW( root_key, keypath, &hkey ))
2620     {
2621         ERR("Could not create key %s\n", debugstr_w(keypath));
2622         msi_free(uikey);
2623         msi_free(keypath);
2624         return ERROR_SUCCESS;
2625     }
2626
2627     value = MSI_RecordGetString(row,5);
2628     if (value)
2629         value_data = parse_value(package, value, &type, &size); 
2630     else
2631     {
2632         value_data = (LPSTR)strdupW(szEmpty);
2633         size = sizeof(szEmpty);
2634         type = REG_SZ;
2635     }
2636
2637     deformat_string(package, name, &deformated);
2638
2639     if (!check_first)
2640     {
2641         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2642                         debugstr_w(uikey));
2643         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2644     }
2645     else
2646     {
2647         DWORD sz = 0;
2648         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2649         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2650         {
2651             TRACE("value %s of %s checked already exists\n",
2652                             debugstr_w(deformated), debugstr_w(uikey));
2653         }
2654         else
2655         {
2656             TRACE("Checked and setting value %s of %s\n",
2657                             debugstr_w(deformated), debugstr_w(uikey));
2658             if (deformated || size)
2659                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2660         }
2661     }
2662     RegCloseKey(hkey);
2663
2664     uirow = MSI_CreateRecord(3);
2665     MSI_RecordSetStringW(uirow,2,deformated);
2666     MSI_RecordSetStringW(uirow,1,uikey);
2667     if (type == REG_SZ || type == REG_EXPAND_SZ)
2668         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2669     msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2670     msiobj_release( &uirow->hdr );
2671
2672     msi_free(value_data);
2673     msi_free(deformated);
2674     msi_free(uikey);
2675     msi_free(keypath);
2676
2677     return ERROR_SUCCESS;
2678 }
2679
2680 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2681 {
2682     static const WCHAR query[] = {
2683         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2684         '`','R','e','g','i','s','t','r','y','`',0};
2685     MSIQUERY *view;
2686     UINT rc;
2687
2688     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2689     if (rc != ERROR_SUCCESS)
2690         return ERROR_SUCCESS;
2691
2692     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2693     msiobj_release(&view->hdr);
2694     return rc;
2695 }
2696
2697 static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value )
2698 {
2699     LONG res;
2700     HKEY hkey;
2701     DWORD num_subkeys, num_values;
2702
2703     if (!(res = RegOpenKeyW( root, keypath, &hkey )))
2704     {
2705         if ((res = RegDeleteValueW( hkey, value )))
2706         {
2707             TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2708         }
2709         res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2710                                 NULL, NULL, NULL, NULL );
2711         RegCloseKey( hkey );
2712         if (!res && !num_subkeys && !num_values)
2713         {
2714             TRACE("removing empty key %s\n", debugstr_w(keypath));
2715             RegDeleteKeyW( root, keypath );
2716         }
2717         return;
2718     }
2719     TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2720 }
2721
2722 static void delete_reg_key( HKEY root, const WCHAR *keypath )
2723 {
2724     LONG res = RegDeleteTreeW( root, keypath );
2725     if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2726 }
2727
2728 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2729 {
2730     MSIPACKAGE *package = param;
2731     LPCWSTR component, name, key_str, root_key_str;
2732     LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2733     MSICOMPONENT *comp;
2734     MSIRECORD *uirow;
2735     BOOL delete_key = FALSE;
2736     HKEY hkey_root;
2737     UINT size;
2738     INT root;
2739
2740     msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2741
2742     component = MSI_RecordGetString( row, 6 );
2743     comp = msi_get_loaded_component( package, component );
2744     if (!comp)
2745         return ERROR_SUCCESS;
2746
2747     comp->Action = msi_get_component_action( package, comp );
2748     if (comp->Action != INSTALLSTATE_ABSENT)
2749     {
2750         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2751         return ERROR_SUCCESS;
2752     }
2753
2754     name = MSI_RecordGetString( row, 4 );
2755     if (MSI_RecordIsNull( row, 5 ) && name )
2756     {
2757         if (name[0] == '+' && !name[1])
2758             return ERROR_SUCCESS;
2759         else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2760         {
2761             delete_key = TRUE;
2762             name = NULL;
2763         }
2764     }
2765
2766     root = MSI_RecordGetInteger( row, 2 );
2767     key_str = MSI_RecordGetString( row, 3 );
2768
2769     root_key_str = get_root_key( package, root, &hkey_root );
2770     if (!root_key_str)
2771         return ERROR_SUCCESS;
2772
2773     deformat_string( package, key_str, &deformated_key );
2774     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2775     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2776     strcpyW( ui_key_str, root_key_str );
2777     strcatW( ui_key_str, deformated_key );
2778
2779     deformat_string( package, name, &deformated_name );
2780
2781     keypath = get_keypath( package, hkey_root, deformated_key );
2782     msi_free( deformated_key );
2783     if (delete_key) delete_reg_key( hkey_root, keypath );
2784     else delete_reg_value( hkey_root, keypath, deformated_name );
2785     msi_free( keypath );
2786
2787     uirow = MSI_CreateRecord( 2 );
2788     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2789     MSI_RecordSetStringW( uirow, 2, deformated_name );
2790     msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2791     msiobj_release( &uirow->hdr );
2792
2793     msi_free( ui_key_str );
2794     msi_free( deformated_name );
2795     return ERROR_SUCCESS;
2796 }
2797
2798 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2799 {
2800     MSIPACKAGE *package = param;
2801     LPCWSTR component, name, key_str, root_key_str;
2802     LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2803     MSICOMPONENT *comp;
2804     MSIRECORD *uirow;
2805     BOOL delete_key = FALSE;
2806     HKEY hkey_root;
2807     UINT size;
2808     INT root;
2809
2810     component = MSI_RecordGetString( row, 5 );
2811     comp = msi_get_loaded_component( package, component );
2812     if (!comp)
2813         return ERROR_SUCCESS;
2814
2815     comp->Action = msi_get_component_action( package, comp );
2816     if (comp->Action != INSTALLSTATE_LOCAL)
2817     {
2818         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2819         return ERROR_SUCCESS;
2820     }
2821
2822     if ((name = MSI_RecordGetString( row, 4 )))
2823     {
2824         if (name[0] == '-' && !name[1])
2825         {
2826             delete_key = TRUE;
2827             name = NULL;
2828         }
2829     }
2830
2831     root = MSI_RecordGetInteger( row, 2 );
2832     key_str = MSI_RecordGetString( row, 3 );
2833
2834     root_key_str = get_root_key( package, root, &hkey_root );
2835     if (!root_key_str)
2836         return ERROR_SUCCESS;
2837
2838     deformat_string( package, key_str, &deformated_key );
2839     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2840     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2841     strcpyW( ui_key_str, root_key_str );
2842     strcatW( ui_key_str, deformated_key );
2843
2844     deformat_string( package, name, &deformated_name );
2845
2846     keypath = get_keypath( package, hkey_root, deformated_key );
2847     msi_free( deformated_key );
2848     if (delete_key) delete_reg_key( hkey_root, keypath );
2849     else delete_reg_value( hkey_root, keypath, deformated_name );
2850     msi_free( keypath );
2851
2852     uirow = MSI_CreateRecord( 2 );
2853     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2854     MSI_RecordSetStringW( uirow, 2, deformated_name );
2855     msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2856     msiobj_release( &uirow->hdr );
2857
2858     msi_free( ui_key_str );
2859     msi_free( deformated_name );
2860     return ERROR_SUCCESS;
2861 }
2862
2863 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2864 {
2865     static const WCHAR registry_query[] = {
2866         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2867         '`','R','e','g','i','s','t','r','y','`',0};
2868     static const WCHAR remove_registry_query[] = {
2869         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2870         '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2871     MSIQUERY *view;
2872     UINT rc;
2873
2874     rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2875     if (rc == ERROR_SUCCESS)
2876     {
2877         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2878         msiobj_release( &view->hdr );
2879         if (rc != ERROR_SUCCESS)
2880             return rc;
2881     }
2882     rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2883     if (rc == ERROR_SUCCESS)
2884     {
2885         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2886         msiobj_release( &view->hdr );
2887         if (rc != ERROR_SUCCESS)
2888             return rc;
2889     }
2890     return ERROR_SUCCESS;
2891 }
2892
2893 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2894 {
2895     package->script->CurrentlyScripting = TRUE;
2896
2897     return ERROR_SUCCESS;
2898 }
2899
2900
2901 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2902 {
2903     static const WCHAR query[]= {
2904         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2905         '`','R','e','g','i','s','t','r','y','`',0};
2906     MSICOMPONENT *comp;
2907     DWORD total = 0, count = 0;
2908     MSIQUERY *view;
2909     MSIFEATURE *feature;
2910     MSIFILE *file;
2911     UINT rc;
2912
2913     TRACE("InstallValidate\n");
2914
2915     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2916     if (rc == ERROR_SUCCESS)
2917     {
2918         rc = MSI_IterateRecords( view, &count, NULL, package );
2919         msiobj_release( &view->hdr );
2920         if (rc != ERROR_SUCCESS)
2921             return rc;
2922         total += count * REG_PROGRESS_VALUE;
2923     }
2924     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2925         total += COMPONENT_PROGRESS_VALUE;
2926
2927     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2928         total += file->FileSize;
2929
2930     msi_ui_progress( package, 0, total, 0, 0 );
2931
2932     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2933     {
2934         TRACE("Feature: %s Installed %d Request %d Action %d\n",
2935               debugstr_w(feature->Feature), feature->Installed,
2936               feature->ActionRequest, feature->Action);
2937     }
2938     return ERROR_SUCCESS;
2939 }
2940
2941 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2942 {
2943     MSIPACKAGE* package = param;
2944     LPCWSTR cond = NULL; 
2945     LPCWSTR message = NULL;
2946     UINT r;
2947
2948     static const WCHAR title[]=
2949         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2950
2951     cond = MSI_RecordGetString(row,1);
2952
2953     r = MSI_EvaluateConditionW(package,cond);
2954     if (r == MSICONDITION_FALSE)
2955     {
2956         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2957         {
2958             LPWSTR deformated;
2959             message = MSI_RecordGetString(row,2);
2960             deformat_string(package,message,&deformated);
2961             MessageBoxW(NULL,deformated,title,MB_OK);
2962             msi_free(deformated);
2963         }
2964
2965         return ERROR_INSTALL_FAILURE;
2966     }
2967
2968     return ERROR_SUCCESS;
2969 }
2970
2971 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2972 {
2973     static const WCHAR query[] = {
2974         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2975         '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2976     MSIQUERY *view;
2977     UINT rc;
2978
2979     TRACE("Checking launch conditions\n");
2980
2981     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2982     if (rc != ERROR_SUCCESS)
2983         return ERROR_SUCCESS;
2984
2985     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2986     msiobj_release(&view->hdr);
2987     return rc;
2988 }
2989
2990 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2991 {
2992
2993     if (!cmp->KeyPath)
2994         return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2995
2996     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2997     {
2998         static const WCHAR query[] = {
2999             'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3000             '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3001             '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3002         static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3003         static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3004         MSIRECORD *row;
3005         UINT root, len;
3006         LPWSTR deformated, buffer, deformated_name;
3007         LPCWSTR key, name;
3008
3009         row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3010         if (!row)
3011             return NULL;
3012
3013         root = MSI_RecordGetInteger(row,2);
3014         key = MSI_RecordGetString(row, 3);
3015         name = MSI_RecordGetString(row, 4);
3016         deformat_string(package, key , &deformated);
3017         deformat_string(package, name, &deformated_name);
3018
3019         len = strlenW(deformated) + 6;
3020         if (deformated_name)
3021             len+=strlenW(deformated_name);
3022
3023         buffer = msi_alloc( len *sizeof(WCHAR));
3024
3025         if (deformated_name)
3026             sprintfW(buffer,fmt2,root,deformated,deformated_name);
3027         else
3028             sprintfW(buffer,fmt,root,deformated);
3029
3030         msi_free(deformated);
3031         msi_free(deformated_name);
3032         msiobj_release(&row->hdr);
3033
3034         return buffer;
3035     }
3036     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3037     {
3038         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3039         return NULL;
3040     }
3041     else
3042     {
3043         MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3044
3045         if (file)
3046             return strdupW( file->TargetPath );
3047     }
3048     return NULL;
3049 }
3050
3051 static HKEY openSharedDLLsKey(void)
3052 {
3053     HKEY hkey=0;
3054     static const WCHAR path[] =
3055         {'S','o','f','t','w','a','r','e','\\',
3056          'M','i','c','r','o','s','o','f','t','\\',
3057          'W','i','n','d','o','w','s','\\',
3058          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3059          'S','h','a','r','e','d','D','L','L','s',0};
3060
3061     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3062     return hkey;
3063 }
3064
3065 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3066 {
3067     HKEY hkey;
3068     DWORD count=0;
3069     DWORD type;
3070     DWORD sz = sizeof(count);
3071     DWORD rc;
3072     
3073     hkey = openSharedDLLsKey();
3074     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3075     if (rc != ERROR_SUCCESS)
3076         count = 0;
3077     RegCloseKey(hkey);
3078     return count;
3079 }
3080
3081 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3082 {
3083     HKEY hkey;
3084
3085     hkey = openSharedDLLsKey();
3086     if (count > 0)
3087         msi_reg_set_val_dword( hkey, path, count );
3088     else
3089         RegDeleteValueW(hkey,path);
3090     RegCloseKey(hkey);
3091     return count;
3092 }
3093
3094 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3095 {
3096     MSIFEATURE *feature;
3097     INT count = 0;
3098     BOOL write = FALSE;
3099
3100     /* only refcount DLLs */
3101     if (comp->KeyPath == NULL || 
3102         comp->assembly ||
3103         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
3104         comp->Attributes & msidbComponentAttributesODBCDataSource)
3105         write = FALSE;
3106     else
3107     {
3108         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3109         write = (count > 0);
3110
3111         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3112             write = TRUE;
3113     }
3114
3115     /* increment counts */
3116     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3117     {
3118         ComponentList *cl;
3119
3120         if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3121             continue;
3122
3123         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3124         {
3125             if ( cl->component == comp )
3126                 count++;
3127         }
3128     }
3129
3130     /* decrement counts */
3131     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3132     {
3133         ComponentList *cl;
3134
3135         if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3136             continue;
3137
3138         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3139         {
3140             if ( cl->component == comp )
3141                 count--;
3142         }
3143     }
3144
3145     /* ref count all the files in the component */
3146     if (write)
3147     {
3148         MSIFILE *file;
3149
3150         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3151         {
3152             if (file->Component == comp)
3153                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3154         }
3155     }
3156     
3157     /* add a count for permanent */
3158     if (comp->Attributes & msidbComponentAttributesPermanent)
3159         count ++;
3160     
3161     comp->RefCount = count;
3162
3163     if (write)
3164         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3165 }
3166
3167 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3168 {
3169     if (comp->assembly)
3170     {
3171         const WCHAR prefixW[] = {'<','\\',0};
3172         DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3173         WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3174
3175         if (keypath)
3176         {
3177             strcpyW( keypath, prefixW );
3178             strcatW( keypath, comp->assembly->display_name );
3179         }
3180         return keypath;
3181     }
3182     return resolve_keypath( package, comp );
3183 }
3184
3185 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3186 {
3187     WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3188     UINT rc;
3189     MSICOMPONENT *comp;
3190     HKEY hkey;
3191
3192     TRACE("\n");
3193
3194     squash_guid(package->ProductCode,squished_pc);
3195     msi_set_sourcedir_props(package, FALSE);
3196
3197     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3198     {
3199         MSIRECORD *uirow;
3200         INSTALLSTATE action;
3201
3202         msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3203         if (!comp->ComponentId)
3204             continue;
3205
3206         squash_guid( comp->ComponentId, squished_cc );
3207         msi_free( comp->FullKeypath );
3208         comp->FullKeypath = build_full_keypath( package, comp );
3209
3210         ACTION_RefCountComponent( package, comp );
3211
3212         if (package->need_rollback) action = comp->Installed;
3213         else action = comp->ActionRequest;
3214
3215         TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3216                             debugstr_w(comp->Component), debugstr_w(squished_cc),
3217                             debugstr_w(comp->FullKeypath), comp->RefCount, action);
3218
3219         if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3220         {
3221             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3222                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3223             else
3224                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3225
3226             if (rc != ERROR_SUCCESS)
3227                 continue;
3228
3229             if (comp->Attributes & msidbComponentAttributesPermanent)
3230             {
3231                 static const WCHAR szPermKey[] =
3232                     { '0','0','0','0','0','0','0','0','0','0','0','0',
3233                       '0','0','0','0','0','0','0','0','0','0','0','0',
3234                       '0','0','0','0','0','0','0','0',0 };
3235
3236                 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3237             }
3238             if (action == INSTALLSTATE_LOCAL)
3239                 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3240             else
3241             {
3242                 MSIFILE *file;
3243                 MSIRECORD *row;
3244                 LPWSTR ptr, ptr2;
3245                 WCHAR source[MAX_PATH];
3246                 WCHAR base[MAX_PATH];
3247                 LPWSTR sourcepath;
3248
3249                 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3250                 static const WCHAR query[] = {
3251                     'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3252                     '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3253                     '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3254                     '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3255                     '`','D','i','s','k','I','d','`',0};
3256
3257                 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3258                     continue;
3259
3260                 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3261                 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3262                 ptr2 = strrchrW(source, '\\') + 1;
3263                 msiobj_release(&row->hdr);
3264
3265                 lstrcpyW(base, package->PackagePath);
3266                 ptr = strrchrW(base, '\\');
3267                 *(ptr + 1) = '\0';
3268
3269                 sourcepath = msi_resolve_file_source(package, file);
3270                 ptr = sourcepath + lstrlenW(base);
3271                 lstrcpyW(ptr2, ptr);
3272                 msi_free(sourcepath);
3273
3274                 msi_reg_set_val_str(hkey, squished_pc, source);
3275             }
3276             RegCloseKey(hkey);
3277         }
3278         else if (action == INSTALLSTATE_ABSENT)
3279         {
3280             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3281                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3282             else
3283                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3284         }
3285
3286         /* UI stuff */
3287         uirow = MSI_CreateRecord(3);
3288         MSI_RecordSetStringW(uirow,1,package->ProductCode);
3289         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3290         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3291         msi_ui_actiondata( package, szProcessComponents, uirow );
3292         msiobj_release( &uirow->hdr );
3293     }
3294     return ERROR_SUCCESS;
3295 }
3296
3297 typedef struct {
3298     CLSID       clsid;
3299     LPWSTR      source;
3300
3301     LPWSTR      path;
3302     ITypeLib    *ptLib;
3303 } typelib_struct;
3304
3305 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
3306                                        LPWSTR lpszName, LONG_PTR lParam)
3307 {
3308     TLIBATTR *attr;
3309     typelib_struct *tl_struct = (typelib_struct*) lParam;
3310     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3311     int sz; 
3312     HRESULT res;
3313
3314     if (!IS_INTRESOURCE(lpszName))
3315     {
3316         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3317         return TRUE;
3318     }
3319
3320     sz = strlenW(tl_struct->source)+4;
3321     sz *= sizeof(WCHAR);
3322
3323     if ((INT_PTR)lpszName == 1)
3324         tl_struct->path = strdupW(tl_struct->source);
3325     else
3326     {
3327         tl_struct->path = msi_alloc(sz);
3328         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3329     }
3330
3331     TRACE("trying %s\n", debugstr_w(tl_struct->path));
3332     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3333     if (FAILED(res))
3334     {
3335         msi_free(tl_struct->path);
3336         tl_struct->path = NULL;
3337
3338         return TRUE;
3339     }
3340
3341     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3342     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3343     {
3344         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3345         return FALSE;
3346     }
3347
3348     msi_free(tl_struct->path);
3349     tl_struct->path = NULL;
3350
3351     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3352     ITypeLib_Release(tl_struct->ptLib);
3353
3354     return TRUE;
3355 }
3356
3357 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3358 {
3359     MSIPACKAGE* package = param;
3360     LPCWSTR component;
3361     MSICOMPONENT *comp;
3362     MSIFILE *file;
3363     typelib_struct tl_struct;
3364     ITypeLib *tlib;
3365     HMODULE module;
3366     HRESULT hr;
3367
3368     component = MSI_RecordGetString(row,3);
3369     comp = msi_get_loaded_component(package,component);
3370     if (!comp)
3371         return ERROR_SUCCESS;
3372
3373     comp->Action = msi_get_component_action( package, comp );
3374     if (comp->Action != INSTALLSTATE_LOCAL)
3375     {
3376         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3377         return ERROR_SUCCESS;
3378     }
3379
3380     if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3381     {
3382         TRACE("component has no key path\n");
3383         return ERROR_SUCCESS;
3384     }
3385     msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3386
3387     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3388     if (module)
3389     {
3390         LPCWSTR guid;
3391         guid = MSI_RecordGetString(row,1);
3392         CLSIDFromString( guid, &tl_struct.clsid);
3393         tl_struct.source = strdupW( file->TargetPath );
3394         tl_struct.path = NULL;
3395
3396         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3397                         (LONG_PTR)&tl_struct);
3398
3399         if (tl_struct.path)
3400         {
3401             LPCWSTR helpid, help_path = NULL;
3402             HRESULT res;
3403
3404             helpid = MSI_RecordGetString(row,6);
3405
3406             if (helpid) help_path = msi_get_target_folder( package, helpid );
3407             res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3408
3409             if (FAILED(res))
3410                 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3411             else
3412                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3413
3414             ITypeLib_Release(tl_struct.ptLib);
3415             msi_free(tl_struct.path);
3416         }
3417         else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3418
3419         FreeLibrary(module);
3420         msi_free(tl_struct.source);
3421     }
3422     else
3423     {
3424         hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3425         if (FAILED(hr))
3426         {
3427             ERR("Failed to load type library: %08x\n", hr);
3428             return ERROR_INSTALL_FAILURE;
3429         }
3430
3431         ITypeLib_Release(tlib);
3432     }
3433
3434     return ERROR_SUCCESS;
3435 }
3436
3437 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3438 {
3439     static const WCHAR query[] = {
3440         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3441         '`','T','y','p','e','L','i','b','`',0};
3442     MSIQUERY *view;
3443     UINT rc;
3444
3445     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3446     if (rc != ERROR_SUCCESS)
3447         return ERROR_SUCCESS;
3448
3449     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3450     msiobj_release(&view->hdr);
3451     return rc;
3452 }
3453
3454 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3455 {
3456     MSIPACKAGE *package = param;
3457     LPCWSTR component, guid;
3458     MSICOMPONENT *comp;
3459     GUID libid;
3460     UINT version;
3461     LCID language;
3462     SYSKIND syskind;
3463     HRESULT hr;
3464
3465     component = MSI_RecordGetString( row, 3 );
3466     comp = msi_get_loaded_component( package, component );
3467     if (!comp)
3468         return ERROR_SUCCESS;
3469
3470     comp->Action = msi_get_component_action( package, comp );
3471     if (comp->Action != INSTALLSTATE_ABSENT)
3472     {
3473         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3474         return ERROR_SUCCESS;
3475     }
3476     msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3477
3478     guid = MSI_RecordGetString( row, 1 );
3479     CLSIDFromString( guid, &libid );
3480     version = MSI_RecordGetInteger( row, 4 );
3481     language = MSI_RecordGetInteger( row, 2 );
3482
3483 #ifdef _WIN64
3484     syskind = SYS_WIN64;
3485 #else
3486     syskind = SYS_WIN32;
3487 #endif
3488
3489     hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3490     if (FAILED(hr))
3491     {
3492         WARN("Failed to unregister typelib: %08x\n", hr);
3493     }
3494
3495     return ERROR_SUCCESS;
3496 }
3497
3498 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3499 {
3500     static const WCHAR query[] = {
3501         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3502         '`','T','y','p','e','L','i','b','`',0};
3503     MSIQUERY *view;
3504     UINT rc;
3505
3506     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3507     if (rc != ERROR_SUCCESS)
3508         return ERROR_SUCCESS;
3509
3510     rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3511     msiobj_release( &view->hdr );
3512     return rc;
3513 }
3514
3515 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3516 {
3517     static const WCHAR szlnk[] = {'.','l','n','k',0};
3518     LPCWSTR directory, extension, link_folder;
3519     LPWSTR link_file, filename;
3520
3521     directory = MSI_RecordGetString( row, 2 );
3522     link_folder = msi_get_target_folder( package, directory );
3523     if (!link_folder)
3524     {
3525         ERR("unable to resolve folder %s\n", debugstr_w(directory));
3526         return NULL;
3527     }
3528     /* may be needed because of a bug somewhere else */
3529     msi_create_full_path( link_folder );
3530
3531     filename = msi_dup_record_field( row, 3 );
3532     msi_reduce_to_long_filename( filename );
3533
3534     extension = strchrW( filename, '.' );
3535     if (!extension || strcmpiW( extension, szlnk ))
3536     {
3537         int len = strlenW( filename );
3538         filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3539         memcpy( filename + len, szlnk, sizeof(szlnk) );
3540     }
3541     link_file = msi_build_directory_name( 2, link_folder, filename );
3542     msi_free( filename );
3543
3544     return link_file;
3545 }
3546
3547 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3548 {
3549     static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3550     static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3551     WCHAR *folder, *dest, *path;
3552
3553     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3554         folder = msi_dup_property( package->db, szWindowsFolder );
3555     else
3556     {
3557         WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3558         folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3559         msi_free( appdata );
3560     }
3561     dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3562     msi_create_full_path( dest );
3563     path = msi_build_directory_name( 2, dest, icon_name );
3564     msi_free( folder );
3565     msi_free( dest );
3566     return path;
3567 }
3568
3569 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3570 {
3571     MSIPACKAGE *package = param;
3572     LPWSTR link_file, deformated, path;
3573     LPCWSTR component, target;
3574     MSICOMPONENT *comp;
3575     IShellLinkW *sl = NULL;
3576     IPersistFile *pf = NULL;
3577     HRESULT res;
3578
3579     component = MSI_RecordGetString(row, 4);
3580     comp = msi_get_loaded_component(package, component);
3581     if (!comp)
3582         return ERROR_SUCCESS;
3583
3584     comp->Action = msi_get_component_action( package, comp );
3585     if (comp->Action != INSTALLSTATE_LOCAL)
3586     {
3587         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3588         return ERROR_SUCCESS;
3589     }
3590     msi_ui_actiondata( package, szCreateShortcuts, row );
3591
3592     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3593                     &IID_IShellLinkW, (LPVOID *) &sl );
3594
3595     if (FAILED( res ))
3596     {
3597         ERR("CLSID_ShellLink not available\n");
3598         goto err;
3599     }
3600
3601     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3602     if (FAILED( res ))
3603     {
3604         ERR("QueryInterface(IID_IPersistFile) failed\n");
3605         goto err;
3606     }
3607
3608     target = MSI_RecordGetString(row, 5);
3609     if (strchrW(target, '['))
3610     {
3611         int len;
3612         WCHAR *format_string, *p;
3613
3614         if (!(p = strchrW( target, ']' ))) goto err;
3615         len = p - target + 1;
3616         format_string = msi_alloc( (len + 1) * sizeof(WCHAR) );
3617         memcpy( format_string, target, len * sizeof(WCHAR) );
3618         format_string[len] = 0;
3619         deformat_string( package, format_string, &deformated );
3620         msi_free( format_string );
3621
3622         path = msi_alloc( (strlenW( deformated ) + strlenW( p + 1 ) + 2) * sizeof(WCHAR) );
3623         strcpyW( path, deformated );
3624         PathAddBackslashW( path );
3625         strcatW( path, p + 1 );
3626         TRACE("target path is %s\n", debugstr_w(path));
3627
3628         IShellLinkW_SetPath( sl, path );
3629         msi_free( deformated );
3630         msi_free( path );
3631     }
3632     else
3633     {
3634         FIXME("poorly handled shortcut format, advertised shortcut\n");
3635         IShellLinkW_SetPath(sl,comp->FullKeypath);
3636     }
3637
3638     if (!MSI_RecordIsNull(row,6))
3639     {
3640         LPCWSTR arguments = MSI_RecordGetString(row, 6);
3641         deformat_string(package, arguments, &deformated);
3642         IShellLinkW_SetArguments(sl,deformated);
3643         msi_free(deformated);
3644     }
3645
3646     if (!MSI_RecordIsNull(row,7))
3647     {
3648         LPCWSTR description = MSI_RecordGetString(row, 7);
3649         IShellLinkW_SetDescription(sl, description);
3650     }
3651
3652     if (!MSI_RecordIsNull(row,8))
3653         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3654
3655     if (!MSI_RecordIsNull(row,9))
3656     {
3657         INT index; 
3658         LPCWSTR icon = MSI_RecordGetString(row, 9);
3659
3660         path = msi_build_icon_path(package, icon);
3661         index = MSI_RecordGetInteger(row,10);
3662
3663         /* no value means 0 */
3664         if (index == MSI_NULL_INTEGER)
3665             index = 0;
3666
3667         IShellLinkW_SetIconLocation(sl, path, index);
3668         msi_free(path);
3669     }
3670
3671     if (!MSI_RecordIsNull(row,11))
3672         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3673
3674     if (!MSI_RecordIsNull(row,12))
3675     {
3676         LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3677         full_path = msi_get_target_folder( package, wkdir );
3678         if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3679     }
3680     link_file = get_link_file(package, row);
3681
3682     TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3683     IPersistFile_Save(pf, link_file, FALSE);
3684     msi_free(link_file);
3685
3686 err:
3687     if (pf)
3688         IPersistFile_Release( pf );
3689     if (sl)
3690         IShellLinkW_Release( sl );
3691
3692     return ERROR_SUCCESS;
3693 }
3694
3695 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3696 {
3697     static const WCHAR query[] = {
3698         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3699         '`','S','h','o','r','t','c','u','t','`',0};
3700     MSIQUERY *view;
3701     HRESULT res;
3702     UINT rc;
3703
3704     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3705     if (rc != ERROR_SUCCESS)
3706         return ERROR_SUCCESS;
3707
3708     res = CoInitialize( NULL );
3709
3710     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3711     msiobj_release(&view->hdr);
3712
3713     if (SUCCEEDED(res)) CoUninitialize();
3714     return rc;
3715 }
3716
3717 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3718 {
3719     MSIPACKAGE *package = param;
3720     LPWSTR link_file;
3721     LPCWSTR component;
3722     MSICOMPONENT *comp;
3723
3724     component = MSI_RecordGetString( row, 4 );
3725     comp = msi_get_loaded_component( package, component );
3726     if (!comp)
3727         return ERROR_SUCCESS;
3728
3729     comp->Action = msi_get_component_action( package, comp );
3730     if (comp->Action != INSTALLSTATE_ABSENT)
3731     {
3732         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3733         return ERROR_SUCCESS;
3734     }
3735     msi_ui_actiondata( package, szRemoveShortcuts, row );
3736
3737     link_file = get_link_file( package, row );
3738
3739     TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3740     if (!DeleteFileW( link_file ))
3741     {
3742         WARN("Failed to remove shortcut file %u\n", GetLastError());
3743     }
3744     msi_free( link_file );
3745
3746     return ERROR_SUCCESS;
3747 }
3748
3749 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3750 {
3751     static const WCHAR query[] = {
3752         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3753         '`','S','h','o','r','t','c','u','t','`',0};
3754     MSIQUERY *view;
3755     UINT rc;
3756
3757     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3758     if (rc != ERROR_SUCCESS)
3759         return ERROR_SUCCESS;
3760
3761     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3762     msiobj_release( &view->hdr );
3763     return rc;
3764 }
3765
3766 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3767 {
3768     MSIPACKAGE* package = param;
3769     HANDLE the_file;
3770     LPWSTR FilePath;
3771     LPCWSTR FileName;
3772     CHAR buffer[1024];
3773     DWORD sz;
3774     UINT rc;
3775
3776     FileName = MSI_RecordGetString(row,1);
3777     if (!FileName)
3778     {
3779         ERR("Unable to get FileName\n");
3780         return ERROR_SUCCESS;
3781     }
3782
3783     FilePath = msi_build_icon_path(package, FileName);
3784
3785     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3786
3787     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3788                         FILE_ATTRIBUTE_NORMAL, NULL);
3789
3790     if (the_file == INVALID_HANDLE_VALUE)
3791     {
3792         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3793         msi_free(FilePath);
3794         return ERROR_SUCCESS;
3795     }
3796
3797     do 
3798     {
3799         DWORD write;
3800         sz = 1024;
3801         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3802         if (rc != ERROR_SUCCESS)
3803         {
3804             ERR("Failed to get stream\n");
3805             CloseHandle(the_file);  
3806             DeleteFileW(FilePath);
3807             break;
3808         }
3809         WriteFile(the_file,buffer,sz,&write,NULL);
3810     } while (sz == 1024);
3811
3812     msi_free(FilePath);
3813     CloseHandle(the_file);
3814
3815     return ERROR_SUCCESS;
3816 }
3817
3818 static UINT msi_publish_icons(MSIPACKAGE *package)
3819 {
3820     static const WCHAR query[]= {
3821         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3822         '`','I','c','o','n','`',0};
3823     MSIQUERY *view;
3824     UINT r;
3825
3826     r = MSI_DatabaseOpenViewW(package->db, query, &view);
3827     if (r == ERROR_SUCCESS)
3828     {
3829         r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3830         msiobj_release(&view->hdr);
3831         if (r != ERROR_SUCCESS)
3832             return r;
3833     }
3834     return ERROR_SUCCESS;
3835 }
3836
3837 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3838 {
3839     UINT r;
3840     HKEY source;
3841     LPWSTR buffer;
3842     MSIMEDIADISK *disk;
3843     MSISOURCELISTINFO *info;
3844
3845     r = RegCreateKeyW(hkey, szSourceList, &source);
3846     if (r != ERROR_SUCCESS)
3847         return r;
3848
3849     RegCloseKey(source);
3850
3851     buffer = strrchrW(package->PackagePath, '\\') + 1;
3852     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3853                               package->Context, MSICODE_PRODUCT,
3854                               INSTALLPROPERTY_PACKAGENAMEW, buffer);
3855     if (r != ERROR_SUCCESS)
3856         return r;
3857
3858     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3859                               package->Context, MSICODE_PRODUCT,
3860                               INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3861     if (r != ERROR_SUCCESS)
3862         return r;
3863
3864     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3865                               package->Context, MSICODE_PRODUCT,
3866                               INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3867     if (r != ERROR_SUCCESS)
3868         return r;
3869
3870     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3871     {
3872         if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3873             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3874                                      info->options, info->value);
3875         else
3876             MsiSourceListSetInfoW(package->ProductCode, NULL,
3877                                   info->context, info->options,
3878                                   info->property, info->value);
3879     }
3880
3881     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3882     {
3883         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3884                                    disk->context, disk->options,
3885                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3886     }
3887
3888     return ERROR_SUCCESS;
3889 }
3890
3891 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3892 {
3893     MSIHANDLE hdb, suminfo;
3894     WCHAR guids[MAX_PATH];
3895     WCHAR packcode[SQUISH_GUID_SIZE];
3896     LPWSTR buffer;
3897     LPWSTR ptr;
3898     DWORD langid;
3899     DWORD size;
3900     UINT r;
3901
3902     static const WCHAR szARPProductIcon[] =
3903         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3904     static const WCHAR szAssignment[] =
3905         {'A','s','s','i','g','n','m','e','n','t',0};
3906     static const WCHAR szAdvertiseFlags[] =
3907         {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3908     static const WCHAR szClients[] =
3909         {'C','l','i','e','n','t','s',0};
3910     static const WCHAR szColon[] = {':',0};
3911
3912     buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3913     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3914     msi_free(buffer);
3915
3916     langid = msi_get_property_int(package->db, szProductLanguage, 0);
3917     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3918
3919     /* FIXME */
3920     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3921
3922     buffer = msi_dup_property(package->db, szARPProductIcon);
3923     if (buffer)
3924     {
3925         LPWSTR path = msi_build_icon_path(package, buffer);
3926         msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3927         msi_free(path);
3928         msi_free(buffer);
3929     }
3930
3931     buffer = msi_dup_property(package->db, szProductVersion);
3932     if (buffer)
3933     {
3934         DWORD verdword = msi_version_str_to_dword(buffer);
3935         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3936         msi_free(buffer);
3937     }
3938
3939     msi_reg_set_val_dword(hkey, szAssignment, 0);
3940     msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3941     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3942     msi_reg_set_val_str(hkey, szClients, szColon);
3943
3944     hdb = alloc_msihandle(&package->db->hdr);
3945     if (!hdb)
3946         return ERROR_NOT_ENOUGH_MEMORY;
3947
3948     r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3949     MsiCloseHandle(hdb);
3950     if (r != ERROR_SUCCESS)
3951         goto done;
3952
3953     size = MAX_PATH;
3954     r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3955                                    NULL, guids, &size);
3956     if (r != ERROR_SUCCESS)
3957         goto done;
3958
3959     ptr = strchrW(guids, ';');
3960     if (ptr) *ptr = 0;
3961     squash_guid(guids, packcode);
3962     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3963
3964 done:
3965     MsiCloseHandle(suminfo);
3966     return ERROR_SUCCESS;
3967 }
3968
3969 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3970 {
3971     UINT r;
3972     HKEY hkey;
3973     LPWSTR upgrade;
3974     WCHAR squashed_pc[SQUISH_GUID_SIZE];
3975
3976     upgrade = msi_dup_property(package->db, szUpgradeCode);
3977     if (!upgrade)
3978         return ERROR_SUCCESS;
3979
3980     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3981         r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3982     else
3983         r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3984
3985     if (r != ERROR_SUCCESS)
3986     {
3987         WARN("failed to open upgrade code key\n");
3988         msi_free(upgrade);
3989         return ERROR_SUCCESS;
3990     }
3991     squash_guid(package->ProductCode, squashed_pc);
3992     msi_reg_set_val_str(hkey, squashed_pc, NULL);
3993     RegCloseKey(hkey);
3994     msi_free(upgrade);
3995     return ERROR_SUCCESS;
3996 }
3997
3998 static BOOL msi_check_publish(MSIPACKAGE *package)
3999 {
4000     MSIFEATURE *feature;
4001
4002     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4003     {
4004         feature->Action = msi_get_feature_action( package, feature );
4005         if (feature->Action == INSTALLSTATE_LOCAL)
4006             return TRUE;
4007     }
4008
4009     return FALSE;
4010 }
4011
4012 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4013 {
4014     MSIFEATURE *feature;
4015
4016     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4017     {
4018         feature->Action = msi_get_feature_action( package, feature );
4019         if (feature->Action != INSTALLSTATE_ABSENT)
4020             return FALSE;
4021     }
4022
4023     return TRUE;
4024 }
4025
4026 static UINT msi_publish_patches( MSIPACKAGE *package )
4027 {
4028     static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4029     WCHAR patch_squashed[GUID_SIZE];
4030     HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4031     LONG res;
4032     MSIPATCHINFO *patch;
4033     UINT r;
4034     WCHAR *p, *all_patches = NULL;
4035     DWORD len = 0;
4036
4037     r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4038     if (r != ERROR_SUCCESS)
4039         return ERROR_FUNCTION_FAILED;
4040
4041     res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4042     if (res != ERROR_SUCCESS)
4043     {
4044         r = ERROR_FUNCTION_FAILED;
4045         goto done;
4046     }
4047
4048     r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4049     if (r != ERROR_SUCCESS)
4050         goto done;
4051
4052     LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4053     {
4054         squash_guid( patch->patchcode, patch_squashed );
4055         len += strlenW( patch_squashed ) + 1;
4056     }
4057
4058     p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4059     if (!all_patches)
4060         goto done;
4061
4062     LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4063     {
4064         HKEY patch_key;
4065
4066         squash_guid( patch->patchcode, p );
4067         p += strlenW( p ) + 1;
4068
4069         res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4070                               (const BYTE *)patch->transforms,
4071                               (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4072         if (res != ERROR_SUCCESS)
4073             goto done;
4074
4075         r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4076         if (r != ERROR_SUCCESS)
4077             goto done;
4078
4079         res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4080                               (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4081         RegCloseKey( patch_key );
4082         if (res != ERROR_SUCCESS)
4083             goto done;
4084
4085         if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4086         {
4087             res = GetLastError();
4088             ERR("Unable to copy patch package %d\n", res);
4089             goto done;
4090         }
4091         res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4092         if (res != ERROR_SUCCESS)
4093             goto done;
4094
4095         res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4096         RegCloseKey( patch_key );
4097         if (res != ERROR_SUCCESS)
4098             goto done;
4099     }
4100
4101     all_patches[len] = 0;
4102     res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4103                           (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4104     if (res != ERROR_SUCCESS)
4105         goto done;
4106
4107     res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4108                           (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4109     if (res != ERROR_SUCCESS)
4110         r = ERROR_FUNCTION_FAILED;
4111
4112 done:
4113     RegCloseKey( product_patches_key );
4114     RegCloseKey( patches_key );
4115     RegCloseKey( product_key );
4116     msi_free( all_patches );
4117     return r;
4118 }
4119
4120 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4121 {
4122     UINT rc;
4123     HKEY hukey = NULL, hudkey = NULL;
4124     MSIRECORD *uirow;
4125
4126     if (!list_empty(&package->patches))
4127     {
4128         rc = msi_publish_patches(package);
4129         if (rc != ERROR_SUCCESS)
4130             goto end;
4131     }
4132
4133     /* FIXME: also need to publish if the product is in advertise mode */
4134     if (!msi_check_publish(package))
4135         return ERROR_SUCCESS;
4136
4137     rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4138                                &hukey, TRUE);
4139     if (rc != ERROR_SUCCESS)
4140         goto end;
4141
4142     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4143                                        NULL, &hudkey, TRUE);
4144     if (rc != ERROR_SUCCESS)
4145         goto end;
4146
4147     rc = msi_publish_upgrade_code(package);
4148     if (rc != ERROR_SUCCESS)
4149         goto end;
4150
4151     rc = msi_publish_product_properties(package, hukey);
4152     if (rc != ERROR_SUCCESS)
4153         goto end;
4154
4155     rc = msi_publish_sourcelist(package, hukey);
4156     if (rc != ERROR_SUCCESS)
4157         goto end;
4158
4159     rc = msi_publish_icons(package);
4160
4161 end:
4162     uirow = MSI_CreateRecord( 1 );
4163     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4164     msi_ui_actiondata( package, szPublishProduct, uirow );
4165     msiobj_release( &uirow->hdr );
4166
4167     RegCloseKey(hukey);
4168     RegCloseKey(hudkey);
4169     return rc;
4170 }
4171
4172 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4173 {
4174     WCHAR *filename, *ptr, *folder, *ret;
4175     const WCHAR *dirprop;
4176
4177     filename = msi_dup_record_field( row, 2 );
4178     if (filename && (ptr = strchrW( filename, '|' )))
4179         ptr++;
4180     else
4181         ptr = filename;
4182
4183     dirprop = MSI_RecordGetString( row, 3 );
4184     if (dirprop)
4185     {
4186         folder = strdupW( msi_get_target_folder( package, dirprop ) );
4187         if (!folder) folder = msi_dup_property( package->db, dirprop );
4188     }
4189     else
4190         folder = msi_dup_property( package->db, szWindowsFolder );
4191
4192     if (!folder)
4193     {
4194         ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4195         msi_free( filename );
4196         return NULL;
4197     }
4198
4199     ret = msi_build_directory_name( 2, folder, ptr );
4200
4201     msi_free( filename );
4202     msi_free( folder );
4203     return ret;
4204 }
4205
4206 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4207 {
4208     MSIPACKAGE *package = param;
4209     LPCWSTR component, section, key, value, identifier;
4210     LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4211     MSIRECORD * uirow;
4212     INT action;
4213     MSICOMPONENT *comp;
4214
4215     component = MSI_RecordGetString(row, 8);
4216     comp = msi_get_loaded_component(package,component);
4217     if (!comp)
4218         return ERROR_SUCCESS;
4219
4220     comp->Action = msi_get_component_action( package, comp );
4221     if (comp->Action != INSTALLSTATE_LOCAL)
4222     {
4223         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4224         return ERROR_SUCCESS;
4225     }
4226
4227     identifier = MSI_RecordGetString(row,1); 
4228     section = MSI_RecordGetString(row,4);
4229     key = MSI_RecordGetString(row,5);
4230     value = MSI_RecordGetString(row,6);
4231     action = MSI_RecordGetInteger(row,7);
4232
4233     deformat_string(package,section,&deformated_section);
4234     deformat_string(package,key,&deformated_key);
4235     deformat_string(package,value,&deformated_value);
4236
4237     fullname = get_ini_file_name(package, row);
4238
4239     if (action == 0)
4240     {
4241         TRACE("Adding value %s to section %s in %s\n",
4242                 debugstr_w(deformated_key), debugstr_w(deformated_section),
4243                 debugstr_w(fullname));
4244         WritePrivateProfileStringW(deformated_section, deformated_key,
4245                                    deformated_value, fullname);
4246     }
4247     else if (action == 1)
4248     {
4249         WCHAR returned[10];
4250         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4251                                  returned, 10, fullname);
4252         if (returned[0] == 0)
4253         {
4254             TRACE("Adding value %s to section %s in %s\n",
4255                     debugstr_w(deformated_key), debugstr_w(deformated_section),
4256                     debugstr_w(fullname));
4257
4258             WritePrivateProfileStringW(deformated_section, deformated_key,
4259                                        deformated_value, fullname);
4260         }
4261     }
4262     else if (action == 3)
4263         FIXME("Append to existing section not yet implemented\n");
4264
4265     uirow = MSI_CreateRecord(4);
4266     MSI_RecordSetStringW(uirow,1,identifier);
4267     MSI_RecordSetStringW(uirow,2,deformated_section);
4268     MSI_RecordSetStringW(uirow,3,deformated_key);
4269     MSI_RecordSetStringW(uirow,4,deformated_value);
4270     msi_ui_actiondata( package, szWriteIniValues, uirow );
4271     msiobj_release( &uirow->hdr );
4272
4273     msi_free(fullname);
4274     msi_free(deformated_key);
4275     msi_free(deformated_value);
4276     msi_free(deformated_section);
4277     return ERROR_SUCCESS;
4278 }
4279
4280 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4281 {
4282     static const WCHAR query[] = {
4283         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4284         '`','I','n','i','F','i','l','e','`',0};
4285     MSIQUERY *view;
4286     UINT rc;
4287
4288     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4289     if (rc != ERROR_SUCCESS)
4290         return ERROR_SUCCESS;
4291
4292     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4293     msiobj_release(&view->hdr);
4294     return rc;
4295 }
4296
4297 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4298 {
4299     MSIPACKAGE *package = param;
4300     LPCWSTR component, section, key, value, identifier;
4301     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4302     MSICOMPONENT *comp;
4303     MSIRECORD *uirow;
4304     INT action;
4305
4306     component = MSI_RecordGetString( row, 8 );
4307     comp = msi_get_loaded_component( package, component );
4308     if (!comp)
4309         return ERROR_SUCCESS;
4310
4311     comp->Action = msi_get_component_action( package, comp );
4312     if (comp->Action != INSTALLSTATE_ABSENT)
4313     {
4314         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4315         return ERROR_SUCCESS;
4316     }
4317
4318     identifier = MSI_RecordGetString( row, 1 );
4319     section = MSI_RecordGetString( row, 4 );
4320     key = MSI_RecordGetString( row, 5 );
4321     value = MSI_RecordGetString( row, 6 );
4322     action = MSI_RecordGetInteger( row, 7 );
4323
4324     deformat_string( package, section, &deformated_section );
4325     deformat_string( package, key, &deformated_key );
4326     deformat_string( package, value, &deformated_value );
4327
4328     if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4329     {
4330         filename = get_ini_file_name( package, row );
4331
4332         TRACE("Removing key %s from section %s in %s\n",
4333                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4334
4335         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4336         {
4337             WARN("Unable to remove key %u\n", GetLastError());
4338         }
4339         msi_free( filename );
4340     }
4341     else
4342         FIXME("Unsupported action %d\n", action);
4343
4344
4345     uirow = MSI_CreateRecord( 4 );
4346     MSI_RecordSetStringW( uirow, 1, identifier );
4347     MSI_RecordSetStringW( uirow, 2, deformated_section );
4348     MSI_RecordSetStringW( uirow, 3, deformated_key );
4349     MSI_RecordSetStringW( uirow, 4, deformated_value );
4350     msi_ui_actiondata( package, szRemoveIniValues, uirow );
4351     msiobj_release( &uirow->hdr );
4352
4353     msi_free( deformated_key );
4354     msi_free( deformated_value );
4355     msi_free( deformated_section );
4356     return ERROR_SUCCESS;
4357 }
4358
4359 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4360 {
4361     MSIPACKAGE *package = param;
4362     LPCWSTR component, section, key, value, identifier;
4363     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4364     MSICOMPONENT *comp;
4365     MSIRECORD *uirow;
4366     INT action;
4367
4368     component = MSI_RecordGetString( row, 8 );
4369     comp = msi_get_loaded_component( package, component );
4370     if (!comp)
4371         return ERROR_SUCCESS;
4372
4373     comp->Action = msi_get_component_action( package, comp );
4374     if (comp->Action != INSTALLSTATE_LOCAL)
4375     {
4376         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4377         return ERROR_SUCCESS;
4378     }
4379
4380     identifier = MSI_RecordGetString( row, 1 );
4381     section = MSI_RecordGetString( row, 4 );
4382     key = MSI_RecordGetString( row, 5 );
4383     value = MSI_RecordGetString( row, 6 );
4384     action = MSI_RecordGetInteger( row, 7 );
4385
4386     deformat_string( package, section, &deformated_section );
4387     deformat_string( package, key, &deformated_key );
4388     deformat_string( package, value, &deformated_value );
4389
4390     if (action == msidbIniFileActionRemoveLine)
4391     {
4392         filename = get_ini_file_name( package, row );
4393
4394         TRACE("Removing key %s from section %s in %s\n",
4395                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4396
4397         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4398         {
4399             WARN("Unable to remove key %u\n", GetLastError());
4400         }
4401         msi_free( filename );
4402     }
4403     else
4404         FIXME("Unsupported action %d\n", action);
4405
4406     uirow = MSI_CreateRecord( 4 );
4407     MSI_RecordSetStringW( uirow, 1, identifier );
4408     MSI_RecordSetStringW( uirow, 2, deformated_section );
4409     MSI_RecordSetStringW( uirow, 3, deformated_key );
4410     MSI_RecordSetStringW( uirow, 4, deformated_value );
4411     msi_ui_actiondata( package, szRemoveIniValues, uirow );
4412     msiobj_release( &uirow->hdr );
4413
4414     msi_free( deformated_key );
4415     msi_free( deformated_value );
4416     msi_free( deformated_section );
4417     return ERROR_SUCCESS;
4418 }
4419
4420 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4421 {
4422     static const WCHAR query[] = {
4423         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4424         '`','I','n','i','F','i','l','e','`',0};
4425     static const WCHAR remove_query[] = {
4426         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4427         '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4428     MSIQUERY *view;
4429     UINT rc;
4430
4431     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4432     if (rc == ERROR_SUCCESS)
4433     {
4434         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4435         msiobj_release( &view->hdr );
4436         if (rc != ERROR_SUCCESS)
4437             return rc;
4438     }
4439     rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4440     if (rc == ERROR_SUCCESS)
4441     {
4442         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4443         msiobj_release( &view->hdr );
4444         if (rc != ERROR_SUCCESS)
4445             return rc;
4446     }
4447     return ERROR_SUCCESS;
4448 }
4449
4450 static void register_dll( const WCHAR *dll, BOOL unregister )
4451 {
4452     HMODULE hmod;
4453
4454     hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4455     if (hmod)
4456     {
4457         HRESULT (WINAPI *func_ptr)( void );
4458         const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4459
4460         func_ptr = (void *)GetProcAddress( hmod, func );
4461         if (func_ptr)
4462         {
4463             HRESULT hr = func_ptr();
4464             if (FAILED( hr ))
4465                 WARN("failed to register dll 0x%08x\n", hr);
4466         }
4467         else
4468             WARN("entry point %s not found\n", func);
4469         FreeLibrary( hmod );
4470         return;
4471     }
4472     WARN("failed to load library %u\n", GetLastError());
4473 }
4474
4475 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4476 {
4477     MSIPACKAGE *package = param;
4478     LPCWSTR filename;
4479     MSIFILE *file;
4480     MSIRECORD *uirow;
4481
4482     filename = MSI_RecordGetString(row,1);
4483     file = msi_get_loaded_file( package, filename );
4484     if (!file)
4485     {
4486         WARN("unable to find file %s\n", debugstr_w(filename));
4487         return ERROR_SUCCESS;
4488     }
4489     file->Component->Action = msi_get_component_action( package, file->Component );
4490     if (file->Component->Action != INSTALLSTATE_LOCAL)
4491     {
4492         TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4493         return ERROR_SUCCESS;
4494     }
4495
4496     TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4497     register_dll( file->TargetPath, FALSE );
4498
4499     uirow = MSI_CreateRecord( 2 );
4500     MSI_RecordSetStringW( uirow, 1, filename );
4501     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4502     msi_ui_actiondata( package, szSelfRegModules, uirow );
4503     msiobj_release( &uirow->hdr );
4504
4505     return ERROR_SUCCESS;
4506 }
4507
4508 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4509 {
4510     static const WCHAR query[] = {
4511         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4512         '`','S','e','l','f','R','e','g','`',0};
4513     MSIQUERY *view;
4514     UINT rc;
4515
4516     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4517     if (rc != ERROR_SUCCESS)
4518         return ERROR_SUCCESS;
4519
4520     rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4521     msiobj_release(&view->hdr);
4522     return rc;
4523 }
4524
4525 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4526 {
4527     MSIPACKAGE *package = param;
4528     LPCWSTR filename;
4529     MSIFILE *file;
4530     MSIRECORD *uirow;
4531
4532     filename = MSI_RecordGetString( row, 1 );
4533     file = msi_get_loaded_file( package, filename );
4534     if (!file)
4535     {
4536         WARN("unable to find file %s\n", debugstr_w(filename));
4537         return ERROR_SUCCESS;
4538     }
4539     file->Component->Action = msi_get_component_action( package, file->Component );
4540     if (file->Component->Action != INSTALLSTATE_ABSENT)
4541     {
4542         TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4543         return ERROR_SUCCESS;
4544     }
4545
4546     TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4547     register_dll( file->TargetPath, TRUE );
4548
4549     uirow = MSI_CreateRecord( 2 );
4550     MSI_RecordSetStringW( uirow, 1, filename );
4551     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4552     msi_ui_actiondata( package, szSelfUnregModules, uirow );
4553     msiobj_release( &uirow->hdr );
4554
4555     return ERROR_SUCCESS;
4556 }
4557
4558 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4559 {
4560     static const WCHAR query[] = {
4561         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4562         '`','S','e','l','f','R','e','g','`',0};
4563     MSIQUERY *view;
4564     UINT rc;
4565
4566     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4567     if (rc != ERROR_SUCCESS)
4568         return ERROR_SUCCESS;
4569
4570     rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4571     msiobj_release( &view->hdr );
4572     return rc;
4573 }
4574
4575 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4576 {
4577     MSIFEATURE *feature;
4578     UINT rc;
4579     HKEY hkey = NULL, userdata = NULL;
4580
4581     if (!msi_check_publish(package))
4582         return ERROR_SUCCESS;
4583
4584     rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4585                                 &hkey, TRUE);
4586     if (rc != ERROR_SUCCESS)
4587         goto end;
4588
4589     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4590                                         &userdata, TRUE);
4591     if (rc != ERROR_SUCCESS)
4592         goto end;
4593
4594     /* here the guids are base 85 encoded */
4595     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4596     {
4597         ComponentList *cl;
4598         LPWSTR data = NULL;
4599         GUID clsid;
4600         INT size;
4601         BOOL absent = FALSE;
4602         MSIRECORD *uirow;
4603
4604         if (feature->Action != INSTALLSTATE_LOCAL &&
4605             feature->Action != INSTALLSTATE_SOURCE &&
4606             feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4607
4608         size = 1;
4609         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4610         {
4611             size += 21;
4612         }
4613         if (feature->Feature_Parent)
4614             size += strlenW( feature->Feature_Parent )+2;
4615
4616         data = msi_alloc(size * sizeof(WCHAR));
4617
4618         data[0] = 0;
4619         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4620         {
4621             MSICOMPONENT* component = cl->component;
4622             WCHAR buf[21];
4623
4624             buf[0] = 0;
4625             if (component->ComponentId)
4626             {
4627                 TRACE("From %s\n",debugstr_w(component->ComponentId));
4628                 CLSIDFromString(component->ComponentId, &clsid);
4629                 encode_base85_guid(&clsid,buf);
4630                 TRACE("to %s\n",debugstr_w(buf));
4631                 strcatW(data,buf);
4632             }
4633         }
4634
4635         if (feature->Feature_Parent)
4636         {
4637             static const WCHAR sep[] = {'\2',0};
4638             strcatW(data,sep);
4639             strcatW(data,feature->Feature_Parent);
4640         }
4641
4642         msi_reg_set_val_str( userdata, feature->Feature, data );
4643         msi_free(data);
4644
4645         size = 0;
4646         if (feature->Feature_Parent)
4647             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4648         if (!absent)
4649         {
4650             size += sizeof(WCHAR);
4651             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4652                            (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4653         }
4654         else
4655         {
4656             size += 2*sizeof(WCHAR);
4657             data = msi_alloc(size);
4658             data[0] = 0x6;
4659             data[1] = 0;
4660             if (feature->Feature_Parent)
4661                 strcpyW( &data[1], feature->Feature_Parent );
4662             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4663                        (LPBYTE)data,size);
4664             msi_free(data);
4665         }
4666
4667         /* the UI chunk */
4668         uirow = MSI_CreateRecord( 1 );
4669         MSI_RecordSetStringW( uirow, 1, feature->Feature );
4670         msi_ui_actiondata( package, szPublishFeatures, uirow );
4671         msiobj_release( &uirow->hdr );
4672         /* FIXME: call msi_ui_progress? */
4673     }
4674
4675 end:
4676     RegCloseKey(hkey);
4677     RegCloseKey(userdata);
4678     return rc;
4679 }
4680
4681 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4682 {
4683     UINT r;
4684     HKEY hkey;
4685     MSIRECORD *uirow;
4686
4687     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4688
4689     r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4690                                &hkey, FALSE);
4691     if (r == ERROR_SUCCESS)
4692     {
4693         RegDeleteValueW(hkey, feature->Feature);
4694         RegCloseKey(hkey);
4695     }
4696
4697     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4698                                        &hkey, FALSE);
4699     if (r == ERROR_SUCCESS)
4700     {
4701         RegDeleteValueW(hkey, feature->Feature);
4702         RegCloseKey(hkey);
4703     }
4704
4705     uirow = MSI_CreateRecord( 1 );
4706     MSI_RecordSetStringW( uirow, 1, feature->Feature );
4707     msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4708     msiobj_release( &uirow->hdr );
4709
4710     return ERROR_SUCCESS;
4711 }
4712
4713 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4714 {
4715     MSIFEATURE *feature;
4716
4717     if (!msi_check_unpublish(package))
4718         return ERROR_SUCCESS;
4719
4720     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4721     {
4722         msi_unpublish_feature(package, feature);
4723     }
4724
4725     return ERROR_SUCCESS;
4726 }
4727
4728 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4729 {
4730     SYSTEMTIME systime;
4731     DWORD size, langid;
4732     WCHAR date[9], *val, *buffer;
4733     const WCHAR *prop, *key;
4734
4735     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4736     static const WCHAR modpath_fmt[] =
4737         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4738          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4739     static const WCHAR szModifyPath[] =
4740         {'M','o','d','i','f','y','P','a','t','h',0};
4741     static const WCHAR szUninstallString[] =
4742         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4743     static const WCHAR szEstimatedSize[] =
4744         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4745     static const WCHAR szDisplayVersion[] =
4746         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4747     static const WCHAR szInstallSource[] =
4748         {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4749     static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4750         {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4751     static const WCHAR szAuthorizedCDFPrefix[] =
4752         {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4753     static const WCHAR szARPCONTACT[] =
4754         {'A','R','P','C','O','N','T','A','C','T',0};
4755     static const WCHAR szContact[] =
4756         {'C','o','n','t','a','c','t',0};
4757     static const WCHAR szARPCOMMENTS[] =
4758         {'A','R','P','C','O','M','M','E','N','T','S',0};
4759     static const WCHAR szComments[] =
4760         {'C','o','m','m','e','n','t','s',0};
4761     static const WCHAR szProductName[] =
4762         {'P','r','o','d','u','c','t','N','a','m','e',0};
4763     static const WCHAR szDisplayName[] =
4764         {'D','i','s','p','l','a','y','N','a','m','e',0};
4765     static const WCHAR szARPHELPLINK[] =
4766         {'A','R','P','H','E','L','P','L','I','N','K',0};
4767     static const WCHAR szHelpLink[] =
4768         {'H','e','l','p','L','i','n','k',0};
4769     static const WCHAR szARPHELPTELEPHONE[] =
4770         {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4771     static const WCHAR szHelpTelephone[] =
4772         {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4773     static const WCHAR szARPINSTALLLOCATION[] =
4774         {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4775     static const WCHAR szInstallLocation[] =
4776         {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4777     static const WCHAR szManufacturer[] =
4778         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4779     static const WCHAR szPublisher[] =
4780         {'P','u','b','l','i','s','h','e','r',0};
4781     static const WCHAR szARPREADME[] =
4782         {'A','R','P','R','E','A','D','M','E',0};
4783     static const WCHAR szReadme[] =
4784         {'R','e','a','d','M','e',0};
4785     static const WCHAR szARPSIZE[] =
4786         {'A','R','P','S','I','Z','E',0};
4787     static const WCHAR szSize[] =
4788         {'S','i','z','e',0};
4789     static const WCHAR szARPURLINFOABOUT[] =
4790         {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4791     static const WCHAR szURLInfoAbout[] =
4792         {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4793     static const WCHAR szARPURLUPDATEINFO[] =
4794         {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4795     static const WCHAR szURLUpdateInfo[] =
4796         {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4797     static const WCHAR szARPSYSTEMCOMPONENT[] =
4798         {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4799     static const WCHAR szSystemComponent[] =
4800         {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4801
4802     static const WCHAR *propval[] = {
4803         szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4804         szARPCONTACT,             szContact,
4805         szARPCOMMENTS,            szComments,
4806         szProductName,            szDisplayName,
4807         szARPHELPLINK,            szHelpLink,
4808         szARPHELPTELEPHONE,       szHelpTelephone,
4809         szARPINSTALLLOCATION,     szInstallLocation,
4810         szSourceDir,              szInstallSource,
4811         szManufacturer,           szPublisher,
4812         szARPREADME,              szReadme,
4813         szARPSIZE,                szSize,
4814         szARPURLINFOABOUT,        szURLInfoAbout,
4815         szARPURLUPDATEINFO,       szURLUpdateInfo,
4816         NULL
4817     };
4818     const WCHAR **p = propval;
4819
4820     while (*p)
4821     {
4822         prop = *p++;
4823         key = *p++;
4824         val = msi_dup_property(package->db, prop);
4825         msi_reg_set_val_str(hkey, key, val);
4826         msi_free(val);
4827     }
4828
4829     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4830     if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4831     {
4832         msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4833     }
4834     size = deformat_string(package, modpath_fmt, &buffer);
4835     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4836     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4837     msi_free(buffer);
4838
4839     /* FIXME: Write real Estimated Size when we have it */
4840     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4841
4842     GetLocalTime(&systime);
4843     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4844     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4845
4846     langid = msi_get_property_int(package->db, szProductLanguage, 0);
4847     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4848
4849     buffer = msi_dup_property(package->db, szProductVersion);
4850     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4851     if (buffer)
4852     {
4853         DWORD verdword = msi_version_str_to_dword(buffer);
4854
4855         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4856         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4857         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4858         msi_free(buffer);
4859     }
4860
4861     return ERROR_SUCCESS;
4862 }
4863
4864 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4865 {
4866     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4867     MSIRECORD *uirow;
4868     LPWSTR upgrade_code;
4869     HKEY hkey, props, upgrade_key;
4870     UINT rc;
4871
4872     /* FIXME: also need to publish if the product is in advertise mode */
4873     if (!msi_check_publish(package))
4874         return ERROR_SUCCESS;
4875
4876     rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4877     if (rc != ERROR_SUCCESS)
4878         return rc;
4879
4880     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4881     if (rc != ERROR_SUCCESS)
4882         goto done;
4883
4884     rc = msi_publish_install_properties(package, hkey);
4885     if (rc != ERROR_SUCCESS)
4886         goto done;
4887
4888     rc = msi_publish_install_properties(package, props);
4889     if (rc != ERROR_SUCCESS)
4890         goto done;
4891
4892     upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4893     if (upgrade_code)
4894     {
4895         rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4896         if (rc == ERROR_SUCCESS)
4897         {
4898             squash_guid( package->ProductCode, squashed_pc );
4899             msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4900             RegCloseKey( upgrade_key );
4901         }
4902         msi_free( upgrade_code );
4903     }
4904     msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4905     package->delete_on_close = FALSE;
4906
4907 done:
4908     uirow = MSI_CreateRecord( 1 );
4909     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4910     msi_ui_actiondata( package, szRegisterProduct, uirow );
4911     msiobj_release( &uirow->hdr );
4912
4913     RegCloseKey(hkey);
4914     return ERROR_SUCCESS;
4915 }
4916
4917 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4918 {
4919     return execute_script(package, SCRIPT_INSTALL);
4920 }
4921
4922 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4923 {
4924     MSIPACKAGE *package = param;
4925     const WCHAR *icon = MSI_RecordGetString( row, 1 );
4926     WCHAR *p, *icon_path;
4927
4928     if (!icon) return ERROR_SUCCESS;
4929     if ((icon_path = msi_build_icon_path( package, icon )))
4930     {
4931         TRACE("removing icon file %s\n", debugstr_w(icon_path));
4932         DeleteFileW( icon_path );
4933         if ((p = strrchrW( icon_path, '\\' )))
4934         {
4935             *p = 0;
4936             RemoveDirectoryW( icon_path );
4937         }
4938         msi_free( icon_path );
4939     }
4940     return ERROR_SUCCESS;
4941 }
4942
4943 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4944 {
4945     static const WCHAR query[]= {
4946         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4947     MSIQUERY *view;
4948     UINT r;
4949
4950     r = MSI_DatabaseOpenViewW( package->db, query, &view );
4951     if (r == ERROR_SUCCESS)
4952     {
4953         r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4954         msiobj_release( &view->hdr );
4955         if (r != ERROR_SUCCESS)
4956             return r;
4957     }
4958     return ERROR_SUCCESS;
4959 }
4960
4961 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4962 {
4963     static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4964     WCHAR *upgrade, **features;
4965     BOOL full_uninstall = TRUE;
4966     MSIFEATURE *feature;
4967     MSIPATCHINFO *patch;
4968     UINT i;
4969
4970     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4971     {
4972         if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4973     }
4974     features = msi_split_string( remove, ',' );
4975     for (i = 0; features && features[i]; i++)
4976     {
4977         if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4978     }
4979     msi_free(features);
4980
4981     if (!full_uninstall)
4982         return ERROR_SUCCESS;
4983
4984     MSIREG_DeleteProductKey(package->ProductCode);
4985     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4986     MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4987
4988     MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4989     MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4990     MSIREG_DeleteUserProductKey(package->ProductCode);
4991     MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4992
4993     upgrade = msi_dup_property(package->db, szUpgradeCode);
4994     if (upgrade)
4995     {
4996         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4997         MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4998         msi_free(upgrade);
4999     }
5000
5001     LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5002     {
5003         MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5004         if (!strcmpW( package->ProductCode, patch->products ))
5005         {
5006             TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5007             patch->delete_on_close = TRUE;
5008         }
5009         /* FIXME: remove local patch package if this is the last product */
5010     }
5011     TRACE("removing local package %s\n", debugstr_w(package->localfile));
5012     package->delete_on_close = TRUE;
5013
5014     msi_unpublish_icons( package );
5015     return ERROR_SUCCESS;
5016 }
5017
5018 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5019 {
5020     UINT rc;
5021     WCHAR *remove;
5022
5023     /* turn off scheduling */
5024     package->script->CurrentlyScripting= FALSE;
5025
5026     /* first do the same as an InstallExecute */
5027     rc = ACTION_InstallExecute(package);
5028     if (rc != ERROR_SUCCESS)
5029         return rc;
5030
5031     /* then handle commit actions */
5032     rc = execute_script(package, SCRIPT_COMMIT);
5033     if (rc != ERROR_SUCCESS)
5034         return rc;
5035
5036     remove = msi_dup_property(package->db, szRemove);
5037     rc = msi_unpublish_product(package, remove);
5038     msi_free(remove);
5039     return rc;
5040 }
5041
5042 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5043 {
5044     static const WCHAR RunOnce[] = {
5045     'S','o','f','t','w','a','r','e','\\',
5046     'M','i','c','r','o','s','o','f','t','\\',
5047     'W','i','n','d','o','w','s','\\',
5048     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5049     'R','u','n','O','n','c','e',0};
5050     static const WCHAR InstallRunOnce[] = {
5051     'S','o','f','t','w','a','r','e','\\',
5052     'M','i','c','r','o','s','o','f','t','\\',
5053     'W','i','n','d','o','w','s','\\',
5054     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5055     'I','n','s','t','a','l','l','e','r','\\',
5056     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5057
5058     static const WCHAR msiexec_fmt[] = {
5059     '%','s',
5060     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5061     '\"','%','s','\"',0};
5062     static const WCHAR install_fmt[] = {
5063     '/','I',' ','\"','%','s','\"',' ',
5064     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5065     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5066     WCHAR buffer[256], sysdir[MAX_PATH];
5067     HKEY hkey;
5068     WCHAR squished_pc[100];
5069
5070     squash_guid(package->ProductCode,squished_pc);
5071
5072     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5073     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5074     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5075      squished_pc);
5076
5077     msi_reg_set_val_str( hkey, squished_pc, buffer );
5078     RegCloseKey(hkey);
5079
5080     TRACE("Reboot command %s\n",debugstr_w(buffer));
5081
5082     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5083     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5084
5085     msi_reg_set_val_str( hkey, squished_pc, buffer );
5086     RegCloseKey(hkey);
5087
5088     return ERROR_INSTALL_SUSPEND;
5089 }
5090
5091 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5092 {
5093     static const WCHAR query[] =
5094         {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5095          'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5096          '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5097     MSIRECORD *rec, *row;
5098     DWORD i, size = 0;
5099     va_list va;
5100     const WCHAR *str;
5101     WCHAR *data;
5102
5103     if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5104
5105     rec = MSI_CreateRecord( count + 2 );
5106     str = MSI_RecordGetString( row, 1 );
5107     MSI_RecordSetStringW( rec, 0, str );
5108     msiobj_release( &row->hdr );
5109     MSI_RecordSetInteger( rec, 1, error );
5110
5111     va_start( va, count );
5112     for (i = 0; i < count; i++)
5113     {
5114         str = va_arg( va, const WCHAR *);
5115         MSI_RecordSetStringW( rec, i + 2, str );
5116     }
5117     va_end( va );
5118
5119     MSI_FormatRecordW( package, rec, NULL, &size );
5120     size++;
5121     data = msi_alloc( size * sizeof(WCHAR) );
5122     if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5123     else data[0] = 0;
5124     msiobj_release( &rec->hdr );
5125     return data;
5126 }
5127
5128 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5129 {
5130     DWORD attrib;
5131     UINT rc;
5132
5133     /*
5134      * We are currently doing what should be done here in the top level Install
5135      * however for Administrative and uninstalls this step will be needed
5136      */
5137     if (!package->PackagePath)
5138         return ERROR_SUCCESS;
5139
5140     msi_set_sourcedir_props(package, TRUE);
5141
5142     attrib = GetFileAttributesW(package->db->path);
5143     if (attrib == INVALID_FILE_ATTRIBUTES)
5144     {
5145         LPWSTR prompt, msg;
5146         DWORD size = 0;
5147
5148         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
5149                 package->Context, MSICODE_PRODUCT,
5150                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5151         if (rc == ERROR_MORE_DATA)
5152         {
5153             prompt = msi_alloc(size * sizeof(WCHAR));
5154             MsiSourceListGetInfoW(package->ProductCode, NULL, 
5155                     package->Context, MSICODE_PRODUCT,
5156                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5157         }
5158         else
5159             prompt = strdupW(package->db->path);
5160
5161         msg = msi_build_error_string(package, 1302, 1, prompt);
5162         msi_free(prompt);
5163         while(attrib == INVALID_FILE_ATTRIBUTES)
5164         {
5165             rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5166             if (rc == IDCANCEL)
5167             {
5168                 msi_free(msg);
5169                 return ERROR_INSTALL_USEREXIT;
5170             }
5171             attrib = GetFileAttributesW(package->db->path);
5172         }
5173         msi_free(msg);
5174         rc = ERROR_SUCCESS;
5175     }
5176     else
5177         return ERROR_SUCCESS;
5178
5179     return rc;
5180 }
5181
5182 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5183 {
5184     HKEY hkey = 0;
5185     LPWSTR buffer, productid = NULL;
5186     UINT i, rc = ERROR_SUCCESS;
5187     MSIRECORD *uirow;
5188
5189     static const WCHAR szPropKeys[][80] = 
5190     {
5191         {'P','r','o','d','u','c','t','I','D',0},
5192         {'U','S','E','R','N','A','M','E',0},
5193         {'C','O','M','P','A','N','Y','N','A','M','E',0},
5194         {0},
5195     };
5196
5197     static const WCHAR szRegKeys[][80] = 
5198     {
5199         {'P','r','o','d','u','c','t','I','D',0},
5200         {'R','e','g','O','w','n','e','r',0},
5201         {'R','e','g','C','o','m','p','a','n','y',0},
5202         {0},
5203     };
5204
5205     if (msi_check_unpublish(package))
5206     {
5207         MSIREG_DeleteUserDataProductKey(package->ProductCode);
5208         goto end;
5209     }
5210
5211     productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5212     if (!productid)
5213         goto end;
5214
5215     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5216                                  NULL, &hkey, TRUE);
5217     if (rc != ERROR_SUCCESS)
5218         goto end;
5219
5220     for( i = 0; szPropKeys[i][0]; i++ )
5221     {
5222         buffer = msi_dup_property( package->db, szPropKeys[i] );
5223         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5224         msi_free( buffer );
5225     }
5226
5227 end:
5228     uirow = MSI_CreateRecord( 1 );
5229     MSI_RecordSetStringW( uirow, 1, productid );
5230     msi_ui_actiondata( package, szRegisterUser, uirow );
5231     msiobj_release( &uirow->hdr );
5232
5233     msi_free(productid);
5234     RegCloseKey(hkey);
5235     return rc;
5236 }
5237
5238
5239 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5240 {
5241     UINT rc;
5242
5243     package->script->InWhatSequence |= SEQUENCE_EXEC;
5244     rc = ACTION_ProcessExecSequence(package,FALSE);
5245     return rc;
5246 }
5247
5248 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5249 {
5250     static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5251     WCHAR productid_85[21], component_85[21], *ret;
5252     GUID clsid;
5253     DWORD sz;
5254
5255     /* > is used if there is a component GUID and < if not.  */
5256
5257     productid_85[0] = 0;
5258     component_85[0] = 0;
5259     CLSIDFromString( package->ProductCode, &clsid );
5260
5261     encode_base85_guid( &clsid, productid_85 );
5262     if (component)
5263     {
5264         CLSIDFromString( component->ComponentId, &clsid );
5265         encode_base85_guid( &clsid, component_85 );
5266     }
5267
5268     TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5269           debugstr_w(component_85));
5270
5271     sz = 20 + strlenW( feature ) + 20 + 3;
5272     ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5273     if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5274     return ret;
5275 }
5276
5277 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5278 {
5279     MSIPACKAGE *package = param;
5280     LPCWSTR compgroupid, component, feature, qualifier, text;
5281     LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5282     HKEY hkey = NULL;
5283     UINT rc;
5284     MSICOMPONENT *comp;
5285     MSIFEATURE *feat;
5286     DWORD sz;
5287     MSIRECORD *uirow;
5288     int len;
5289
5290     feature = MSI_RecordGetString(rec, 5);
5291     feat = msi_get_loaded_feature(package, feature);
5292     if (!feat)
5293         return ERROR_SUCCESS;
5294
5295     feat->Action = msi_get_feature_action( package, feat );
5296     if (feat->Action != INSTALLSTATE_LOCAL &&
5297         feat->Action != INSTALLSTATE_SOURCE &&
5298         feat->Action != INSTALLSTATE_ADVERTISED)
5299     {
5300         TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5301         return ERROR_SUCCESS;
5302     }
5303
5304     component = MSI_RecordGetString(rec, 3);
5305     comp = msi_get_loaded_component(package, component);
5306     if (!comp)
5307         return ERROR_SUCCESS;
5308
5309     compgroupid = MSI_RecordGetString(rec,1);
5310     qualifier = MSI_RecordGetString(rec,2);
5311
5312     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5313     if (rc != ERROR_SUCCESS)
5314         goto end;
5315
5316     advertise = msi_create_component_advertise_string( package, comp, feature );
5317     text = MSI_RecordGetString( rec, 4 );
5318     if (text)
5319     {
5320         p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5321         strcpyW( p, advertise );
5322         strcatW( p, text );
5323         msi_free( advertise );
5324         advertise = p;
5325     }
5326     existing = msi_reg_get_val_str( hkey, qualifier );
5327
5328     sz = strlenW( advertise ) + 1;
5329     if (existing)
5330     {
5331         for (p = existing; *p; p += len)
5332         {
5333             len = strlenW( p ) + 1;
5334             if (strcmpW( advertise, p )) sz += len;
5335         }
5336     }
5337     if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5338     {
5339         rc = ERROR_OUTOFMEMORY;
5340         goto end;
5341     }
5342     q = output;
5343     if (existing)
5344     {
5345         for (p = existing; *p; p += len)
5346         {
5347             len = strlenW( p ) + 1;
5348             if (strcmpW( advertise, p ))
5349             {
5350                 memcpy( q, p, len * sizeof(WCHAR) );
5351                 q += len;
5352             }
5353         }
5354     }
5355     strcpyW( q, advertise );
5356     q[strlenW( q ) + 1] = 0;
5357
5358     msi_reg_set_val_multi_str( hkey, qualifier, output );
5359     
5360 end:
5361     RegCloseKey(hkey);
5362     msi_free( output );
5363     msi_free( advertise );
5364     msi_free( existing );
5365
5366     /* the UI chunk */
5367     uirow = MSI_CreateRecord( 2 );
5368     MSI_RecordSetStringW( uirow, 1, compgroupid );
5369     MSI_RecordSetStringW( uirow, 2, qualifier);
5370     msi_ui_actiondata( package, szPublishComponents, uirow );
5371     msiobj_release( &uirow->hdr );
5372     /* FIXME: call ui_progress? */
5373
5374     return rc;
5375 }
5376
5377 /*
5378  * At present I am ignorning the advertised components part of this and only
5379  * focusing on the qualified component sets
5380  */
5381 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5382 {
5383     static const WCHAR query[] = {
5384         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5385         '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5386     MSIQUERY *view;
5387     UINT rc;
5388     
5389     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5390     if (rc != ERROR_SUCCESS)
5391         return ERROR_SUCCESS;
5392
5393     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5394     msiobj_release(&view->hdr);
5395     return rc;
5396 }
5397
5398 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5399 {
5400     static const WCHAR szInstallerComponents[] = {
5401         'S','o','f','t','w','a','r','e','\\',
5402         'M','i','c','r','o','s','o','f','t','\\',
5403         'I','n','s','t','a','l','l','e','r','\\',
5404         'C','o','m','p','o','n','e','n','t','s','\\',0};
5405
5406     MSIPACKAGE *package = param;
5407     LPCWSTR compgroupid, component, feature, qualifier;
5408     MSICOMPONENT *comp;
5409     MSIFEATURE *feat;
5410     MSIRECORD *uirow;
5411     WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5412     LONG res;
5413
5414     feature = MSI_RecordGetString( rec, 5 );
5415     feat = msi_get_loaded_feature( package, feature );
5416     if (!feat)
5417         return ERROR_SUCCESS;
5418
5419     feat->Action = msi_get_feature_action( package, feat );
5420     if (feat->Action != INSTALLSTATE_ABSENT)
5421     {
5422         TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5423         return ERROR_SUCCESS;
5424     }
5425
5426     component = MSI_RecordGetString( rec, 3 );
5427     comp = msi_get_loaded_component( package, component );
5428     if (!comp)
5429         return ERROR_SUCCESS;
5430
5431     compgroupid = MSI_RecordGetString( rec, 1 );
5432     qualifier = MSI_RecordGetString( rec, 2 );
5433
5434     squash_guid( compgroupid, squashed );
5435     strcpyW( keypath, szInstallerComponents );
5436     strcatW( keypath, squashed );
5437
5438     res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5439     if (res != ERROR_SUCCESS)
5440     {
5441         WARN("Unable to delete component key %d\n", res);
5442     }
5443
5444     uirow = MSI_CreateRecord( 2 );
5445     MSI_RecordSetStringW( uirow, 1, compgroupid );
5446     MSI_RecordSetStringW( uirow, 2, qualifier );
5447     msi_ui_actiondata( package, szUnpublishComponents, uirow );
5448     msiobj_release( &uirow->hdr );
5449
5450     return ERROR_SUCCESS;
5451 }
5452
5453 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5454 {
5455     static const WCHAR query[] = {
5456         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5457         '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5458     MSIQUERY *view;
5459     UINT rc;
5460
5461     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5462     if (rc != ERROR_SUCCESS)
5463         return ERROR_SUCCESS;
5464
5465     rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5466     msiobj_release( &view->hdr );
5467     return rc;
5468 }
5469
5470 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5471 {
5472     static const WCHAR query[] =
5473         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5474          '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5475          '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5476     MSIPACKAGE *package = param;
5477     MSICOMPONENT *component;
5478     MSIRECORD *row;
5479     MSIFILE *file;
5480     SC_HANDLE hscm = NULL, service = NULL;
5481     LPCWSTR comp, key;
5482     LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5483     LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5484     DWORD serv_type, start_type, err_control;
5485     SERVICE_DESCRIPTIONW sd = {NULL};
5486
5487     comp = MSI_RecordGetString( rec, 12 );
5488     component = msi_get_loaded_component( package, comp );
5489     if (!component)
5490     {
5491         WARN("service component not found\n");
5492         goto done;
5493     }
5494     component->Action = msi_get_component_action( package, component );
5495     if (component->Action != INSTALLSTATE_LOCAL)
5496     {
5497         TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5498         goto done;
5499     }
5500     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5501     if (!hscm)
5502     {
5503         ERR("Failed to open the SC Manager!\n");
5504         goto done;
5505     }
5506
5507     start_type = MSI_RecordGetInteger(rec, 5);
5508     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5509         goto done;
5510
5511     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5512     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5513     serv_type = MSI_RecordGetInteger(rec, 4);
5514     err_control = MSI_RecordGetInteger(rec, 6);
5515     deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5516     deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5517     deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5518     deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5519     deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5520     deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5521
5522     /* fetch the service path */
5523     row = MSI_QueryGetRecord(package->db, query, comp);
5524     if (!row)
5525     {
5526         ERR("Query failed\n");
5527         goto done;
5528     }
5529     key = MSI_RecordGetString(row, 6);
5530     file = msi_get_loaded_file(package, key);
5531     msiobj_release(&row->hdr);
5532     if (!file)
5533     {
5534         ERR("Failed to load the service file\n");
5535         goto done;
5536     }
5537
5538     if (!args || !args[0]) image_path = file->TargetPath;
5539     else
5540     {
5541         int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5542         if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5543             return ERROR_OUTOFMEMORY;
5544
5545         strcpyW(image_path, file->TargetPath);
5546         strcatW(image_path, szSpace);
5547         strcatW(image_path, args);
5548     }
5549     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5550                              start_type, err_control, image_path, load_order,
5551                              NULL, depends, serv_name, pass);
5552
5553     if (!service)
5554     {
5555         if (GetLastError() != ERROR_SERVICE_EXISTS)
5556             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5557     }
5558     else if (sd.lpDescription)
5559     {
5560         if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5561             WARN("failed to set service description %u\n", GetLastError());
5562     }
5563
5564     if (image_path != file->TargetPath) msi_free(image_path);
5565 done:
5566     CloseServiceHandle(service);
5567     CloseServiceHandle(hscm);
5568     msi_free(name);
5569     msi_free(disp);
5570     msi_free(sd.lpDescription);
5571     msi_free(load_order);
5572     msi_free(serv_name);
5573     msi_free(pass);
5574     msi_free(depends);
5575     msi_free(args);
5576
5577     return ERROR_SUCCESS;
5578 }
5579
5580 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5581 {
5582     static const WCHAR query[] = {
5583         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5584         'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5585     MSIQUERY *view;
5586     UINT rc;
5587     
5588     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5589     if (rc != ERROR_SUCCESS)
5590         return ERROR_SUCCESS;
5591
5592     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5593     msiobj_release(&view->hdr);
5594     return rc;
5595 }
5596
5597 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5598 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5599 {
5600     LPCWSTR *vector, *temp_vector;
5601     LPWSTR p, q;
5602     DWORD sep_len;
5603
5604     static const WCHAR separator[] = {'[','~',']',0};
5605
5606     *numargs = 0;
5607     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5608
5609     if (!args)
5610         return NULL;
5611
5612     vector = msi_alloc(sizeof(LPWSTR));
5613     if (!vector)
5614         return NULL;
5615
5616     p = args;
5617     do
5618     {
5619         (*numargs)++;
5620         vector[*numargs - 1] = p;
5621
5622         if ((q = strstrW(p, separator)))
5623         {
5624             *q = '\0';
5625
5626             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5627             if (!temp_vector)
5628             {
5629                 msi_free(vector);
5630                 return NULL;
5631             }
5632             vector = temp_vector;
5633
5634             p = q + sep_len;
5635         }
5636     } while (q);
5637
5638     return vector;
5639 }
5640
5641 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5642 {
5643     MSIPACKAGE *package = param;
5644     MSICOMPONENT *comp;
5645     MSIRECORD *uirow;
5646     SC_HANDLE scm = NULL, service = NULL;
5647     LPCWSTR component, *vector = NULL;
5648     LPWSTR name, args, display_name = NULL;
5649     DWORD event, numargs, len, wait, dummy;
5650     UINT r = ERROR_FUNCTION_FAILED;
5651     SERVICE_STATUS_PROCESS status;
5652     ULONGLONG start_time;
5653
5654     component = MSI_RecordGetString(rec, 6);
5655     comp = msi_get_loaded_component(package, component);
5656     if (!comp)
5657         return ERROR_SUCCESS;
5658
5659     comp->Action = msi_get_component_action( package, comp );
5660     if (comp->Action != INSTALLSTATE_LOCAL)
5661     {
5662         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5663         return ERROR_SUCCESS;
5664     }
5665
5666     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5667     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5668     event = MSI_RecordGetInteger(rec, 3);
5669     wait = MSI_RecordGetInteger(rec, 5);
5670
5671     if (!(event & msidbServiceControlEventStart))
5672     {
5673         r = ERROR_SUCCESS;
5674         goto done;
5675     }
5676
5677     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5678     if (!scm)
5679     {
5680         ERR("Failed to open the service control manager\n");
5681         goto done;
5682     }
5683
5684     len = 0;
5685     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5686         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5687     {
5688         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5689             GetServiceDisplayNameW( scm, name, display_name, &len );
5690     }
5691
5692     service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5693     if (!service)
5694     {
5695         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5696         goto done;
5697     }
5698
5699     vector = msi_service_args_to_vector(args, &numargs);
5700
5701     if (!StartServiceW(service, numargs, vector) &&
5702         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5703     {
5704         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5705         goto done;
5706     }
5707
5708     r = ERROR_SUCCESS;
5709     if (wait)
5710     {
5711         /* wait for at most 30 seconds for the service to be up and running */
5712         if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5713             (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5714         {
5715             TRACE("failed to query service status (%u)\n", GetLastError());
5716             goto done;
5717         }
5718         start_time = GetTickCount64();
5719         while (status.dwCurrentState == SERVICE_START_PENDING)
5720         {
5721             if (GetTickCount64() - start_time > 30000) break;
5722             Sleep(1000);
5723             if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5724                 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5725             {
5726                 TRACE("failed to query service status (%u)\n", GetLastError());
5727                 goto done;
5728             }
5729         }
5730         if (status.dwCurrentState != SERVICE_RUNNING)
5731         {
5732             WARN("service failed to start %u\n", status.dwCurrentState);
5733             r = ERROR_FUNCTION_FAILED;
5734         }
5735     }
5736
5737 done:
5738     uirow = MSI_CreateRecord( 2 );
5739     MSI_RecordSetStringW( uirow, 1, display_name );
5740     MSI_RecordSetStringW( uirow, 2, name );
5741     msi_ui_actiondata( package, szStartServices, uirow );
5742     msiobj_release( &uirow->hdr );
5743
5744     CloseServiceHandle(service);
5745     CloseServiceHandle(scm);
5746
5747     msi_free(name);
5748     msi_free(args);
5749     msi_free(vector);
5750     msi_free(display_name);
5751     return r;
5752 }
5753
5754 static UINT ACTION_StartServices( MSIPACKAGE *package )
5755 {
5756     static const WCHAR query[] = {
5757         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5758         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5759     MSIQUERY *view;
5760     UINT rc;
5761
5762     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5763     if (rc != ERROR_SUCCESS)
5764         return ERROR_SUCCESS;
5765
5766     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5767     msiobj_release(&view->hdr);
5768     return rc;
5769 }
5770
5771 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5772 {
5773     DWORD i, needed, count;
5774     ENUM_SERVICE_STATUSW *dependencies;
5775     SERVICE_STATUS ss;
5776     SC_HANDLE depserv;
5777
5778     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5779                                0, &needed, &count))
5780         return TRUE;
5781
5782     if (GetLastError() != ERROR_MORE_DATA)
5783         return FALSE;
5784
5785     dependencies = msi_alloc(needed);
5786     if (!dependencies)
5787         return FALSE;
5788
5789     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5790                                 needed, &needed, &count))
5791         goto error;
5792
5793     for (i = 0; i < count; i++)
5794     {
5795         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5796                                SERVICE_STOP | SERVICE_QUERY_STATUS);
5797         if (!depserv)
5798             goto error;
5799
5800         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5801             goto error;
5802     }
5803
5804     return TRUE;
5805
5806 error:
5807     msi_free(dependencies);
5808     return FALSE;
5809 }
5810
5811 static UINT stop_service( LPCWSTR name )
5812 {
5813     SC_HANDLE scm = NULL, service = NULL;
5814     SERVICE_STATUS status;
5815     SERVICE_STATUS_PROCESS ssp;
5816     DWORD needed;
5817
5818     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5819     if (!scm)
5820     {
5821         WARN("Failed to open the SCM: %d\n", GetLastError());
5822         goto done;
5823     }
5824
5825     service = OpenServiceW(scm, name,
5826                            SERVICE_STOP |
5827                            SERVICE_QUERY_STATUS |
5828                            SERVICE_ENUMERATE_DEPENDENTS);
5829     if (!service)
5830     {
5831         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5832         goto done;
5833     }
5834
5835     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5836                               sizeof(SERVICE_STATUS_PROCESS), &needed))
5837     {
5838         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5839         goto done;
5840     }
5841
5842     if (ssp.dwCurrentState == SERVICE_STOPPED)
5843         goto done;
5844
5845     stop_service_dependents(scm, service);
5846
5847     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5848         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5849
5850 done:
5851     CloseServiceHandle(service);
5852     CloseServiceHandle(scm);
5853
5854     return ERROR_SUCCESS;
5855 }
5856
5857 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5858 {
5859     MSIPACKAGE *package = param;
5860     MSICOMPONENT *comp;
5861     MSIRECORD *uirow;
5862     LPCWSTR component;
5863     LPWSTR name = NULL, display_name = NULL;
5864     DWORD event, len;
5865     SC_HANDLE scm;
5866
5867     event = MSI_RecordGetInteger( rec, 3 );
5868     if (!(event & msidbServiceControlEventStop))
5869         return ERROR_SUCCESS;
5870
5871     component = MSI_RecordGetString( rec, 6 );
5872     comp = msi_get_loaded_component( package, component );
5873     if (!comp)
5874         return ERROR_SUCCESS;
5875
5876     comp->Action = msi_get_component_action( package, comp );
5877     if (comp->Action != INSTALLSTATE_ABSENT)
5878     {
5879         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5880         return ERROR_SUCCESS;
5881     }
5882
5883     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5884     if (!scm)
5885     {
5886         ERR("Failed to open the service control manager\n");
5887         goto done;
5888     }
5889
5890     len = 0;
5891     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5892         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5893     {
5894         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5895             GetServiceDisplayNameW( scm, name, display_name, &len );
5896     }
5897     CloseServiceHandle( scm );
5898
5899     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5900     stop_service( name );
5901
5902 done:
5903     uirow = MSI_CreateRecord( 2 );
5904     MSI_RecordSetStringW( uirow, 1, display_name );
5905     MSI_RecordSetStringW( uirow, 2, name );
5906     msi_ui_actiondata( package, szStopServices, uirow );
5907     msiobj_release( &uirow->hdr );
5908
5909     msi_free( name );
5910     msi_free( display_name );
5911     return ERROR_SUCCESS;
5912 }
5913
5914 static UINT ACTION_StopServices( MSIPACKAGE *package )
5915 {
5916     static const WCHAR query[] = {
5917         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5918         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5919     MSIQUERY *view;
5920     UINT rc;
5921
5922     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5923     if (rc != ERROR_SUCCESS)
5924         return ERROR_SUCCESS;
5925
5926     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5927     msiobj_release(&view->hdr);
5928     return rc;
5929 }
5930
5931 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5932 {
5933     MSIPACKAGE *package = param;
5934     MSICOMPONENT *comp;
5935     MSIRECORD *uirow;
5936     LPCWSTR component;
5937     LPWSTR name = NULL, display_name = NULL;
5938     DWORD event, len;
5939     SC_HANDLE scm = NULL, service = NULL;
5940
5941     event = MSI_RecordGetInteger( rec, 3 );
5942     if (!(event & msidbServiceControlEventDelete))
5943         return ERROR_SUCCESS;
5944
5945     component = MSI_RecordGetString(rec, 6);
5946     comp = msi_get_loaded_component(package, component);
5947     if (!comp)
5948         return ERROR_SUCCESS;
5949
5950     comp->Action = msi_get_component_action( package, comp );
5951     if (comp->Action != INSTALLSTATE_ABSENT)
5952     {
5953         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5954         return ERROR_SUCCESS;
5955     }
5956
5957     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5958     stop_service( name );
5959
5960     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5961     if (!scm)
5962     {
5963         WARN("Failed to open the SCM: %d\n", GetLastError());
5964         goto done;
5965     }
5966
5967     len = 0;
5968     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5969         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5970     {
5971         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5972             GetServiceDisplayNameW( scm, name, display_name, &len );
5973     }
5974
5975     service = OpenServiceW( scm, name, DELETE );
5976     if (!service)
5977     {
5978         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5979         goto done;
5980     }
5981
5982     if (!DeleteService( service ))
5983         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5984
5985 done:
5986     uirow = MSI_CreateRecord( 2 );
5987     MSI_RecordSetStringW( uirow, 1, display_name );
5988     MSI_RecordSetStringW( uirow, 2, name );
5989     msi_ui_actiondata( package, szDeleteServices, uirow );
5990     msiobj_release( &uirow->hdr );
5991
5992     CloseServiceHandle( service );
5993     CloseServiceHandle( scm );
5994     msi_free( name );
5995     msi_free( display_name );
5996
5997     return ERROR_SUCCESS;
5998 }
5999
6000 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6001 {
6002     static const WCHAR query[] = {
6003         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6004         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6005     MSIQUERY *view;
6006     UINT rc;
6007
6008     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6009     if (rc != ERROR_SUCCESS)
6010         return ERROR_SUCCESS;
6011
6012     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6013     msiobj_release( &view->hdr );
6014     return rc;
6015 }
6016
6017 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6018 {
6019     MSIPACKAGE *package = param;
6020     LPWSTR driver, driver_path, ptr;
6021     WCHAR outpath[MAX_PATH];
6022     MSIFILE *driver_file = NULL, *setup_file = NULL;
6023     MSICOMPONENT *comp;
6024     MSIRECORD *uirow;
6025     LPCWSTR desc, file_key, component;
6026     DWORD len, usage;
6027     UINT r = ERROR_SUCCESS;
6028
6029     static const WCHAR driver_fmt[] = {
6030         'D','r','i','v','e','r','=','%','s',0};
6031     static const WCHAR setup_fmt[] = {
6032         'S','e','t','u','p','=','%','s',0};
6033     static const WCHAR usage_fmt[] = {
6034         'F','i','l','e','U','s','a','g','e','=','1',0};
6035
6036     component = MSI_RecordGetString( rec, 2 );
6037     comp = msi_get_loaded_component( package, component );
6038     if (!comp)
6039         return ERROR_SUCCESS;
6040
6041     comp->Action = msi_get_component_action( package, comp );
6042     if (comp->Action != INSTALLSTATE_LOCAL)
6043     {
6044         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6045         return ERROR_SUCCESS;
6046     }
6047     desc = MSI_RecordGetString(rec, 3);
6048
6049     file_key = MSI_RecordGetString( rec, 4 );
6050     if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6051
6052     file_key = MSI_RecordGetString( rec, 5 );
6053     if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6054
6055     if (!driver_file)
6056     {
6057         ERR("ODBC Driver entry not found!\n");
6058         return ERROR_FUNCTION_FAILED;
6059     }
6060
6061     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6062     if (setup_file)
6063         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6064     len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6065
6066     driver = msi_alloc(len * sizeof(WCHAR));
6067     if (!driver)
6068         return ERROR_OUTOFMEMORY;
6069
6070     ptr = driver;
6071     lstrcpyW(ptr, desc);
6072     ptr += lstrlenW(ptr) + 1;
6073
6074     len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6075     ptr += len + 1;
6076
6077     if (setup_file)
6078     {
6079         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6080         ptr += len + 1;
6081     }
6082
6083     lstrcpyW(ptr, usage_fmt);
6084     ptr += lstrlenW(ptr) + 1;
6085     *ptr = '\0';
6086
6087     driver_path = strdupW(driver_file->TargetPath);
6088     ptr = strrchrW(driver_path, '\\');
6089     if (ptr) *ptr = '\0';
6090
6091     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6092                              NULL, ODBC_INSTALL_COMPLETE, &usage))
6093     {
6094         ERR("Failed to install SQL driver!\n");
6095         r = ERROR_FUNCTION_FAILED;
6096     }
6097
6098     uirow = MSI_CreateRecord( 5 );
6099     MSI_RecordSetStringW( uirow, 1, desc );
6100     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6101     MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6102     msi_ui_actiondata( package, szInstallODBC, uirow );
6103     msiobj_release( &uirow->hdr );
6104
6105     msi_free(driver);
6106     msi_free(driver_path);
6107
6108     return r;
6109 }
6110
6111 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6112 {
6113     MSIPACKAGE *package = param;
6114     LPWSTR translator, translator_path, ptr;
6115     WCHAR outpath[MAX_PATH];
6116     MSIFILE *translator_file = NULL, *setup_file = NULL;
6117     MSICOMPONENT *comp;
6118     MSIRECORD *uirow;
6119     LPCWSTR desc, file_key, component;
6120     DWORD len, usage;
6121     UINT r = ERROR_SUCCESS;
6122
6123     static const WCHAR translator_fmt[] = {
6124         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6125     static const WCHAR setup_fmt[] = {
6126         'S','e','t','u','p','=','%','s',0};
6127
6128     component = MSI_RecordGetString( rec, 2 );
6129     comp = msi_get_loaded_component( package, component );
6130     if (!comp)
6131         return ERROR_SUCCESS;
6132
6133     comp->Action = msi_get_component_action( package, comp );
6134     if (comp->Action != INSTALLSTATE_LOCAL)
6135     {
6136         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6137         return ERROR_SUCCESS;
6138     }
6139     desc = MSI_RecordGetString(rec, 3);
6140
6141     file_key = MSI_RecordGetString( rec, 4 );
6142     if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6143
6144     file_key = MSI_RecordGetString( rec, 5 );
6145     if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6146
6147     if (!translator_file)
6148     {
6149         ERR("ODBC Translator entry not found!\n");
6150         return ERROR_FUNCTION_FAILED;
6151     }
6152
6153     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6154     if (setup_file)
6155         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6156
6157     translator = msi_alloc(len * sizeof(WCHAR));
6158     if (!translator)
6159         return ERROR_OUTOFMEMORY;
6160
6161     ptr = translator;
6162     lstrcpyW(ptr, desc);
6163     ptr += lstrlenW(ptr) + 1;
6164
6165     len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6166     ptr += len + 1;
6167
6168     if (setup_file)
6169     {
6170         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6171         ptr += len + 1;
6172     }
6173     *ptr = '\0';
6174
6175     translator_path = strdupW(translator_file->TargetPath);
6176     ptr = strrchrW(translator_path, '\\');
6177     if (ptr) *ptr = '\0';
6178
6179     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6180                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
6181     {
6182         ERR("Failed to install SQL translator!\n");
6183         r = ERROR_FUNCTION_FAILED;
6184     }
6185
6186     uirow = MSI_CreateRecord( 5 );
6187     MSI_RecordSetStringW( uirow, 1, desc );
6188     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6189     MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6190     msi_ui_actiondata( package, szInstallODBC, uirow );
6191     msiobj_release( &uirow->hdr );
6192
6193     msi_free(translator);
6194     msi_free(translator_path);
6195
6196     return r;
6197 }
6198
6199 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6200 {
6201     MSIPACKAGE *package = param;
6202     MSICOMPONENT *comp;
6203     LPWSTR attrs;
6204     LPCWSTR desc, driver, component;
6205     WORD request = ODBC_ADD_SYS_DSN;
6206     INT registration;
6207     DWORD len;
6208     UINT r = ERROR_SUCCESS;
6209     MSIRECORD *uirow;
6210
6211     static const WCHAR attrs_fmt[] = {
6212         'D','S','N','=','%','s',0 };
6213
6214     component = MSI_RecordGetString( rec, 2 );
6215     comp = msi_get_loaded_component( package, component );
6216     if (!comp)
6217         return ERROR_SUCCESS;
6218
6219     comp->Action = msi_get_component_action( package, comp );
6220     if (comp->Action != INSTALLSTATE_LOCAL)
6221     {
6222         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6223         return ERROR_SUCCESS;
6224     }
6225
6226     desc = MSI_RecordGetString(rec, 3);
6227     driver = MSI_RecordGetString(rec, 4);
6228     registration = MSI_RecordGetInteger(rec, 5);
6229
6230     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6231     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6232
6233     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6234     attrs = msi_alloc(len * sizeof(WCHAR));
6235     if (!attrs)
6236         return ERROR_OUTOFMEMORY;
6237
6238     len = sprintfW(attrs, attrs_fmt, desc);
6239     attrs[len + 1] = 0;
6240
6241     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6242     {
6243         ERR("Failed to install SQL data source!\n");
6244         r = ERROR_FUNCTION_FAILED;
6245     }
6246
6247     uirow = MSI_CreateRecord( 5 );
6248     MSI_RecordSetStringW( uirow, 1, desc );
6249     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6250     MSI_RecordSetInteger( uirow, 3, request );
6251     msi_ui_actiondata( package, szInstallODBC, uirow );
6252     msiobj_release( &uirow->hdr );
6253
6254     msi_free(attrs);
6255
6256     return r;
6257 }
6258
6259 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6260 {
6261     static const WCHAR driver_query[] = {
6262         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6263         'O','D','B','C','D','r','i','v','e','r',0};
6264     static const WCHAR translator_query[] = {
6265         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6266         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6267     static const WCHAR source_query[] = {
6268         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6269         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6270     MSIQUERY *view;
6271     UINT rc;
6272
6273     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6274     if (rc == ERROR_SUCCESS)
6275     {
6276         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6277         msiobj_release(&view->hdr);
6278         if (rc != ERROR_SUCCESS)
6279             return rc;
6280     }
6281     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6282     if (rc == ERROR_SUCCESS)
6283     {
6284         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6285         msiobj_release(&view->hdr);
6286         if (rc != ERROR_SUCCESS)
6287             return rc;
6288     }
6289     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6290     if (rc == ERROR_SUCCESS)
6291     {
6292         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6293         msiobj_release(&view->hdr);
6294         if (rc != ERROR_SUCCESS)
6295             return rc;
6296     }
6297     return ERROR_SUCCESS;
6298 }
6299
6300 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6301 {
6302     MSIPACKAGE *package = param;
6303     MSICOMPONENT *comp;
6304     MSIRECORD *uirow;
6305     DWORD usage;
6306     LPCWSTR desc, component;
6307
6308     component = MSI_RecordGetString( rec, 2 );
6309     comp = msi_get_loaded_component( package, component );
6310     if (!comp)
6311         return ERROR_SUCCESS;
6312
6313     comp->Action = msi_get_component_action( package, comp );
6314     if (comp->Action != INSTALLSTATE_ABSENT)
6315     {
6316         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6317         return ERROR_SUCCESS;
6318     }
6319
6320     desc = MSI_RecordGetString( rec, 3 );
6321     if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6322     {
6323         WARN("Failed to remove ODBC driver\n");
6324     }
6325     else if (!usage)
6326     {
6327         FIXME("Usage count reached 0\n");
6328     }
6329
6330     uirow = MSI_CreateRecord( 2 );
6331     MSI_RecordSetStringW( uirow, 1, desc );
6332     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6333     msi_ui_actiondata( package, szRemoveODBC, uirow );
6334     msiobj_release( &uirow->hdr );
6335
6336     return ERROR_SUCCESS;
6337 }
6338
6339 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6340 {
6341     MSIPACKAGE *package = param;
6342     MSICOMPONENT *comp;
6343     MSIRECORD *uirow;
6344     DWORD usage;
6345     LPCWSTR desc, component;
6346
6347     component = MSI_RecordGetString( rec, 2 );
6348     comp = msi_get_loaded_component( package, component );
6349     if (!comp)
6350         return ERROR_SUCCESS;
6351
6352     comp->Action = msi_get_component_action( package, comp );
6353     if (comp->Action != INSTALLSTATE_ABSENT)
6354     {
6355         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6356         return ERROR_SUCCESS;
6357     }
6358
6359     desc = MSI_RecordGetString( rec, 3 );
6360     if (!SQLRemoveTranslatorW( desc, &usage ))
6361     {
6362         WARN("Failed to remove ODBC translator\n");
6363     }
6364     else if (!usage)
6365     {
6366         FIXME("Usage count reached 0\n");
6367     }
6368
6369     uirow = MSI_CreateRecord( 2 );
6370     MSI_RecordSetStringW( uirow, 1, desc );
6371     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6372     msi_ui_actiondata( package, szRemoveODBC, uirow );
6373     msiobj_release( &uirow->hdr );
6374
6375     return ERROR_SUCCESS;
6376 }
6377
6378 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6379 {
6380     MSIPACKAGE *package = param;
6381     MSICOMPONENT *comp;
6382     MSIRECORD *uirow;
6383     LPWSTR attrs;
6384     LPCWSTR desc, driver, component;
6385     WORD request = ODBC_REMOVE_SYS_DSN;
6386     INT registration;
6387     DWORD len;
6388
6389     static const WCHAR attrs_fmt[] = {
6390         'D','S','N','=','%','s',0 };
6391
6392     component = MSI_RecordGetString( rec, 2 );
6393     comp = msi_get_loaded_component( package, component );
6394     if (!comp)
6395         return ERROR_SUCCESS;
6396
6397     comp->Action = msi_get_component_action( package, comp );
6398     if (comp->Action != INSTALLSTATE_ABSENT)
6399     {
6400         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6401         return ERROR_SUCCESS;
6402     }
6403
6404     desc = MSI_RecordGetString( rec, 3 );
6405     driver = MSI_RecordGetString( rec, 4 );
6406     registration = MSI_RecordGetInteger( rec, 5 );
6407
6408     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6409     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6410
6411     len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6412     attrs = msi_alloc( len * sizeof(WCHAR) );
6413     if (!attrs)
6414         return ERROR_OUTOFMEMORY;
6415
6416     FIXME("Use ODBCSourceAttribute table\n");
6417
6418     len = sprintfW( attrs, attrs_fmt, desc );
6419     attrs[len + 1] = 0;
6420
6421     if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6422     {
6423         WARN("Failed to remove ODBC data source\n");
6424     }
6425     msi_free( attrs );
6426
6427     uirow = MSI_CreateRecord( 3 );
6428     MSI_RecordSetStringW( uirow, 1, desc );
6429     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6430     MSI_RecordSetInteger( uirow, 3, request );
6431     msi_ui_actiondata( package, szRemoveODBC, uirow );
6432     msiobj_release( &uirow->hdr );
6433
6434     return ERROR_SUCCESS;
6435 }
6436
6437 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6438 {
6439     static const WCHAR driver_query[] = {
6440         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6441         'O','D','B','C','D','r','i','v','e','r',0};
6442     static const WCHAR translator_query[] = {
6443         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6444         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6445     static const WCHAR source_query[] = {
6446         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6447         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6448     MSIQUERY *view;
6449     UINT rc;
6450
6451     rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6452     if (rc == ERROR_SUCCESS)
6453     {
6454         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6455         msiobj_release( &view->hdr );
6456         if (rc != ERROR_SUCCESS)
6457             return rc;
6458     }
6459     rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6460     if (rc == ERROR_SUCCESS)
6461     {
6462         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6463         msiobj_release( &view->hdr );
6464         if (rc != ERROR_SUCCESS)
6465             return rc;
6466     }
6467     rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6468     if (rc == ERROR_SUCCESS)
6469     {
6470         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6471         msiobj_release( &view->hdr );
6472         if (rc != ERROR_SUCCESS)
6473             return rc;
6474     }
6475     return ERROR_SUCCESS;
6476 }
6477
6478 #define ENV_ACT_SETALWAYS   0x1
6479 #define ENV_ACT_SETABSENT   0x2
6480 #define ENV_ACT_REMOVE      0x4
6481 #define ENV_ACT_REMOVEMATCH 0x8
6482
6483 #define ENV_MOD_MACHINE     0x20000000
6484 #define ENV_MOD_APPEND      0x40000000
6485 #define ENV_MOD_PREFIX      0x80000000
6486 #define ENV_MOD_MASK        0xC0000000
6487
6488 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6489
6490 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6491 {
6492     LPCWSTR cptr = *name;
6493
6494     static const WCHAR prefix[] = {'[','~',']',0};
6495     static const int prefix_len = 3;
6496
6497     *flags = 0;
6498     while (*cptr)
6499     {
6500         if (*cptr == '=')
6501             *flags |= ENV_ACT_SETALWAYS;
6502         else if (*cptr == '+')
6503             *flags |= ENV_ACT_SETABSENT;
6504         else if (*cptr == '-')
6505             *flags |= ENV_ACT_REMOVE;
6506         else if (*cptr == '!')
6507             *flags |= ENV_ACT_REMOVEMATCH;
6508         else if (*cptr == '*')
6509             *flags |= ENV_MOD_MACHINE;
6510         else
6511             break;
6512
6513         cptr++;
6514         (*name)++;
6515     }
6516
6517     if (!*cptr)
6518     {
6519         ERR("Missing environment variable\n");
6520         return ERROR_FUNCTION_FAILED;
6521     }
6522
6523     if (*value)
6524     {
6525         LPCWSTR ptr = *value;
6526         if (!strncmpW(ptr, prefix, prefix_len))
6527         {
6528             if (ptr[prefix_len] == szSemiColon[0])
6529             {
6530                 *flags |= ENV_MOD_APPEND;
6531                 *value += lstrlenW(prefix);
6532             }
6533             else
6534             {
6535                 *value = NULL;
6536             }
6537         }
6538         else if (lstrlenW(*value) >= prefix_len)
6539         {
6540             ptr += lstrlenW(ptr) - prefix_len;
6541             if (!strcmpW( ptr, prefix ))
6542             {
6543                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6544                 {
6545                     *flags |= ENV_MOD_PREFIX;
6546                     /* the "[~]" will be removed by deformat_string */;
6547                 }
6548                 else
6549                 {
6550                     *value = NULL;
6551                 }
6552             }
6553         }
6554     }
6555
6556     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6557         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6558         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6559         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6560     {
6561         ERR("Invalid flags: %08x\n", *flags);
6562         return ERROR_FUNCTION_FAILED;
6563     }
6564
6565     if (!*flags)
6566         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6567
6568     return ERROR_SUCCESS;
6569 }
6570
6571 static UINT open_env_key( DWORD flags, HKEY *key )
6572 {
6573     static const WCHAR user_env[] =
6574         {'E','n','v','i','r','o','n','m','e','n','t',0};
6575     static const WCHAR machine_env[] =
6576         {'S','y','s','t','e','m','\\',
6577          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6578          'C','o','n','t','r','o','l','\\',
6579          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6580          'E','n','v','i','r','o','n','m','e','n','t',0};
6581     const WCHAR *env;
6582     HKEY root;
6583     LONG res;
6584
6585     if (flags & ENV_MOD_MACHINE)
6586     {
6587         env = machine_env;
6588         root = HKEY_LOCAL_MACHINE;
6589     }
6590     else
6591     {
6592         env = user_env;
6593         root = HKEY_CURRENT_USER;
6594     }
6595
6596     res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6597     if (res != ERROR_SUCCESS)
6598     {
6599         WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6600         return ERROR_FUNCTION_FAILED;
6601     }
6602
6603     return ERROR_SUCCESS;
6604 }
6605
6606 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6607 {
6608     MSIPACKAGE *package = param;
6609     LPCWSTR name, value, component;
6610     LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6611     DWORD flags, type, size;
6612     UINT res;
6613     HKEY env = NULL;
6614     MSICOMPONENT *comp;
6615     MSIRECORD *uirow;
6616     int action = 0;
6617
6618     component = MSI_RecordGetString(rec, 4);
6619     comp = msi_get_loaded_component(package, component);
6620     if (!comp)
6621         return ERROR_SUCCESS;
6622
6623     comp->Action = msi_get_component_action( package, comp );
6624     if (comp->Action != INSTALLSTATE_LOCAL)
6625     {
6626         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6627         return ERROR_SUCCESS;
6628     }
6629     name = MSI_RecordGetString(rec, 2);
6630     value = MSI_RecordGetString(rec, 3);
6631
6632     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6633
6634     res = env_parse_flags(&name, &value, &flags);
6635     if (res != ERROR_SUCCESS || !value)
6636        goto done;
6637
6638     if (value && !deformat_string(package, value, &deformatted))
6639     {
6640         res = ERROR_OUTOFMEMORY;
6641         goto done;
6642     }
6643
6644     value = deformatted;
6645
6646     res = open_env_key( flags, &env );
6647     if (res != ERROR_SUCCESS)
6648         goto done;
6649
6650     if (flags & ENV_MOD_MACHINE)
6651         action |= 0x20000000;
6652
6653     size = 0;
6654     type = REG_SZ;
6655     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6656     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6657         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6658         goto done;
6659
6660     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6661     {
6662         action = 0x2;
6663
6664         /* Nothing to do. */
6665         if (!value)
6666         {
6667             res = ERROR_SUCCESS;
6668             goto done;
6669         }
6670
6671         /* If we are appending but the string was empty, strip ; */
6672         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6673
6674         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6675         newval = strdupW(value);
6676         if (!newval)
6677         {
6678             res = ERROR_OUTOFMEMORY;
6679             goto done;
6680         }
6681     }
6682     else
6683     {
6684         action = 0x1;
6685
6686         /* Contrary to MSDN, +-variable to [~];path works */
6687         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6688         {
6689             res = ERROR_SUCCESS;
6690             goto done;
6691         }
6692
6693         data = msi_alloc(size);
6694         if (!data)
6695         {
6696             RegCloseKey(env);
6697             return ERROR_OUTOFMEMORY;
6698         }
6699
6700         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6701         if (res != ERROR_SUCCESS)
6702             goto done;
6703
6704         if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6705         {
6706             action = 0x4;
6707             res = RegDeleteValueW(env, name);
6708             if (res != ERROR_SUCCESS)
6709                 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6710             goto done;
6711         }
6712
6713         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6714         if (flags & ENV_MOD_MASK)
6715         {
6716             DWORD mod_size;
6717             int multiplier = 0;
6718             if (flags & ENV_MOD_APPEND) multiplier++;
6719             if (flags & ENV_MOD_PREFIX) multiplier++;
6720             mod_size = lstrlenW(value) * multiplier;
6721             size += mod_size * sizeof(WCHAR);
6722         }
6723
6724         newval = msi_alloc(size);
6725         ptr = newval;
6726         if (!newval)
6727         {
6728             res = ERROR_OUTOFMEMORY;
6729             goto done;
6730         }
6731
6732         if (flags & ENV_MOD_PREFIX)
6733         {
6734             lstrcpyW(newval, value);
6735             ptr = newval + lstrlenW(value);
6736             action |= 0x80000000;
6737         }
6738
6739         lstrcpyW(ptr, data);
6740
6741         if (flags & ENV_MOD_APPEND)
6742         {
6743             lstrcatW(newval, value);
6744             action |= 0x40000000;
6745         }
6746     }
6747     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6748     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6749     if (res)
6750     {
6751         WARN("Failed to set %s to %s (%d)\n",  debugstr_w(name), debugstr_w(newval), res);
6752     }
6753
6754 done:
6755     uirow = MSI_CreateRecord( 3 );
6756     MSI_RecordSetStringW( uirow, 1, name );
6757     MSI_RecordSetStringW( uirow, 2, newval );
6758     MSI_RecordSetInteger( uirow, 3, action );
6759     msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6760     msiobj_release( &uirow->hdr );
6761
6762     if (env) RegCloseKey(env);
6763     msi_free(deformatted);
6764     msi_free(data);
6765     msi_free(newval);
6766     return res;
6767 }
6768
6769 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6770 {
6771     static const WCHAR query[] = {
6772         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6773         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6774     MSIQUERY *view;
6775     UINT rc;
6776
6777     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6778     if (rc != ERROR_SUCCESS)
6779         return ERROR_SUCCESS;
6780
6781     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6782     msiobj_release(&view->hdr);
6783     return rc;
6784 }
6785
6786 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6787 {
6788     MSIPACKAGE *package = param;
6789     LPCWSTR name, value, component;
6790     LPWSTR deformatted = NULL;
6791     DWORD flags;
6792     HKEY env;
6793     MSICOMPONENT *comp;
6794     MSIRECORD *uirow;
6795     int action = 0;
6796     LONG res;
6797     UINT r;
6798
6799     component = MSI_RecordGetString( rec, 4 );
6800     comp = msi_get_loaded_component( package, component );
6801     if (!comp)
6802         return ERROR_SUCCESS;
6803
6804     comp->Action = msi_get_component_action( package, comp );
6805     if (comp->Action != INSTALLSTATE_ABSENT)
6806     {
6807         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6808         return ERROR_SUCCESS;
6809     }
6810     name = MSI_RecordGetString( rec, 2 );
6811     value = MSI_RecordGetString( rec, 3 );
6812
6813     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6814
6815     r = env_parse_flags( &name, &value, &flags );
6816     if (r != ERROR_SUCCESS)
6817        return r;
6818
6819     if (!(flags & ENV_ACT_REMOVE))
6820     {
6821         TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6822         return ERROR_SUCCESS;
6823     }
6824
6825     if (value && !deformat_string( package, value, &deformatted ))
6826         return ERROR_OUTOFMEMORY;
6827
6828     value = deformatted;
6829
6830     r = open_env_key( flags, &env );
6831     if (r != ERROR_SUCCESS)
6832     {
6833         r = ERROR_SUCCESS;
6834         goto done;
6835     }
6836
6837     if (flags & ENV_MOD_MACHINE)
6838         action |= 0x20000000;
6839
6840     TRACE("Removing %s\n", debugstr_w(name));
6841
6842     res = RegDeleteValueW( env, name );
6843     if (res != ERROR_SUCCESS)
6844     {
6845         WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6846         r = ERROR_SUCCESS;
6847     }
6848
6849 done:
6850     uirow = MSI_CreateRecord( 3 );
6851     MSI_RecordSetStringW( uirow, 1, name );
6852     MSI_RecordSetStringW( uirow, 2, value );
6853     MSI_RecordSetInteger( uirow, 3, action );
6854     msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6855     msiobj_release( &uirow->hdr );
6856
6857     if (env) RegCloseKey( env );
6858     msi_free( deformatted );
6859     return r;
6860 }
6861
6862 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6863 {
6864     static const WCHAR query[] = {
6865         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6866         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6867     MSIQUERY *view;
6868     UINT rc;
6869
6870     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6871     if (rc != ERROR_SUCCESS)
6872         return ERROR_SUCCESS;
6873
6874     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6875     msiobj_release( &view->hdr );
6876     return rc;
6877 }
6878
6879 UINT msi_validate_product_id( MSIPACKAGE *package )
6880 {
6881     LPWSTR key, template, id;
6882     UINT r = ERROR_SUCCESS;
6883
6884     id = msi_dup_property( package->db, szProductID );
6885     if (id)
6886     {
6887         msi_free( id );
6888         return ERROR_SUCCESS;
6889     }
6890     template = msi_dup_property( package->db, szPIDTemplate );
6891     key = msi_dup_property( package->db, szPIDKEY );
6892     if (key && template)
6893     {
6894         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6895         r = msi_set_property( package->db, szProductID, key );
6896     }
6897     msi_free( template );
6898     msi_free( key );
6899     return r;
6900 }
6901
6902 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6903 {
6904     return msi_validate_product_id( package );
6905 }
6906
6907 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6908 {
6909     TRACE("\n");
6910     package->need_reboot = 1;
6911     return ERROR_SUCCESS;
6912 }
6913
6914 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6915 {
6916     static const WCHAR szAvailableFreeReg[] =
6917         {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6918     MSIRECORD *uirow;
6919     int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6920
6921     TRACE("%p %d kilobytes\n", package, space);
6922
6923     uirow = MSI_CreateRecord( 1 );
6924     MSI_RecordSetInteger( uirow, 1, space );
6925     msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6926     msiobj_release( &uirow->hdr );
6927
6928     return ERROR_SUCCESS;
6929 }
6930
6931 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6932 {
6933     TRACE("%p\n", package);
6934
6935     msi_set_property( package->db, szRollbackDisabled, szOne );
6936     return ERROR_SUCCESS;
6937 }
6938
6939 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6940 {
6941     FIXME("%p\n", package);
6942     return ERROR_SUCCESS;
6943 }
6944
6945 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6946 {
6947     static const WCHAR driver_query[] = {
6948         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6949         'O','D','B','C','D','r','i','v','e','r',0};
6950     static const WCHAR translator_query[] = {
6951         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6952         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6953     MSIQUERY *view;
6954     UINT r, count;
6955
6956     r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6957     if (r == ERROR_SUCCESS)
6958     {
6959         count = 0;
6960         r = MSI_IterateRecords( view, &count, NULL, package );
6961         msiobj_release( &view->hdr );
6962         if (r != ERROR_SUCCESS)
6963             return r;
6964         if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6965     }
6966     r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6967     if (r == ERROR_SUCCESS)
6968     {
6969         count = 0;
6970         r = MSI_IterateRecords( view, &count, NULL, package );
6971         msiobj_release( &view->hdr );
6972         if (r != ERROR_SUCCESS)
6973             return r;
6974         if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6975     }
6976     return ERROR_SUCCESS;
6977 }
6978
6979 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6980 {
6981     MSIPACKAGE *package = param;
6982     const WCHAR *property = MSI_RecordGetString( rec, 1 );
6983     WCHAR *value;
6984
6985     if ((value = msi_dup_property( package->db, property )))
6986     {
6987         FIXME("remove %s\n", debugstr_w(value));
6988         msi_free( value );
6989     }
6990     return ERROR_SUCCESS;
6991 }
6992
6993 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6994 {
6995     static const WCHAR query[] = {
6996         'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6997         'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6998     MSIQUERY *view;
6999     UINT r;
7000
7001     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7002     if (r == ERROR_SUCCESS)
7003     {
7004         r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7005         msiobj_release( &view->hdr );
7006         if (r != ERROR_SUCCESS)
7007             return r;
7008     }
7009     return ERROR_SUCCESS;
7010 }
7011
7012 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7013 {
7014     MSIPACKAGE *package = param;
7015     int attributes = MSI_RecordGetInteger( rec, 5 );
7016
7017     if (attributes & msidbUpgradeAttributesMigrateFeatures)
7018     {
7019         const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7020         const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7021         const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7022         const WCHAR *language = MSI_RecordGetString( rec, 4 );
7023         HKEY hkey;
7024         UINT r;
7025
7026         if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7027         {
7028             r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7029             if (r != ERROR_SUCCESS)
7030                 return ERROR_SUCCESS;
7031         }
7032         else
7033         {
7034             r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7035             if (r != ERROR_SUCCESS)
7036                 return ERROR_SUCCESS;
7037         }
7038         RegCloseKey( hkey );
7039
7040         FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7041               debugstr_w(upgrade_code), debugstr_w(version_min),
7042               debugstr_w(version_max), debugstr_w(language));
7043     }
7044     return ERROR_SUCCESS;
7045 }
7046
7047 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7048 {
7049     static const WCHAR query[] = {
7050         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7051         'U','p','g','r','a','d','e',0};
7052     MSIQUERY *view;
7053     UINT r;
7054
7055     if (msi_get_property_int( package->db, szInstalled, 0 ))
7056     {
7057         TRACE("product is installed, skipping action\n");
7058         return ERROR_SUCCESS;
7059     }
7060     if (msi_get_property_int( package->db, szPreselected, 0 ))
7061     {
7062         TRACE("Preselected property is set, not migrating feature states\n");
7063         return ERROR_SUCCESS;
7064     }
7065     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7066     if (r == ERROR_SUCCESS)
7067     {
7068         r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7069         msiobj_release( &view->hdr );
7070         if (r != ERROR_SUCCESS)
7071             return r;
7072     }
7073     return ERROR_SUCCESS;
7074 }
7075
7076 static void bind_image( const char *filename, const char *path )
7077 {
7078     if (!BindImageEx( 0, filename, path, NULL, NULL ))
7079     {
7080         WARN("failed to bind image %u\n", GetLastError());
7081     }
7082 }
7083
7084 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7085 {
7086     UINT i;
7087     MSIFILE *file;
7088     MSIPACKAGE *package = param;
7089     const WCHAR *key = MSI_RecordGetString( rec, 1 );
7090     const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7091     char *filenameA, *pathA;
7092     WCHAR *pathW, **path_list;
7093
7094     if (!(file = msi_get_loaded_file( package, key )))
7095     {
7096         WARN("file %s not found\n", debugstr_w(key));
7097         return ERROR_SUCCESS;
7098     }
7099     if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7100     path_list = msi_split_string( paths, ';' );
7101     if (!path_list) bind_image( filenameA, NULL );
7102     else
7103     {
7104         for (i = 0; path_list[i] && path_list[i][0]; i++)
7105         {
7106             deformat_string( package, path_list[i], &pathW );
7107             if ((pathA = strdupWtoA( pathW )))
7108             {
7109                 bind_image( filenameA, pathA );
7110                 msi_free( pathA );
7111             }
7112             msi_free( pathW );
7113         }
7114     }
7115     msi_free( path_list );
7116     msi_free( filenameA );
7117     return ERROR_SUCCESS;
7118 }
7119
7120 static UINT ACTION_BindImage( MSIPACKAGE *package )
7121 {
7122     static const WCHAR query[] = {
7123         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7124         'B','i','n','d','I','m','a','g','e',0};
7125     MSIQUERY *view;
7126     UINT r;
7127
7128     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7129     if (r == ERROR_SUCCESS)
7130     {
7131         r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7132         msiobj_release( &view->hdr );
7133         if (r != ERROR_SUCCESS)
7134             return r;
7135     }
7136     return ERROR_SUCCESS;
7137 }
7138
7139 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7140 {
7141     static const WCHAR query[] = {
7142         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7143     MSIQUERY *view;
7144     DWORD count = 0;
7145     UINT r;
7146     
7147     r = MSI_OpenQuery( package->db, &view, query, table );
7148     if (r == ERROR_SUCCESS)
7149     {
7150         r = MSI_IterateRecords(view, &count, NULL, package);
7151         msiobj_release(&view->hdr);
7152         if (r != ERROR_SUCCESS)
7153             return r;
7154     }
7155     if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7156     return ERROR_SUCCESS;
7157 }
7158
7159 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7160 {
7161     static const WCHAR table[] = {
7162         'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7163     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7164 }
7165
7166 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7167 {
7168     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7169     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7170 }
7171
7172 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7173 {
7174     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7175     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7176 }
7177
7178 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7179 {
7180     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7181     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7182 }
7183
7184 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7185 {
7186     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7187     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7188 }
7189
7190 static const struct
7191 {
7192     const WCHAR *action;
7193     UINT (*handler)(MSIPACKAGE *);
7194     const WCHAR *action_rollback;
7195 }
7196 StandardActions[] =
7197 {
7198     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7199     { szAppSearch, ACTION_AppSearch, NULL },
7200     { szBindImage, ACTION_BindImage, NULL },
7201     { szCCPSearch, ACTION_CCPSearch, NULL },
7202     { szCostFinalize, ACTION_CostFinalize, NULL },
7203     { szCostInitialize, ACTION_CostInitialize, NULL },
7204     { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7205     { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7206     { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7207     { szDisableRollback, ACTION_DisableRollback, NULL },
7208     { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7209     { szExecuteAction, ACTION_ExecuteAction, NULL },
7210     { szFileCost, ACTION_FileCost, NULL },
7211     { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7212     { szForceReboot, ACTION_ForceReboot, NULL },
7213     { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7214     { szInstallExecute, ACTION_InstallExecute, NULL },
7215     { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7216     { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7217     { szInstallFinalize, ACTION_InstallFinalize, NULL },
7218     { szInstallInitialize, ACTION_InstallInitialize, NULL },
7219     { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7220     { szInstallServices, ACTION_InstallServices, szDeleteServices },
7221     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7222     { szInstallValidate, ACTION_InstallValidate, NULL },
7223     { szIsolateComponents, ACTION_IsolateComponents, NULL },
7224     { szLaunchConditions, ACTION_LaunchConditions, NULL },
7225     { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7226     { szMoveFiles, ACTION_MoveFiles, NULL },
7227     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7228     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7229     { szPatchFiles, ACTION_PatchFiles, NULL },
7230     { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7231     { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7232     { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7233     { szPublishProduct, ACTION_PublishProduct, NULL },
7234     { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7235     { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7236     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7237     { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7238     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7239     { szRegisterProduct, ACTION_RegisterProduct, NULL },
7240     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7241     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7242     { szRegisterUser, ACTION_RegisterUser, NULL },
7243     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7244     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7245     { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7246     { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7247     { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7248     { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7249     { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7250     { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7251     { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7252     { szResolveSource, ACTION_ResolveSource, NULL },
7253     { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7254     { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7255     { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7256     { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7257     { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7258     { szStartServices, ACTION_StartServices, szStopServices },
7259     { szStopServices, ACTION_StopServices, szStartServices },
7260     { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7261     { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7262     { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7263     { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7264     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7265     { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7266     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7267     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7268     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7269     { szValidateProductID, ACTION_ValidateProductID, NULL },
7270     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7271     { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7272     { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7273     { NULL, NULL, NULL }
7274 };
7275
7276 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7277 {
7278     BOOL ret = FALSE;
7279     UINT i;
7280
7281     i = 0;
7282     while (StandardActions[i].action != NULL)
7283     {
7284         if (!strcmpW( StandardActions[i].action, action ))
7285         {
7286             ui_actionstart( package, action );
7287             if (StandardActions[i].handler)
7288             {
7289                 ui_actioninfo( package, action, TRUE, 0 );
7290                 *rc = StandardActions[i].handler( package );
7291                 ui_actioninfo( package, action, FALSE, *rc );
7292
7293                 if (StandardActions[i].action_rollback && !package->need_rollback)
7294                 {
7295                     TRACE("scheduling rollback action\n");
7296                     msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7297                 }
7298             }
7299             else
7300             {
7301                 FIXME("unhandled standard action %s\n", debugstr_w(action));
7302                 *rc = ERROR_SUCCESS;
7303             }
7304             ret = TRUE;
7305             break;
7306         }
7307         i++;
7308     }
7309     return ret;
7310 }
7311
7312 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7313 {
7314     UINT rc = ERROR_SUCCESS;
7315     BOOL handled;
7316
7317     TRACE("Performing action (%s)\n", debugstr_w(action));
7318
7319     handled = ACTION_HandleStandardAction(package, action, &rc);
7320
7321     if (!handled)
7322         handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7323
7324     if (!handled)
7325     {
7326         WARN("unhandled msi action %s\n", debugstr_w(action));
7327         rc = ERROR_FUNCTION_NOT_CALLED;
7328     }
7329
7330     return rc;
7331 }
7332
7333 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7334 {
7335     UINT rc = ERROR_SUCCESS;
7336     BOOL handled = FALSE;
7337
7338     TRACE("Performing action (%s)\n", debugstr_w(action));
7339
7340     handled = ACTION_HandleStandardAction(package, action, &rc);
7341
7342     if (!handled)
7343         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7344
7345     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7346         handled = TRUE;
7347
7348     if (!handled)
7349     {
7350         WARN("unhandled msi action %s\n", debugstr_w(action));
7351         rc = ERROR_FUNCTION_NOT_CALLED;
7352     }
7353
7354     return rc;
7355 }
7356
7357 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7358 {
7359     UINT rc = ERROR_SUCCESS;
7360     MSIRECORD *row;
7361
7362     static const WCHAR query[] =
7363         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7364          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7365          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7366          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7367     static const WCHAR ui_query[] =
7368         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7369      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7370      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7371          ' ', '=',' ','%','i',0};
7372
7373     if (needs_ui_sequence(package))
7374         row = MSI_QueryGetRecord(package->db, ui_query, seq);
7375     else
7376         row = MSI_QueryGetRecord(package->db, query, seq);
7377
7378     if (row)
7379     {
7380         LPCWSTR action, cond;
7381
7382         TRACE("Running the actions\n");
7383
7384         /* check conditions */
7385         cond = MSI_RecordGetString(row, 2);
7386
7387         /* this is a hack to skip errors in the condition code */
7388         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7389         {
7390             msiobj_release(&row->hdr);
7391             return ERROR_SUCCESS;
7392         }
7393
7394         action = MSI_RecordGetString(row, 1);
7395         if (!action)
7396         {
7397             ERR("failed to fetch action\n");
7398             msiobj_release(&row->hdr);
7399             return ERROR_FUNCTION_FAILED;
7400         }
7401
7402         if (needs_ui_sequence(package))
7403             rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7404         else
7405             rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7406
7407         msiobj_release(&row->hdr);
7408     }
7409
7410     return rc;
7411 }
7412
7413 /****************************************************
7414  * TOP level entry points
7415  *****************************************************/
7416
7417 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7418                          LPCWSTR szCommandLine )
7419 {
7420     static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7421     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7422     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7423     WCHAR *reinstall = NULL;
7424     BOOL ui_exists;
7425     UINT rc;
7426
7427     msi_set_property( package->db, szAction, szInstall );
7428
7429     package->script->InWhatSequence = SEQUENCE_INSTALL;
7430
7431     if (szPackagePath)
7432     {
7433         LPWSTR p, dir;
7434         LPCWSTR file;
7435
7436         dir = strdupW(szPackagePath);
7437         p = strrchrW(dir, '\\');
7438         if (p)
7439         {
7440             *(++p) = 0;
7441             file = szPackagePath + (p - dir);
7442         }
7443         else
7444         {
7445             msi_free(dir);
7446             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7447             GetCurrentDirectoryW(MAX_PATH, dir);
7448             lstrcatW(dir, szBackSlash);
7449             file = szPackagePath;
7450         }
7451
7452         msi_free( package->PackagePath );
7453         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7454         if (!package->PackagePath)
7455         {
7456             msi_free(dir);
7457             return ERROR_OUTOFMEMORY;
7458         }
7459
7460         lstrcpyW(package->PackagePath, dir);
7461         lstrcatW(package->PackagePath, file);
7462         msi_free(dir);
7463
7464         msi_set_sourcedir_props(package, FALSE);
7465     }
7466
7467     rc = msi_parse_command_line( package, szCommandLine, FALSE );
7468     if (rc != ERROR_SUCCESS)
7469         return rc;
7470
7471     msi_apply_transforms( package );
7472     msi_apply_patches( package );
7473
7474     if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7475     {
7476         TRACE("setting reinstall property\n");
7477         msi_set_property( package->db, szReinstall, szAll );
7478     }
7479
7480     /* properties may have been added by a transform */
7481     msi_clone_properties( package );
7482
7483     msi_parse_command_line( package, szCommandLine, FALSE );
7484     msi_adjust_privilege_properties( package );
7485     msi_set_context( package );
7486
7487     if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7488     {
7489         TRACE("disabling rollback\n");
7490         msi_set_property( package->db, szRollbackDisabled, szOne );
7491     }
7492
7493     if (needs_ui_sequence( package))
7494     {
7495         package->script->InWhatSequence |= SEQUENCE_UI;
7496         rc = ACTION_ProcessUISequence(package);
7497         ui_exists = ui_sequence_exists(package);
7498         if (rc == ERROR_SUCCESS || !ui_exists)
7499         {
7500             package->script->InWhatSequence |= SEQUENCE_EXEC;
7501             rc = ACTION_ProcessExecSequence(package, ui_exists);
7502         }
7503     }
7504     else
7505         rc = ACTION_ProcessExecSequence(package, FALSE);
7506
7507     package->script->CurrentlyScripting = FALSE;
7508
7509     /* process the ending type action */
7510     if (rc == ERROR_SUCCESS)
7511         ACTION_PerformActionSequence(package, -1);
7512     else if (rc == ERROR_INSTALL_USEREXIT)
7513         ACTION_PerformActionSequence(package, -2);
7514     else if (rc == ERROR_INSTALL_SUSPEND)
7515         ACTION_PerformActionSequence(package, -4);
7516     else  /* failed */
7517     {
7518         ACTION_PerformActionSequence(package, -3);
7519         if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7520         {
7521             package->need_rollback = TRUE;
7522         }
7523     }
7524
7525     /* finish up running custom actions */
7526     ACTION_FinishCustomActions(package);
7527
7528     if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7529     {
7530         WARN("installation failed, running rollback script\n");
7531         execute_script( package, SCRIPT_ROLLBACK );
7532     }
7533     msi_free( reinstall );
7534
7535     if (rc == ERROR_SUCCESS && package->need_reboot)
7536         return ERROR_SUCCESS_REBOOT_REQUIRED;
7537
7538     return rc;
7539 }