mshtml: Wine Gecko 1.4 release.
[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, -1);
528     else
529         rc = ACTION_PerformAction(package, action, -1);
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 == ROLLBACK_SCRIPT)
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         parent = msi_get_loaded_folder( package, folder->Parent );
2289         path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2290     }
2291     msi_clean_path( path );
2292     if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
2293     {
2294         TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2295         msi_free( path );
2296         return;
2297     }
2298     msi_set_property( package->db, folder->Directory, path );
2299     msi_free( folder->ResolvedTarget );
2300     folder->ResolvedTarget = path;
2301
2302     LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2303     {
2304         child = fl->folder;
2305         msi_resolve_target_folder( package, child->Directory, load_prop );
2306     }
2307     TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2308 }
2309
2310 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2311 {
2312     static const WCHAR query[] = {
2313         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2314         '`','C','o','n','d','i','t','i','o','n','`',0};
2315     static const WCHAR szOutOfDiskSpace[] = {
2316         'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2317     MSICOMPONENT *comp;
2318     MSIQUERY *view;
2319     LPWSTR level;
2320     UINT rc;
2321
2322     TRACE("Building directory properties\n");
2323     msi_resolve_target_folder( package, szTargetDir, TRUE );
2324
2325     TRACE("Evaluating component conditions\n");
2326     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2327     {
2328         if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2329         {
2330             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2331             comp->Enabled = FALSE;
2332         }
2333         else
2334             comp->Enabled = TRUE;
2335     }
2336
2337     /* read components states from the registry */
2338     ACTION_GetComponentInstallStates(package);
2339     ACTION_GetFeatureInstallStates(package);
2340
2341     if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2342     {
2343         TRACE("Evaluating feature conditions\n");
2344
2345         rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2346         if (rc == ERROR_SUCCESS)
2347         {
2348             rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2349             msiobj_release( &view->hdr );
2350             if (rc != ERROR_SUCCESS)
2351                 return rc;
2352         }
2353     }
2354
2355     TRACE("Calculating file cost\n");
2356     calculate_file_cost( package );
2357
2358     msi_set_property( package->db, szCostingComplete, szOne );
2359     /* set default run level if not set */
2360     level = msi_dup_property( package->db, szInstallLevel );
2361     if (!level)
2362         msi_set_property( package->db, szInstallLevel, szOne );
2363     msi_free(level);
2364
2365     /* FIXME: check volume disk space */
2366     msi_set_property( package->db, szOutOfDiskSpace, szZero );
2367
2368     return MSI_SetFeatureStates(package);
2369 }
2370
2371 /* OK this value is "interpreted" and then formatted based on the 
2372    first few characters */
2373 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2374                          DWORD *size)
2375 {
2376     LPSTR data = NULL;
2377
2378     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2379     {
2380         if (value[1]=='x')
2381         {
2382             LPWSTR ptr;
2383             CHAR byte[5];
2384             LPWSTR deformated = NULL;
2385             int count;
2386
2387             deformat_string(package, &value[2], &deformated);
2388
2389             /* binary value type */
2390             ptr = deformated;
2391             *type = REG_BINARY;
2392             if (strlenW(ptr)%2)
2393                 *size = (strlenW(ptr)/2)+1;
2394             else
2395                 *size = strlenW(ptr)/2;
2396
2397             data = msi_alloc(*size);
2398
2399             byte[0] = '0'; 
2400             byte[1] = 'x'; 
2401             byte[4] = 0; 
2402             count = 0;
2403             /* if uneven pad with a zero in front */
2404             if (strlenW(ptr)%2)
2405             {
2406                 byte[2]= '0';
2407                 byte[3]= *ptr;
2408                 ptr++;
2409                 data[count] = (BYTE)strtol(byte,NULL,0);
2410                 count ++;
2411                 TRACE("Uneven byte count\n");
2412             }
2413             while (*ptr)
2414             {
2415                 byte[2]= *ptr;
2416                 ptr++;
2417                 byte[3]= *ptr;
2418                 ptr++;
2419                 data[count] = (BYTE)strtol(byte,NULL,0);
2420                 count ++;
2421             }
2422             msi_free(deformated);
2423
2424             TRACE("Data %i bytes(%i)\n",*size,count);
2425         }
2426         else
2427         {
2428             LPWSTR deformated;
2429             LPWSTR p;
2430             DWORD d = 0;
2431             deformat_string(package, &value[1], &deformated);
2432
2433             *type=REG_DWORD; 
2434             *size = sizeof(DWORD);
2435             data = msi_alloc(*size);
2436             p = deformated;
2437             if (*p == '-')
2438                 p++;
2439             while (*p)
2440             {
2441                 if ( (*p < '0') || (*p > '9') )
2442                     break;
2443                 d *= 10;
2444                 d += (*p - '0');
2445                 p++;
2446             }
2447             if (deformated[0] == '-')
2448                 d = -d;
2449             *(LPDWORD)data = d;
2450             TRACE("DWORD %i\n",*(LPDWORD)data);
2451
2452             msi_free(deformated);
2453         }
2454     }
2455     else
2456     {
2457         static const WCHAR szMulti[] = {'[','~',']',0};
2458         LPCWSTR ptr;
2459         *type=REG_SZ;
2460
2461         if (value[0]=='#')
2462         {
2463             if (value[1]=='%')
2464             {
2465                 ptr = &value[2];
2466                 *type=REG_EXPAND_SZ;
2467             }
2468             else
2469                 ptr = &value[1];
2470          }
2471          else
2472             ptr=value;
2473
2474         if (strstrW(value, szMulti))
2475             *type = REG_MULTI_SZ;
2476
2477         /* remove initial delimiter */
2478         if (!strncmpW(value, szMulti, 3))
2479             ptr = value + 3;
2480
2481         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2482
2483         /* add double NULL terminator */
2484         if (*type == REG_MULTI_SZ)
2485         {
2486             *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2487             data = msi_realloc_zero(data, *size);
2488         }
2489     }
2490     return data;
2491 }
2492
2493 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2494 {
2495     const WCHAR *ret;
2496
2497     switch (root)
2498     {
2499     case -1:
2500         if (msi_get_property_int( package->db, szAllUsers, 0 ))
2501         {
2502             *root_key = HKEY_LOCAL_MACHINE;
2503             ret = szHLM;
2504         }
2505         else
2506         {
2507             *root_key = HKEY_CURRENT_USER;
2508             ret = szHCU;
2509         }
2510         break;
2511     case 0:
2512         *root_key = HKEY_CLASSES_ROOT;
2513         ret = szHCR;
2514         break;
2515     case 1:
2516         *root_key = HKEY_CURRENT_USER;
2517         ret = szHCU;
2518         break;
2519     case 2:
2520         *root_key = HKEY_LOCAL_MACHINE;
2521         ret = szHLM;
2522         break;
2523     case 3:
2524         *root_key = HKEY_USERS;
2525         ret = szHU;
2526         break;
2527     default:
2528         ERR("Unknown root %i\n", root);
2529         return NULL;
2530     }
2531
2532     return ret;
2533 }
2534
2535 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2536 {
2537     static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2538     static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2539
2540     if (is_64bit && package->platform == PLATFORM_INTEL &&
2541         root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2542     {
2543         UINT size;
2544         WCHAR *path_32node;
2545
2546         size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2547         if (!(path_32node = msi_alloc( size ))) return NULL;
2548
2549         memcpy( path_32node, path, len * sizeof(WCHAR) );
2550         strcpyW( path_32node + len, szWow6432Node );
2551         strcatW( path_32node, szBackSlash );
2552         strcatW( path_32node, path + len );
2553         return path_32node;
2554     }
2555
2556     return strdupW( path );
2557 }
2558
2559 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2560 {
2561     MSIPACKAGE *package = param;
2562     LPSTR value_data = NULL;
2563     HKEY  root_key, hkey;
2564     DWORD type,size;
2565     LPWSTR deformated, uikey, keypath;
2566     LPCWSTR szRoot, component, name, key, value;
2567     MSICOMPONENT *comp;
2568     MSIRECORD * uirow;
2569     INT   root;
2570     BOOL check_first = FALSE;
2571     UINT rc;
2572
2573     msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2574
2575     component = MSI_RecordGetString(row, 6);
2576     comp = msi_get_loaded_component(package,component);
2577     if (!comp)
2578         return ERROR_SUCCESS;
2579
2580     comp->Action = msi_get_component_action( package, comp );
2581     if (comp->Action != INSTALLSTATE_LOCAL)
2582     {
2583         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2584         return ERROR_SUCCESS;
2585     }
2586
2587     name = MSI_RecordGetString(row, 4);
2588     if( MSI_RecordIsNull(row,5) && name )
2589     {
2590         /* null values can have special meanings */
2591         if (name[0]=='-' && name[1] == 0)
2592                 return ERROR_SUCCESS;
2593         else if ((name[0]=='+' && name[1] == 0) || 
2594                  (name[0] == '*' && name[1] == 0))
2595                 name = NULL;
2596         check_first = TRUE;
2597     }
2598
2599     root = MSI_RecordGetInteger(row,2);
2600     key = MSI_RecordGetString(row, 3);
2601
2602     szRoot = get_root_key( package, root, &root_key );
2603     if (!szRoot)
2604         return ERROR_SUCCESS;
2605
2606     deformat_string(package, key , &deformated);
2607     size = strlenW(deformated) + strlenW(szRoot) + 1;
2608     uikey = msi_alloc(size*sizeof(WCHAR));
2609     strcpyW(uikey,szRoot);
2610     strcatW(uikey,deformated);
2611
2612     keypath = get_keypath( package, root_key, deformated );
2613     msi_free( deformated );
2614     if (RegCreateKeyW( root_key, keypath, &hkey ))
2615     {
2616         ERR("Could not create key %s\n", debugstr_w(keypath));
2617         msi_free(uikey);
2618         msi_free(keypath);
2619         return ERROR_SUCCESS;
2620     }
2621
2622     value = MSI_RecordGetString(row,5);
2623     if (value)
2624         value_data = parse_value(package, value, &type, &size); 
2625     else
2626     {
2627         value_data = (LPSTR)strdupW(szEmpty);
2628         size = sizeof(szEmpty);
2629         type = REG_SZ;
2630     }
2631
2632     deformat_string(package, name, &deformated);
2633
2634     if (!check_first)
2635     {
2636         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2637                         debugstr_w(uikey));
2638         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2639     }
2640     else
2641     {
2642         DWORD sz = 0;
2643         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2644         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2645         {
2646             TRACE("value %s of %s checked already exists\n",
2647                             debugstr_w(deformated), debugstr_w(uikey));
2648         }
2649         else
2650         {
2651             TRACE("Checked and setting value %s of %s\n",
2652                             debugstr_w(deformated), debugstr_w(uikey));
2653             if (deformated || size)
2654                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2655         }
2656     }
2657     RegCloseKey(hkey);
2658
2659     uirow = MSI_CreateRecord(3);
2660     MSI_RecordSetStringW(uirow,2,deformated);
2661     MSI_RecordSetStringW(uirow,1,uikey);
2662     if (type == REG_SZ || type == REG_EXPAND_SZ)
2663         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2664     msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2665     msiobj_release( &uirow->hdr );
2666
2667     msi_free(value_data);
2668     msi_free(deformated);
2669     msi_free(uikey);
2670     msi_free(keypath);
2671
2672     return ERROR_SUCCESS;
2673 }
2674
2675 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2676 {
2677     static const WCHAR query[] = {
2678         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2679         '`','R','e','g','i','s','t','r','y','`',0};
2680     MSIQUERY *view;
2681     UINT rc;
2682
2683     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2684     if (rc != ERROR_SUCCESS)
2685         return ERROR_SUCCESS;
2686
2687     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2688     msiobj_release(&view->hdr);
2689     return rc;
2690 }
2691
2692 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2693 {
2694     LONG res;
2695     HKEY hkey;
2696     DWORD num_subkeys, num_values;
2697
2698     if (delete_key)
2699     {
2700         if ((res = RegDeleteTreeW( hkey_root, key )))
2701         {
2702             TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2703         }
2704         return;
2705     }
2706
2707     if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2708     {
2709         if ((res = RegDeleteValueW( hkey, value )))
2710         {
2711             TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2712         }
2713         res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2714                                 NULL, NULL, NULL, NULL );
2715         RegCloseKey( hkey );
2716         if (!res && !num_subkeys && !num_values)
2717         {
2718             TRACE("Removing empty key %s\n", debugstr_w(key));
2719             RegDeleteKeyW( hkey_root, key );
2720         }
2721         return;
2722     }
2723     TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
2724 }
2725
2726
2727 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2728 {
2729     MSIPACKAGE *package = param;
2730     LPCWSTR component, name, key_str, root_key_str;
2731     LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2732     MSICOMPONENT *comp;
2733     MSIRECORD *uirow;
2734     BOOL delete_key = FALSE;
2735     HKEY hkey_root;
2736     UINT size;
2737     INT root;
2738
2739     msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2740
2741     component = MSI_RecordGetString( row, 6 );
2742     comp = msi_get_loaded_component( package, component );
2743     if (!comp)
2744         return ERROR_SUCCESS;
2745
2746     comp->Action = msi_get_component_action( package, comp );
2747     if (comp->Action != INSTALLSTATE_ABSENT)
2748     {
2749         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2750         return ERROR_SUCCESS;
2751     }
2752
2753     name = MSI_RecordGetString( row, 4 );
2754     if (MSI_RecordIsNull( row, 5 ) && name )
2755     {
2756         if (name[0] == '+' && !name[1])
2757             return ERROR_SUCCESS;
2758         else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2759         {
2760             delete_key = TRUE;
2761             name = NULL;
2762         }
2763     }
2764
2765     root = MSI_RecordGetInteger( row, 2 );
2766     key_str = MSI_RecordGetString( row, 3 );
2767
2768     root_key_str = get_root_key( package, root, &hkey_root );
2769     if (!root_key_str)
2770         return ERROR_SUCCESS;
2771
2772     deformat_string( package, key_str, &deformated_key );
2773     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2774     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2775     strcpyW( ui_key_str, root_key_str );
2776     strcatW( ui_key_str, deformated_key );
2777
2778     deformat_string( package, name, &deformated_name );
2779
2780     keypath = get_keypath( package, hkey_root, deformated_key );
2781     msi_free( deformated_key );
2782     delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2783     msi_free( keypath );
2784
2785     uirow = MSI_CreateRecord( 2 );
2786     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2787     MSI_RecordSetStringW( uirow, 2, deformated_name );
2788     msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2789     msiobj_release( &uirow->hdr );
2790
2791     msi_free( ui_key_str );
2792     msi_free( deformated_name );
2793     return ERROR_SUCCESS;
2794 }
2795
2796 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2797 {
2798     MSIPACKAGE *package = param;
2799     LPCWSTR component, name, key_str, root_key_str;
2800     LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2801     MSICOMPONENT *comp;
2802     MSIRECORD *uirow;
2803     BOOL delete_key = FALSE;
2804     HKEY hkey_root;
2805     UINT size;
2806     INT root;
2807
2808     component = MSI_RecordGetString( row, 5 );
2809     comp = msi_get_loaded_component( package, component );
2810     if (!comp)
2811         return ERROR_SUCCESS;
2812
2813     comp->Action = msi_get_component_action( package, comp );
2814     if (comp->Action != INSTALLSTATE_LOCAL)
2815     {
2816         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2817         return ERROR_SUCCESS;
2818     }
2819
2820     if ((name = MSI_RecordGetString( row, 4 )))
2821     {
2822         if (name[0] == '-' && !name[1])
2823         {
2824             delete_key = TRUE;
2825             name = NULL;
2826         }
2827     }
2828
2829     root = MSI_RecordGetInteger( row, 2 );
2830     key_str = MSI_RecordGetString( row, 3 );
2831
2832     root_key_str = get_root_key( package, root, &hkey_root );
2833     if (!root_key_str)
2834         return ERROR_SUCCESS;
2835
2836     deformat_string( package, key_str, &deformated_key );
2837     size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2838     ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2839     strcpyW( ui_key_str, root_key_str );
2840     strcatW( ui_key_str, deformated_key );
2841
2842     deformat_string( package, name, &deformated_name );
2843
2844     keypath = get_keypath( package, hkey_root, deformated_key );
2845     msi_free( deformated_key );
2846     delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2847     msi_free( keypath );
2848
2849     uirow = MSI_CreateRecord( 2 );
2850     MSI_RecordSetStringW( uirow, 1, ui_key_str );
2851     MSI_RecordSetStringW( uirow, 2, deformated_name );
2852     msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2853     msiobj_release( &uirow->hdr );
2854
2855     msi_free( ui_key_str );
2856     msi_free( deformated_name );
2857     return ERROR_SUCCESS;
2858 }
2859
2860 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2861 {
2862     static const WCHAR registry_query[] = {
2863         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2864         '`','R','e','g','i','s','t','r','y','`',0};
2865     static const WCHAR remove_registry_query[] = {
2866         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2867         '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2868     MSIQUERY *view;
2869     UINT rc;
2870
2871     rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2872     if (rc == ERROR_SUCCESS)
2873     {
2874         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2875         msiobj_release( &view->hdr );
2876         if (rc != ERROR_SUCCESS)
2877             return rc;
2878     }
2879     rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2880     if (rc == ERROR_SUCCESS)
2881     {
2882         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2883         msiobj_release( &view->hdr );
2884         if (rc != ERROR_SUCCESS)
2885             return rc;
2886     }
2887     return ERROR_SUCCESS;
2888 }
2889
2890 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2891 {
2892     package->script->CurrentlyScripting = TRUE;
2893
2894     return ERROR_SUCCESS;
2895 }
2896
2897
2898 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2899 {
2900     static const WCHAR query[]= {
2901         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2902         '`','R','e','g','i','s','t','r','y','`',0};
2903     MSICOMPONENT *comp;
2904     DWORD total = 0, count = 0;
2905     MSIQUERY *view;
2906     MSIFEATURE *feature;
2907     MSIFILE *file;
2908     UINT rc;
2909
2910     TRACE("InstallValidate\n");
2911
2912     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2913     if (rc == ERROR_SUCCESS)
2914     {
2915         rc = MSI_IterateRecords( view, &count, NULL, package );
2916         msiobj_release( &view->hdr );
2917         if (rc != ERROR_SUCCESS)
2918             return rc;
2919         total += count * REG_PROGRESS_VALUE;
2920     }
2921     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2922         total += COMPONENT_PROGRESS_VALUE;
2923
2924     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2925         total += file->FileSize;
2926
2927     msi_ui_progress( package, 0, total, 0, 0 );
2928
2929     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2930     {
2931         TRACE("Feature: %s Installed %d Request %d Action %d\n",
2932               debugstr_w(feature->Feature), feature->Installed,
2933               feature->ActionRequest, feature->Action);
2934     }
2935     return ERROR_SUCCESS;
2936 }
2937
2938 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2939 {
2940     MSIPACKAGE* package = param;
2941     LPCWSTR cond = NULL; 
2942     LPCWSTR message = NULL;
2943     UINT r;
2944
2945     static const WCHAR title[]=
2946         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2947
2948     cond = MSI_RecordGetString(row,1);
2949
2950     r = MSI_EvaluateConditionW(package,cond);
2951     if (r == MSICONDITION_FALSE)
2952     {
2953         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2954         {
2955             LPWSTR deformated;
2956             message = MSI_RecordGetString(row,2);
2957             deformat_string(package,message,&deformated);
2958             MessageBoxW(NULL,deformated,title,MB_OK);
2959             msi_free(deformated);
2960         }
2961
2962         return ERROR_INSTALL_FAILURE;
2963     }
2964
2965     return ERROR_SUCCESS;
2966 }
2967
2968 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2969 {
2970     static const WCHAR query[] = {
2971         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2972         '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2973     MSIQUERY *view;
2974     UINT rc;
2975
2976     TRACE("Checking launch conditions\n");
2977
2978     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2979     if (rc != ERROR_SUCCESS)
2980         return ERROR_SUCCESS;
2981
2982     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2983     msiobj_release(&view->hdr);
2984     return rc;
2985 }
2986
2987 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2988 {
2989
2990     if (!cmp->KeyPath)
2991         return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2992
2993     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2994     {
2995         static const WCHAR query[] = {
2996             'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2997             '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
2998             '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
2999         static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3000         static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3001         MSIRECORD *row;
3002         UINT root, len;
3003         LPWSTR deformated, buffer, deformated_name;
3004         LPCWSTR key, name;
3005
3006         row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3007         if (!row)
3008             return NULL;
3009
3010         root = MSI_RecordGetInteger(row,2);
3011         key = MSI_RecordGetString(row, 3);
3012         name = MSI_RecordGetString(row, 4);
3013         deformat_string(package, key , &deformated);
3014         deformat_string(package, name, &deformated_name);
3015
3016         len = strlenW(deformated) + 6;
3017         if (deformated_name)
3018             len+=strlenW(deformated_name);
3019
3020         buffer = msi_alloc( len *sizeof(WCHAR));
3021
3022         if (deformated_name)
3023             sprintfW(buffer,fmt2,root,deformated,deformated_name);
3024         else
3025             sprintfW(buffer,fmt,root,deformated);
3026
3027         msi_free(deformated);
3028         msi_free(deformated_name);
3029         msiobj_release(&row->hdr);
3030
3031         return buffer;
3032     }
3033     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3034     {
3035         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3036         return NULL;
3037     }
3038     else
3039     {
3040         MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3041
3042         if (file)
3043             return strdupW( file->TargetPath );
3044     }
3045     return NULL;
3046 }
3047
3048 static HKEY openSharedDLLsKey(void)
3049 {
3050     HKEY hkey=0;
3051     static const WCHAR path[] =
3052         {'S','o','f','t','w','a','r','e','\\',
3053          'M','i','c','r','o','s','o','f','t','\\',
3054          'W','i','n','d','o','w','s','\\',
3055          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3056          'S','h','a','r','e','d','D','L','L','s',0};
3057
3058     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3059     return hkey;
3060 }
3061
3062 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3063 {
3064     HKEY hkey;
3065     DWORD count=0;
3066     DWORD type;
3067     DWORD sz = sizeof(count);
3068     DWORD rc;
3069     
3070     hkey = openSharedDLLsKey();
3071     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3072     if (rc != ERROR_SUCCESS)
3073         count = 0;
3074     RegCloseKey(hkey);
3075     return count;
3076 }
3077
3078 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3079 {
3080     HKEY hkey;
3081
3082     hkey = openSharedDLLsKey();
3083     if (count > 0)
3084         msi_reg_set_val_dword( hkey, path, count );
3085     else
3086         RegDeleteValueW(hkey,path);
3087     RegCloseKey(hkey);
3088     return count;
3089 }
3090
3091 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3092 {
3093     MSIFEATURE *feature;
3094     INT count = 0;
3095     BOOL write = FALSE;
3096
3097     /* only refcount DLLs */
3098     if (comp->KeyPath == NULL || 
3099         comp->assembly ||
3100         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
3101         comp->Attributes & msidbComponentAttributesODBCDataSource)
3102         write = FALSE;
3103     else
3104     {
3105         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3106         write = (count > 0);
3107
3108         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3109             write = TRUE;
3110     }
3111
3112     /* increment counts */
3113     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3114     {
3115         ComponentList *cl;
3116
3117         if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3118             continue;
3119
3120         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3121         {
3122             if ( cl->component == comp )
3123                 count++;
3124         }
3125     }
3126
3127     /* decrement counts */
3128     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3129     {
3130         ComponentList *cl;
3131
3132         if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3133             continue;
3134
3135         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3136         {
3137             if ( cl->component == comp )
3138                 count--;
3139         }
3140     }
3141
3142     /* ref count all the files in the component */
3143     if (write)
3144     {
3145         MSIFILE *file;
3146
3147         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3148         {
3149             if (file->Component == comp)
3150                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3151         }
3152     }
3153     
3154     /* add a count for permanent */
3155     if (comp->Attributes & msidbComponentAttributesPermanent)
3156         count ++;
3157     
3158     comp->RefCount = count;
3159
3160     if (write)
3161         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3162 }
3163
3164 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3165 {
3166     if (comp->assembly)
3167     {
3168         const WCHAR prefixW[] = {'<','\\',0};
3169         DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3170         WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3171
3172         if (keypath)
3173         {
3174             strcpyW( keypath, prefixW );
3175             strcatW( keypath, comp->assembly->display_name );
3176         }
3177         return keypath;
3178     }
3179     return resolve_keypath( package, comp );
3180 }
3181
3182 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3183 {
3184     WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3185     UINT rc;
3186     MSICOMPONENT *comp;
3187     HKEY hkey;
3188
3189     TRACE("\n");
3190
3191     squash_guid(package->ProductCode,squished_pc);
3192     msi_set_sourcedir_props(package, FALSE);
3193
3194     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3195     {
3196         MSIRECORD *uirow;
3197         INSTALLSTATE action;
3198
3199         msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3200         if (!comp->ComponentId)
3201             continue;
3202
3203         squash_guid( comp->ComponentId, squished_cc );
3204         msi_free( comp->FullKeypath );
3205         comp->FullKeypath = build_full_keypath( package, comp );
3206
3207         ACTION_RefCountComponent( package, comp );
3208
3209         if (package->need_rollback) action = comp->Installed;
3210         else action = comp->ActionRequest;
3211
3212         TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3213                             debugstr_w(comp->Component), debugstr_w(squished_cc),
3214                             debugstr_w(comp->FullKeypath), comp->RefCount, action);
3215
3216         if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3217         {
3218             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3219                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3220             else
3221                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3222
3223             if (rc != ERROR_SUCCESS)
3224                 continue;
3225
3226             if (comp->Attributes & msidbComponentAttributesPermanent)
3227             {
3228                 static const WCHAR szPermKey[] =
3229                     { '0','0','0','0','0','0','0','0','0','0','0','0',
3230                       '0','0','0','0','0','0','0','0','0','0','0','0',
3231                       '0','0','0','0','0','0','0','0',0 };
3232
3233                 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3234             }
3235             if (action == INSTALLSTATE_LOCAL)
3236                 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3237             else
3238             {
3239                 MSIFILE *file;
3240                 MSIRECORD *row;
3241                 LPWSTR ptr, ptr2;
3242                 WCHAR source[MAX_PATH];
3243                 WCHAR base[MAX_PATH];
3244                 LPWSTR sourcepath;
3245
3246                 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3247                 static const WCHAR query[] = {
3248                     'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3249                     '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3250                     '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3251                     '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3252                     '`','D','i','s','k','I','d','`',0};
3253
3254                 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3255                     continue;
3256
3257                 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3258                 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3259                 ptr2 = strrchrW(source, '\\') + 1;
3260                 msiobj_release(&row->hdr);
3261
3262                 lstrcpyW(base, package->PackagePath);
3263                 ptr = strrchrW(base, '\\');
3264                 *(ptr + 1) = '\0';
3265
3266                 sourcepath = msi_resolve_file_source(package, file);
3267                 ptr = sourcepath + lstrlenW(base);
3268                 lstrcpyW(ptr2, ptr);
3269                 msi_free(sourcepath);
3270
3271                 msi_reg_set_val_str(hkey, squished_pc, source);
3272             }
3273             RegCloseKey(hkey);
3274         }
3275         else if (action == INSTALLSTATE_ABSENT)
3276         {
3277             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3278                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3279             else
3280                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3281         }
3282
3283         /* UI stuff */
3284         uirow = MSI_CreateRecord(3);
3285         MSI_RecordSetStringW(uirow,1,package->ProductCode);
3286         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3287         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3288         msi_ui_actiondata( package, szProcessComponents, uirow );
3289         msiobj_release( &uirow->hdr );
3290     }
3291     return ERROR_SUCCESS;
3292 }
3293
3294 typedef struct {
3295     CLSID       clsid;
3296     LPWSTR      source;
3297
3298     LPWSTR      path;
3299     ITypeLib    *ptLib;
3300 } typelib_struct;
3301
3302 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
3303                                        LPWSTR lpszName, LONG_PTR lParam)
3304 {
3305     TLIBATTR *attr;
3306     typelib_struct *tl_struct = (typelib_struct*) lParam;
3307     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3308     int sz; 
3309     HRESULT res;
3310
3311     if (!IS_INTRESOURCE(lpszName))
3312     {
3313         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3314         return TRUE;
3315     }
3316
3317     sz = strlenW(tl_struct->source)+4;
3318     sz *= sizeof(WCHAR);
3319
3320     if ((INT_PTR)lpszName == 1)
3321         tl_struct->path = strdupW(tl_struct->source);
3322     else
3323     {
3324         tl_struct->path = msi_alloc(sz);
3325         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3326     }
3327
3328     TRACE("trying %s\n", debugstr_w(tl_struct->path));
3329     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3330     if (FAILED(res))
3331     {
3332         msi_free(tl_struct->path);
3333         tl_struct->path = NULL;
3334
3335         return TRUE;
3336     }
3337
3338     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3339     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3340     {
3341         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3342         return FALSE;
3343     }
3344
3345     msi_free(tl_struct->path);
3346     tl_struct->path = NULL;
3347
3348     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3349     ITypeLib_Release(tl_struct->ptLib);
3350
3351     return TRUE;
3352 }
3353
3354 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3355 {
3356     MSIPACKAGE* package = param;
3357     LPCWSTR component;
3358     MSICOMPONENT *comp;
3359     MSIFILE *file;
3360     typelib_struct tl_struct;
3361     ITypeLib *tlib;
3362     HMODULE module;
3363     HRESULT hr;
3364
3365     component = MSI_RecordGetString(row,3);
3366     comp = msi_get_loaded_component(package,component);
3367     if (!comp)
3368         return ERROR_SUCCESS;
3369
3370     comp->Action = msi_get_component_action( package, comp );
3371     if (comp->Action != INSTALLSTATE_LOCAL)
3372     {
3373         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3374         return ERROR_SUCCESS;
3375     }
3376
3377     if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3378     {
3379         TRACE("component has no key path\n");
3380         return ERROR_SUCCESS;
3381     }
3382     msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3383
3384     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3385     if (module)
3386     {
3387         LPCWSTR guid;
3388         guid = MSI_RecordGetString(row,1);
3389         CLSIDFromString( guid, &tl_struct.clsid);
3390         tl_struct.source = strdupW( file->TargetPath );
3391         tl_struct.path = NULL;
3392
3393         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3394                         (LONG_PTR)&tl_struct);
3395
3396         if (tl_struct.path)
3397         {
3398             LPCWSTR helpid, help_path = NULL;
3399             HRESULT res;
3400
3401             helpid = MSI_RecordGetString(row,6);
3402
3403             if (helpid) help_path = msi_get_target_folder( package, helpid );
3404             res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3405
3406             if (FAILED(res))
3407                 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3408             else
3409                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3410
3411             ITypeLib_Release(tl_struct.ptLib);
3412             msi_free(tl_struct.path);
3413         }
3414         else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3415
3416         FreeLibrary(module);
3417         msi_free(tl_struct.source);
3418     }
3419     else
3420     {
3421         hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3422         if (FAILED(hr))
3423         {
3424             ERR("Failed to load type library: %08x\n", hr);
3425             return ERROR_INSTALL_FAILURE;
3426         }
3427
3428         ITypeLib_Release(tlib);
3429     }
3430
3431     return ERROR_SUCCESS;
3432 }
3433
3434 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3435 {
3436     static const WCHAR query[] = {
3437         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3438         '`','T','y','p','e','L','i','b','`',0};
3439     MSIQUERY *view;
3440     UINT rc;
3441
3442     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3443     if (rc != ERROR_SUCCESS)
3444         return ERROR_SUCCESS;
3445
3446     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3447     msiobj_release(&view->hdr);
3448     return rc;
3449 }
3450
3451 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3452 {
3453     MSIPACKAGE *package = param;
3454     LPCWSTR component, guid;
3455     MSICOMPONENT *comp;
3456     GUID libid;
3457     UINT version;
3458     LCID language;
3459     SYSKIND syskind;
3460     HRESULT hr;
3461
3462     component = MSI_RecordGetString( row, 3 );
3463     comp = msi_get_loaded_component( package, component );
3464     if (!comp)
3465         return ERROR_SUCCESS;
3466
3467     comp->Action = msi_get_component_action( package, comp );
3468     if (comp->Action != INSTALLSTATE_ABSENT)
3469     {
3470         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3471         return ERROR_SUCCESS;
3472     }
3473     msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3474
3475     guid = MSI_RecordGetString( row, 1 );
3476     CLSIDFromString( guid, &libid );
3477     version = MSI_RecordGetInteger( row, 4 );
3478     language = MSI_RecordGetInteger( row, 2 );
3479
3480 #ifdef _WIN64
3481     syskind = SYS_WIN64;
3482 #else
3483     syskind = SYS_WIN32;
3484 #endif
3485
3486     hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3487     if (FAILED(hr))
3488     {
3489         WARN("Failed to unregister typelib: %08x\n", hr);
3490     }
3491
3492     return ERROR_SUCCESS;
3493 }
3494
3495 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3496 {
3497     static const WCHAR query[] = {
3498         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3499         '`','T','y','p','e','L','i','b','`',0};
3500     MSIQUERY *view;
3501     UINT rc;
3502
3503     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3504     if (rc != ERROR_SUCCESS)
3505         return ERROR_SUCCESS;
3506
3507     rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3508     msiobj_release( &view->hdr );
3509     return rc;
3510 }
3511
3512 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3513 {
3514     static const WCHAR szlnk[] = {'.','l','n','k',0};
3515     LPCWSTR directory, extension, link_folder;
3516     LPWSTR link_file, filename;
3517
3518     directory = MSI_RecordGetString( row, 2 );
3519     link_folder = msi_get_target_folder( package, directory );
3520     if (!link_folder)
3521     {
3522         /* some installers use a separate root */
3523         MSIFOLDER *folder = msi_get_loaded_folder( package, directory );
3524         while (folder->Parent && strcmpW( folder->Parent, folder->Directory ))
3525         {
3526             folder = msi_get_loaded_folder( package, folder->Parent );
3527         }
3528         msi_resolve_target_folder( package, folder->Directory, TRUE );
3529         link_folder = msi_get_target_folder( package, directory );
3530     }
3531     /* may be needed because of a bug somewhere else */
3532     msi_create_full_path( link_folder );
3533
3534     filename = msi_dup_record_field( row, 3 );
3535     msi_reduce_to_long_filename( filename );
3536
3537     extension = strchrW( filename, '.' );
3538     if (!extension || strcmpiW( extension, szlnk ))
3539     {
3540         int len = strlenW( filename );
3541         filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3542         memcpy( filename + len, szlnk, sizeof(szlnk) );
3543     }
3544     link_file = msi_build_directory_name( 2, link_folder, filename );
3545     msi_free( filename );
3546
3547     return link_file;
3548 }
3549
3550 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3551 {
3552     static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3553     static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3554     WCHAR *folder, *dest, *path;
3555
3556     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3557         folder = msi_dup_property( package->db, szWindowsFolder );
3558     else
3559     {
3560         WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3561         folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3562         msi_free( appdata );
3563     }
3564     dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3565     msi_create_full_path( dest );
3566     path = msi_build_directory_name( 2, dest, icon_name );
3567     msi_free( folder );
3568     msi_free( dest );
3569     return path;
3570 }
3571
3572 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3573 {
3574     MSIPACKAGE *package = param;
3575     LPWSTR link_file, deformated, path;
3576     LPCWSTR component, target;
3577     MSICOMPONENT *comp;
3578     IShellLinkW *sl = NULL;
3579     IPersistFile *pf = NULL;
3580     HRESULT res;
3581
3582     component = MSI_RecordGetString(row, 4);
3583     comp = msi_get_loaded_component(package, component);
3584     if (!comp)
3585         return ERROR_SUCCESS;
3586
3587     comp->Action = msi_get_component_action( package, comp );
3588     if (comp->Action != INSTALLSTATE_LOCAL)
3589     {
3590         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3591         return ERROR_SUCCESS;
3592     }
3593     msi_ui_actiondata( package, szCreateShortcuts, row );
3594
3595     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3596                     &IID_IShellLinkW, (LPVOID *) &sl );
3597
3598     if (FAILED( res ))
3599     {
3600         ERR("CLSID_ShellLink not available\n");
3601         goto err;
3602     }
3603
3604     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3605     if (FAILED( res ))
3606     {
3607         ERR("QueryInterface(IID_IPersistFile) failed\n");
3608         goto err;
3609     }
3610
3611     target = MSI_RecordGetString(row, 5);
3612     if (strchrW(target, '['))
3613     {
3614         int len;
3615         WCHAR *format_string, *p;
3616
3617         if (!(p = strchrW( target, ']' ))) goto err;
3618         len = p - target + 1;
3619         format_string = msi_alloc( (len + 1) * sizeof(WCHAR) );
3620         memcpy( format_string, target, len * sizeof(WCHAR) );
3621         format_string[len] = 0;
3622         deformat_string( package, format_string, &deformated );
3623         msi_free( format_string );
3624
3625         path = msi_alloc( (strlenW( deformated ) + strlenW( p + 1 ) + 2) * sizeof(WCHAR) );
3626         strcpyW( path, deformated );
3627         PathAddBackslashW( path );
3628         strcatW( path, p + 1 );
3629         TRACE("target path is %s\n", debugstr_w(path));
3630
3631         IShellLinkW_SetPath( sl, path );
3632         msi_free( deformated );
3633         msi_free( path );
3634     }
3635     else
3636     {
3637         FIXME("poorly handled shortcut format, advertised shortcut\n");
3638         IShellLinkW_SetPath(sl,comp->FullKeypath);
3639     }
3640
3641     if (!MSI_RecordIsNull(row,6))
3642     {
3643         LPCWSTR arguments = MSI_RecordGetString(row, 6);
3644         deformat_string(package, arguments, &deformated);
3645         IShellLinkW_SetArguments(sl,deformated);
3646         msi_free(deformated);
3647     }
3648
3649     if (!MSI_RecordIsNull(row,7))
3650     {
3651         LPCWSTR description = MSI_RecordGetString(row, 7);
3652         IShellLinkW_SetDescription(sl, description);
3653     }
3654
3655     if (!MSI_RecordIsNull(row,8))
3656         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3657
3658     if (!MSI_RecordIsNull(row,9))
3659     {
3660         INT index; 
3661         LPCWSTR icon = MSI_RecordGetString(row, 9);
3662
3663         path = msi_build_icon_path(package, icon);
3664         index = MSI_RecordGetInteger(row,10);
3665
3666         /* no value means 0 */
3667         if (index == MSI_NULL_INTEGER)
3668             index = 0;
3669
3670         IShellLinkW_SetIconLocation(sl, path, index);
3671         msi_free(path);
3672     }
3673
3674     if (!MSI_RecordIsNull(row,11))
3675         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3676
3677     if (!MSI_RecordIsNull(row,12))
3678     {
3679         LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3680         full_path = msi_get_target_folder( package, wkdir );
3681         if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3682     }
3683     link_file = get_link_file(package, row);
3684
3685     TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3686     IPersistFile_Save(pf, link_file, FALSE);
3687     msi_free(link_file);
3688
3689 err:
3690     if (pf)
3691         IPersistFile_Release( pf );
3692     if (sl)
3693         IShellLinkW_Release( sl );
3694
3695     return ERROR_SUCCESS;
3696 }
3697
3698 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3699 {
3700     static const WCHAR query[] = {
3701         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3702         '`','S','h','o','r','t','c','u','t','`',0};
3703     MSIQUERY *view;
3704     HRESULT res;
3705     UINT rc;
3706
3707     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3708     if (rc != ERROR_SUCCESS)
3709         return ERROR_SUCCESS;
3710
3711     res = CoInitialize( NULL );
3712
3713     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3714     msiobj_release(&view->hdr);
3715
3716     if (SUCCEEDED(res)) CoUninitialize();
3717     return rc;
3718 }
3719
3720 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3721 {
3722     MSIPACKAGE *package = param;
3723     LPWSTR link_file;
3724     LPCWSTR component;
3725     MSICOMPONENT *comp;
3726
3727     component = MSI_RecordGetString( row, 4 );
3728     comp = msi_get_loaded_component( package, component );
3729     if (!comp)
3730         return ERROR_SUCCESS;
3731
3732     comp->Action = msi_get_component_action( package, comp );
3733     if (comp->Action != INSTALLSTATE_ABSENT)
3734     {
3735         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3736         return ERROR_SUCCESS;
3737     }
3738     msi_ui_actiondata( package, szRemoveShortcuts, row );
3739
3740     link_file = get_link_file( package, row );
3741
3742     TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3743     if (!DeleteFileW( link_file ))
3744     {
3745         WARN("Failed to remove shortcut file %u\n", GetLastError());
3746     }
3747     msi_free( link_file );
3748
3749     return ERROR_SUCCESS;
3750 }
3751
3752 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3753 {
3754     static const WCHAR query[] = {
3755         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3756         '`','S','h','o','r','t','c','u','t','`',0};
3757     MSIQUERY *view;
3758     UINT rc;
3759
3760     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3761     if (rc != ERROR_SUCCESS)
3762         return ERROR_SUCCESS;
3763
3764     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3765     msiobj_release( &view->hdr );
3766     return rc;
3767 }
3768
3769 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3770 {
3771     MSIPACKAGE* package = param;
3772     HANDLE the_file;
3773     LPWSTR FilePath;
3774     LPCWSTR FileName;
3775     CHAR buffer[1024];
3776     DWORD sz;
3777     UINT rc;
3778
3779     FileName = MSI_RecordGetString(row,1);
3780     if (!FileName)
3781     {
3782         ERR("Unable to get FileName\n");
3783         return ERROR_SUCCESS;
3784     }
3785
3786     FilePath = msi_build_icon_path(package, FileName);
3787
3788     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3789
3790     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3791                         FILE_ATTRIBUTE_NORMAL, NULL);
3792
3793     if (the_file == INVALID_HANDLE_VALUE)
3794     {
3795         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3796         msi_free(FilePath);
3797         return ERROR_SUCCESS;
3798     }
3799
3800     do 
3801     {
3802         DWORD write;
3803         sz = 1024;
3804         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3805         if (rc != ERROR_SUCCESS)
3806         {
3807             ERR("Failed to get stream\n");
3808             CloseHandle(the_file);  
3809             DeleteFileW(FilePath);
3810             break;
3811         }
3812         WriteFile(the_file,buffer,sz,&write,NULL);
3813     } while (sz == 1024);
3814
3815     msi_free(FilePath);
3816     CloseHandle(the_file);
3817
3818     return ERROR_SUCCESS;
3819 }
3820
3821 static UINT msi_publish_icons(MSIPACKAGE *package)
3822 {
3823     static const WCHAR query[]= {
3824         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3825         '`','I','c','o','n','`',0};
3826     MSIQUERY *view;
3827     UINT r;
3828
3829     r = MSI_DatabaseOpenViewW(package->db, query, &view);
3830     if (r == ERROR_SUCCESS)
3831     {
3832         r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3833         msiobj_release(&view->hdr);
3834         if (r != ERROR_SUCCESS)
3835             return r;
3836     }
3837     return ERROR_SUCCESS;
3838 }
3839
3840 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3841 {
3842     UINT r;
3843     HKEY source;
3844     LPWSTR buffer;
3845     MSIMEDIADISK *disk;
3846     MSISOURCELISTINFO *info;
3847
3848     r = RegCreateKeyW(hkey, szSourceList, &source);
3849     if (r != ERROR_SUCCESS)
3850         return r;
3851
3852     RegCloseKey(source);
3853
3854     buffer = strrchrW(package->PackagePath, '\\') + 1;
3855     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3856                               package->Context, MSICODE_PRODUCT,
3857                               INSTALLPROPERTY_PACKAGENAMEW, buffer);
3858     if (r != ERROR_SUCCESS)
3859         return r;
3860
3861     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3862                               package->Context, MSICODE_PRODUCT,
3863                               INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3864     if (r != ERROR_SUCCESS)
3865         return r;
3866
3867     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3868                               package->Context, MSICODE_PRODUCT,
3869                               INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3870     if (r != ERROR_SUCCESS)
3871         return r;
3872
3873     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3874     {
3875         if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3876             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3877                                      info->options, info->value);
3878         else
3879             MsiSourceListSetInfoW(package->ProductCode, NULL,
3880                                   info->context, info->options,
3881                                   info->property, info->value);
3882     }
3883
3884     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3885     {
3886         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3887                                    disk->context, disk->options,
3888                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3889     }
3890
3891     return ERROR_SUCCESS;
3892 }
3893
3894 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3895 {
3896     MSIHANDLE hdb, suminfo;
3897     WCHAR guids[MAX_PATH];
3898     WCHAR packcode[SQUISH_GUID_SIZE];
3899     LPWSTR buffer;
3900     LPWSTR ptr;
3901     DWORD langid;
3902     DWORD size;
3903     UINT r;
3904
3905     static const WCHAR szARPProductIcon[] =
3906         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3907     static const WCHAR szAssignment[] =
3908         {'A','s','s','i','g','n','m','e','n','t',0};
3909     static const WCHAR szAdvertiseFlags[] =
3910         {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3911     static const WCHAR szClients[] =
3912         {'C','l','i','e','n','t','s',0};
3913     static const WCHAR szColon[] = {':',0};
3914
3915     buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3916     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3917     msi_free(buffer);
3918
3919     langid = msi_get_property_int(package->db, szProductLanguage, 0);
3920     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3921
3922     /* FIXME */
3923     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3924
3925     buffer = msi_dup_property(package->db, szARPProductIcon);
3926     if (buffer)
3927     {
3928         LPWSTR path = msi_build_icon_path(package, buffer);
3929         msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3930         msi_free(path);
3931         msi_free(buffer);
3932     }
3933
3934     buffer = msi_dup_property(package->db, szProductVersion);
3935     if (buffer)
3936     {
3937         DWORD verdword = msi_version_str_to_dword(buffer);
3938         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3939         msi_free(buffer);
3940     }
3941
3942     msi_reg_set_val_dword(hkey, szAssignment, 0);
3943     msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3944     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3945     msi_reg_set_val_str(hkey, szClients, szColon);
3946
3947     hdb = alloc_msihandle(&package->db->hdr);
3948     if (!hdb)
3949         return ERROR_NOT_ENOUGH_MEMORY;
3950
3951     r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3952     MsiCloseHandle(hdb);
3953     if (r != ERROR_SUCCESS)
3954         goto done;
3955
3956     size = MAX_PATH;
3957     r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3958                                    NULL, guids, &size);
3959     if (r != ERROR_SUCCESS)
3960         goto done;
3961
3962     ptr = strchrW(guids, ';');
3963     if (ptr) *ptr = 0;
3964     squash_guid(guids, packcode);
3965     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3966
3967 done:
3968     MsiCloseHandle(suminfo);
3969     return ERROR_SUCCESS;
3970 }
3971
3972 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3973 {
3974     UINT r;
3975     HKEY hkey;
3976     LPWSTR upgrade;
3977     WCHAR squashed_pc[SQUISH_GUID_SIZE];
3978
3979     upgrade = msi_dup_property(package->db, szUpgradeCode);
3980     if (!upgrade)
3981         return ERROR_SUCCESS;
3982
3983     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3984         r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3985     else
3986         r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3987
3988     if (r != ERROR_SUCCESS)
3989     {
3990         WARN("failed to open upgrade code key\n");
3991         msi_free(upgrade);
3992         return ERROR_SUCCESS;
3993     }
3994     squash_guid(package->ProductCode, squashed_pc);
3995     msi_reg_set_val_str(hkey, squashed_pc, NULL);
3996     RegCloseKey(hkey);
3997     msi_free(upgrade);
3998     return ERROR_SUCCESS;
3999 }
4000
4001 static BOOL msi_check_publish(MSIPACKAGE *package)
4002 {
4003     MSIFEATURE *feature;
4004
4005     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4006     {
4007         feature->Action = msi_get_feature_action( package, feature );
4008         if (feature->Action == INSTALLSTATE_LOCAL)
4009             return TRUE;
4010     }
4011
4012     return FALSE;
4013 }
4014
4015 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4016 {
4017     MSIFEATURE *feature;
4018
4019     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4020     {
4021         feature->Action = msi_get_feature_action( package, feature );
4022         if (feature->Action != INSTALLSTATE_ABSENT)
4023             return FALSE;
4024     }
4025
4026     return TRUE;
4027 }
4028
4029 static UINT msi_publish_patches( MSIPACKAGE *package )
4030 {
4031     static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4032     WCHAR patch_squashed[GUID_SIZE];
4033     HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4034     LONG res;
4035     MSIPATCHINFO *patch;
4036     UINT r;
4037     WCHAR *p, *all_patches = NULL;
4038     DWORD len = 0;
4039
4040     r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4041     if (r != ERROR_SUCCESS)
4042         return ERROR_FUNCTION_FAILED;
4043
4044     res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4045     if (res != ERROR_SUCCESS)
4046     {
4047         r = ERROR_FUNCTION_FAILED;
4048         goto done;
4049     }
4050
4051     r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4052     if (r != ERROR_SUCCESS)
4053         goto done;
4054
4055     LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4056     {
4057         squash_guid( patch->patchcode, patch_squashed );
4058         len += strlenW( patch_squashed ) + 1;
4059     }
4060
4061     p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4062     if (!all_patches)
4063         goto done;
4064
4065     LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4066     {
4067         HKEY patch_key;
4068
4069         squash_guid( patch->patchcode, p );
4070         p += strlenW( p ) + 1;
4071
4072         res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4073                               (const BYTE *)patch->transforms,
4074                               (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4075         if (res != ERROR_SUCCESS)
4076             goto done;
4077
4078         r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4079         if (r != ERROR_SUCCESS)
4080             goto done;
4081
4082         res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4083                               (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4084         RegCloseKey( patch_key );
4085         if (res != ERROR_SUCCESS)
4086             goto done;
4087
4088         if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4089         {
4090             res = GetLastError();
4091             ERR("Unable to copy patch package %d\n", res);
4092             goto done;
4093         }
4094         res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4095         if (res != ERROR_SUCCESS)
4096             goto done;
4097
4098         res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4099         RegCloseKey( patch_key );
4100         if (res != ERROR_SUCCESS)
4101             goto done;
4102     }
4103
4104     all_patches[len] = 0;
4105     res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4106                           (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4107     if (res != ERROR_SUCCESS)
4108         goto done;
4109
4110     res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4111                           (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4112     if (res != ERROR_SUCCESS)
4113         r = ERROR_FUNCTION_FAILED;
4114
4115 done:
4116     RegCloseKey( product_patches_key );
4117     RegCloseKey( patches_key );
4118     RegCloseKey( product_key );
4119     msi_free( all_patches );
4120     return r;
4121 }
4122
4123 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4124 {
4125     UINT rc;
4126     HKEY hukey = NULL, hudkey = NULL;
4127     MSIRECORD *uirow;
4128
4129     if (!list_empty(&package->patches))
4130     {
4131         rc = msi_publish_patches(package);
4132         if (rc != ERROR_SUCCESS)
4133             goto end;
4134     }
4135
4136     /* FIXME: also need to publish if the product is in advertise mode */
4137     if (!msi_check_publish(package))
4138         return ERROR_SUCCESS;
4139
4140     rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4141                                &hukey, TRUE);
4142     if (rc != ERROR_SUCCESS)
4143         goto end;
4144
4145     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4146                                        NULL, &hudkey, TRUE);
4147     if (rc != ERROR_SUCCESS)
4148         goto end;
4149
4150     rc = msi_publish_upgrade_code(package);
4151     if (rc != ERROR_SUCCESS)
4152         goto end;
4153
4154     rc = msi_publish_product_properties(package, hukey);
4155     if (rc != ERROR_SUCCESS)
4156         goto end;
4157
4158     rc = msi_publish_sourcelist(package, hukey);
4159     if (rc != ERROR_SUCCESS)
4160         goto end;
4161
4162     rc = msi_publish_icons(package);
4163
4164 end:
4165     uirow = MSI_CreateRecord( 1 );
4166     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4167     msi_ui_actiondata( package, szPublishProduct, uirow );
4168     msiobj_release( &uirow->hdr );
4169
4170     RegCloseKey(hukey);
4171     RegCloseKey(hudkey);
4172     return rc;
4173 }
4174
4175 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4176 {
4177     WCHAR *filename, *ptr, *folder, *ret;
4178     const WCHAR *dirprop;
4179
4180     filename = msi_dup_record_field( row, 2 );
4181     if (filename && (ptr = strchrW( filename, '|' )))
4182         ptr++;
4183     else
4184         ptr = filename;
4185
4186     dirprop = MSI_RecordGetString( row, 3 );
4187     if (dirprop)
4188     {
4189         folder = strdupW( msi_get_target_folder( package, dirprop ) );
4190         if (!folder) folder = msi_dup_property( package->db, dirprop );
4191     }
4192     else
4193         folder = msi_dup_property( package->db, szWindowsFolder );
4194
4195     if (!folder)
4196     {
4197         ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4198         msi_free( filename );
4199         return NULL;
4200     }
4201
4202     ret = msi_build_directory_name( 2, folder, ptr );
4203
4204     msi_free( filename );
4205     msi_free( folder );
4206     return ret;
4207 }
4208
4209 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4210 {
4211     MSIPACKAGE *package = param;
4212     LPCWSTR component, section, key, value, identifier;
4213     LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4214     MSIRECORD * uirow;
4215     INT action;
4216     MSICOMPONENT *comp;
4217
4218     component = MSI_RecordGetString(row, 8);
4219     comp = msi_get_loaded_component(package,component);
4220     if (!comp)
4221         return ERROR_SUCCESS;
4222
4223     comp->Action = msi_get_component_action( package, comp );
4224     if (comp->Action != INSTALLSTATE_LOCAL)
4225     {
4226         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4227         return ERROR_SUCCESS;
4228     }
4229
4230     identifier = MSI_RecordGetString(row,1); 
4231     section = MSI_RecordGetString(row,4);
4232     key = MSI_RecordGetString(row,5);
4233     value = MSI_RecordGetString(row,6);
4234     action = MSI_RecordGetInteger(row,7);
4235
4236     deformat_string(package,section,&deformated_section);
4237     deformat_string(package,key,&deformated_key);
4238     deformat_string(package,value,&deformated_value);
4239
4240     fullname = get_ini_file_name(package, row);
4241
4242     if (action == 0)
4243     {
4244         TRACE("Adding value %s to section %s in %s\n",
4245                 debugstr_w(deformated_key), debugstr_w(deformated_section),
4246                 debugstr_w(fullname));
4247         WritePrivateProfileStringW(deformated_section, deformated_key,
4248                                    deformated_value, fullname);
4249     }
4250     else if (action == 1)
4251     {
4252         WCHAR returned[10];
4253         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4254                                  returned, 10, fullname);
4255         if (returned[0] == 0)
4256         {
4257             TRACE("Adding value %s to section %s in %s\n",
4258                     debugstr_w(deformated_key), debugstr_w(deformated_section),
4259                     debugstr_w(fullname));
4260
4261             WritePrivateProfileStringW(deformated_section, deformated_key,
4262                                        deformated_value, fullname);
4263         }
4264     }
4265     else if (action == 3)
4266         FIXME("Append to existing section not yet implemented\n");
4267
4268     uirow = MSI_CreateRecord(4);
4269     MSI_RecordSetStringW(uirow,1,identifier);
4270     MSI_RecordSetStringW(uirow,2,deformated_section);
4271     MSI_RecordSetStringW(uirow,3,deformated_key);
4272     MSI_RecordSetStringW(uirow,4,deformated_value);
4273     msi_ui_actiondata( package, szWriteIniValues, uirow );
4274     msiobj_release( &uirow->hdr );
4275
4276     msi_free(fullname);
4277     msi_free(deformated_key);
4278     msi_free(deformated_value);
4279     msi_free(deformated_section);
4280     return ERROR_SUCCESS;
4281 }
4282
4283 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4284 {
4285     static const WCHAR query[] = {
4286         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4287         '`','I','n','i','F','i','l','e','`',0};
4288     MSIQUERY *view;
4289     UINT rc;
4290
4291     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4292     if (rc != ERROR_SUCCESS)
4293         return ERROR_SUCCESS;
4294
4295     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4296     msiobj_release(&view->hdr);
4297     return rc;
4298 }
4299
4300 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4301 {
4302     MSIPACKAGE *package = param;
4303     LPCWSTR component, section, key, value, identifier;
4304     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4305     MSICOMPONENT *comp;
4306     MSIRECORD *uirow;
4307     INT action;
4308
4309     component = MSI_RecordGetString( row, 8 );
4310     comp = msi_get_loaded_component( package, component );
4311     if (!comp)
4312         return ERROR_SUCCESS;
4313
4314     comp->Action = msi_get_component_action( package, comp );
4315     if (comp->Action != INSTALLSTATE_ABSENT)
4316     {
4317         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4318         return ERROR_SUCCESS;
4319     }
4320
4321     identifier = MSI_RecordGetString( row, 1 );
4322     section = MSI_RecordGetString( row, 4 );
4323     key = MSI_RecordGetString( row, 5 );
4324     value = MSI_RecordGetString( row, 6 );
4325     action = MSI_RecordGetInteger( row, 7 );
4326
4327     deformat_string( package, section, &deformated_section );
4328     deformat_string( package, key, &deformated_key );
4329     deformat_string( package, value, &deformated_value );
4330
4331     if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4332     {
4333         filename = get_ini_file_name( package, row );
4334
4335         TRACE("Removing key %s from section %s in %s\n",
4336                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4337
4338         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4339         {
4340             WARN("Unable to remove key %u\n", GetLastError());
4341         }
4342         msi_free( filename );
4343     }
4344     else
4345         FIXME("Unsupported action %d\n", action);
4346
4347
4348     uirow = MSI_CreateRecord( 4 );
4349     MSI_RecordSetStringW( uirow, 1, identifier );
4350     MSI_RecordSetStringW( uirow, 2, deformated_section );
4351     MSI_RecordSetStringW( uirow, 3, deformated_key );
4352     MSI_RecordSetStringW( uirow, 4, deformated_value );
4353     msi_ui_actiondata( package, szRemoveIniValues, uirow );
4354     msiobj_release( &uirow->hdr );
4355
4356     msi_free( deformated_key );
4357     msi_free( deformated_value );
4358     msi_free( deformated_section );
4359     return ERROR_SUCCESS;
4360 }
4361
4362 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4363 {
4364     MSIPACKAGE *package = param;
4365     LPCWSTR component, section, key, value, identifier;
4366     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4367     MSICOMPONENT *comp;
4368     MSIRECORD *uirow;
4369     INT action;
4370
4371     component = MSI_RecordGetString( row, 8 );
4372     comp = msi_get_loaded_component( package, component );
4373     if (!comp)
4374         return ERROR_SUCCESS;
4375
4376     comp->Action = msi_get_component_action( package, comp );
4377     if (comp->Action != INSTALLSTATE_LOCAL)
4378     {
4379         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4380         return ERROR_SUCCESS;
4381     }
4382
4383     identifier = MSI_RecordGetString( row, 1 );
4384     section = MSI_RecordGetString( row, 4 );
4385     key = MSI_RecordGetString( row, 5 );
4386     value = MSI_RecordGetString( row, 6 );
4387     action = MSI_RecordGetInteger( row, 7 );
4388
4389     deformat_string( package, section, &deformated_section );
4390     deformat_string( package, key, &deformated_key );
4391     deformat_string( package, value, &deformated_value );
4392
4393     if (action == msidbIniFileActionRemoveLine)
4394     {
4395         filename = get_ini_file_name( package, row );
4396
4397         TRACE("Removing key %s from section %s in %s\n",
4398                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4399
4400         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4401         {
4402             WARN("Unable to remove key %u\n", GetLastError());
4403         }
4404         msi_free( filename );
4405     }
4406     else
4407         FIXME("Unsupported action %d\n", action);
4408
4409     uirow = MSI_CreateRecord( 4 );
4410     MSI_RecordSetStringW( uirow, 1, identifier );
4411     MSI_RecordSetStringW( uirow, 2, deformated_section );
4412     MSI_RecordSetStringW( uirow, 3, deformated_key );
4413     MSI_RecordSetStringW( uirow, 4, deformated_value );
4414     msi_ui_actiondata( package, szRemoveIniValues, uirow );
4415     msiobj_release( &uirow->hdr );
4416
4417     msi_free( deformated_key );
4418     msi_free( deformated_value );
4419     msi_free( deformated_section );
4420     return ERROR_SUCCESS;
4421 }
4422
4423 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4424 {
4425     static const WCHAR query[] = {
4426         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4427         '`','I','n','i','F','i','l','e','`',0};
4428     static const WCHAR remove_query[] = {
4429         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4430         '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4431     MSIQUERY *view;
4432     UINT rc;
4433
4434     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4435     if (rc == ERROR_SUCCESS)
4436     {
4437         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4438         msiobj_release( &view->hdr );
4439         if (rc != ERROR_SUCCESS)
4440             return rc;
4441     }
4442     rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4443     if (rc == ERROR_SUCCESS)
4444     {
4445         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4446         msiobj_release( &view->hdr );
4447         if (rc != ERROR_SUCCESS)
4448             return rc;
4449     }
4450     return ERROR_SUCCESS;
4451 }
4452
4453 static void register_dll( const WCHAR *dll, BOOL unregister )
4454 {
4455     HMODULE hmod;
4456
4457     hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4458     if (hmod)
4459     {
4460         HRESULT (WINAPI *func_ptr)( void );
4461         const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4462
4463         func_ptr = (void *)GetProcAddress( hmod, func );
4464         if (func_ptr)
4465         {
4466             HRESULT hr = func_ptr();
4467             if (FAILED( hr ))
4468                 WARN("failed to register dll 0x%08x\n", hr);
4469         }
4470         else
4471             WARN("entry point %s not found\n", func);
4472         FreeLibrary( hmod );
4473         return;
4474     }
4475     WARN("failed to load library %u\n", GetLastError());
4476 }
4477
4478 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4479 {
4480     MSIPACKAGE *package = param;
4481     LPCWSTR filename;
4482     MSIFILE *file;
4483     MSIRECORD *uirow;
4484
4485     filename = MSI_RecordGetString(row,1);
4486     file = msi_get_loaded_file( package, filename );
4487     if (!file)
4488     {
4489         WARN("unable to find file %s\n", debugstr_w(filename));
4490         return ERROR_SUCCESS;
4491     }
4492     file->Component->Action = msi_get_component_action( package, file->Component );
4493     if (file->Component->Action != INSTALLSTATE_LOCAL)
4494     {
4495         TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4496         return ERROR_SUCCESS;
4497     }
4498
4499     TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4500     register_dll( file->TargetPath, FALSE );
4501
4502     uirow = MSI_CreateRecord( 2 );
4503     MSI_RecordSetStringW( uirow, 1, filename );
4504     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4505     msi_ui_actiondata( package, szSelfRegModules, uirow );
4506     msiobj_release( &uirow->hdr );
4507
4508     return ERROR_SUCCESS;
4509 }
4510
4511 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4512 {
4513     static const WCHAR query[] = {
4514         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4515         '`','S','e','l','f','R','e','g','`',0};
4516     MSIQUERY *view;
4517     UINT rc;
4518
4519     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4520     if (rc != ERROR_SUCCESS)
4521         return ERROR_SUCCESS;
4522
4523     rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4524     msiobj_release(&view->hdr);
4525     return rc;
4526 }
4527
4528 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4529 {
4530     MSIPACKAGE *package = param;
4531     LPCWSTR filename;
4532     MSIFILE *file;
4533     MSIRECORD *uirow;
4534
4535     filename = MSI_RecordGetString( row, 1 );
4536     file = msi_get_loaded_file( package, filename );
4537     if (!file)
4538     {
4539         WARN("unable to find file %s\n", debugstr_w(filename));
4540         return ERROR_SUCCESS;
4541     }
4542     file->Component->Action = msi_get_component_action( package, file->Component );
4543     if (file->Component->Action != INSTALLSTATE_ABSENT)
4544     {
4545         TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4546         return ERROR_SUCCESS;
4547     }
4548
4549     TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4550     register_dll( file->TargetPath, TRUE );
4551
4552     uirow = MSI_CreateRecord( 2 );
4553     MSI_RecordSetStringW( uirow, 1, filename );
4554     MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4555     msi_ui_actiondata( package, szSelfUnregModules, uirow );
4556     msiobj_release( &uirow->hdr );
4557
4558     return ERROR_SUCCESS;
4559 }
4560
4561 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4562 {
4563     static const WCHAR query[] = {
4564         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4565         '`','S','e','l','f','R','e','g','`',0};
4566     MSIQUERY *view;
4567     UINT rc;
4568
4569     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4570     if (rc != ERROR_SUCCESS)
4571         return ERROR_SUCCESS;
4572
4573     rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4574     msiobj_release( &view->hdr );
4575     return rc;
4576 }
4577
4578 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4579 {
4580     MSIFEATURE *feature;
4581     UINT rc;
4582     HKEY hkey = NULL, userdata = NULL;
4583
4584     if (!msi_check_publish(package))
4585         return ERROR_SUCCESS;
4586
4587     rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4588                                 &hkey, TRUE);
4589     if (rc != ERROR_SUCCESS)
4590         goto end;
4591
4592     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4593                                         &userdata, TRUE);
4594     if (rc != ERROR_SUCCESS)
4595         goto end;
4596
4597     /* here the guids are base 85 encoded */
4598     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4599     {
4600         ComponentList *cl;
4601         LPWSTR data = NULL;
4602         GUID clsid;
4603         INT size;
4604         BOOL absent = FALSE;
4605         MSIRECORD *uirow;
4606
4607         if (feature->Action != INSTALLSTATE_LOCAL &&
4608             feature->Action != INSTALLSTATE_SOURCE &&
4609             feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4610
4611         size = 1;
4612         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4613         {
4614             size += 21;
4615         }
4616         if (feature->Feature_Parent)
4617             size += strlenW( feature->Feature_Parent )+2;
4618
4619         data = msi_alloc(size * sizeof(WCHAR));
4620
4621         data[0] = 0;
4622         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4623         {
4624             MSICOMPONENT* component = cl->component;
4625             WCHAR buf[21];
4626
4627             buf[0] = 0;
4628             if (component->ComponentId)
4629             {
4630                 TRACE("From %s\n",debugstr_w(component->ComponentId));
4631                 CLSIDFromString(component->ComponentId, &clsid);
4632                 encode_base85_guid(&clsid,buf);
4633                 TRACE("to %s\n",debugstr_w(buf));
4634                 strcatW(data,buf);
4635             }
4636         }
4637
4638         if (feature->Feature_Parent)
4639         {
4640             static const WCHAR sep[] = {'\2',0};
4641             strcatW(data,sep);
4642             strcatW(data,feature->Feature_Parent);
4643         }
4644
4645         msi_reg_set_val_str( userdata, feature->Feature, data );
4646         msi_free(data);
4647
4648         size = 0;
4649         if (feature->Feature_Parent)
4650             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4651         if (!absent)
4652         {
4653             size += sizeof(WCHAR);
4654             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4655                            (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4656         }
4657         else
4658         {
4659             size += 2*sizeof(WCHAR);
4660             data = msi_alloc(size);
4661             data[0] = 0x6;
4662             data[1] = 0;
4663             if (feature->Feature_Parent)
4664                 strcpyW( &data[1], feature->Feature_Parent );
4665             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4666                        (LPBYTE)data,size);
4667             msi_free(data);
4668         }
4669
4670         /* the UI chunk */
4671         uirow = MSI_CreateRecord( 1 );
4672         MSI_RecordSetStringW( uirow, 1, feature->Feature );
4673         msi_ui_actiondata( package, szPublishFeatures, uirow );
4674         msiobj_release( &uirow->hdr );
4675         /* FIXME: call msi_ui_progress? */
4676     }
4677
4678 end:
4679     RegCloseKey(hkey);
4680     RegCloseKey(userdata);
4681     return rc;
4682 }
4683
4684 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4685 {
4686     UINT r;
4687     HKEY hkey;
4688     MSIRECORD *uirow;
4689
4690     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4691
4692     r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4693                                &hkey, FALSE);
4694     if (r == ERROR_SUCCESS)
4695     {
4696         RegDeleteValueW(hkey, feature->Feature);
4697         RegCloseKey(hkey);
4698     }
4699
4700     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4701                                        &hkey, FALSE);
4702     if (r == ERROR_SUCCESS)
4703     {
4704         RegDeleteValueW(hkey, feature->Feature);
4705         RegCloseKey(hkey);
4706     }
4707
4708     uirow = MSI_CreateRecord( 1 );
4709     MSI_RecordSetStringW( uirow, 1, feature->Feature );
4710     msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4711     msiobj_release( &uirow->hdr );
4712
4713     return ERROR_SUCCESS;
4714 }
4715
4716 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4717 {
4718     MSIFEATURE *feature;
4719
4720     if (!msi_check_unpublish(package))
4721         return ERROR_SUCCESS;
4722
4723     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4724     {
4725         msi_unpublish_feature(package, feature);
4726     }
4727
4728     return ERROR_SUCCESS;
4729 }
4730
4731 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4732 {
4733     SYSTEMTIME systime;
4734     DWORD size, langid;
4735     WCHAR date[9], *val, *buffer;
4736     const WCHAR *prop, *key;
4737
4738     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4739     static const WCHAR modpath_fmt[] =
4740         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4741          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4742     static const WCHAR szModifyPath[] =
4743         {'M','o','d','i','f','y','P','a','t','h',0};
4744     static const WCHAR szUninstallString[] =
4745         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4746     static const WCHAR szEstimatedSize[] =
4747         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4748     static const WCHAR szDisplayVersion[] =
4749         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4750     static const WCHAR szInstallSource[] =
4751         {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4752     static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4753         {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4754     static const WCHAR szAuthorizedCDFPrefix[] =
4755         {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4756     static const WCHAR szARPCONTACT[] =
4757         {'A','R','P','C','O','N','T','A','C','T',0};
4758     static const WCHAR szContact[] =
4759         {'C','o','n','t','a','c','t',0};
4760     static const WCHAR szARPCOMMENTS[] =
4761         {'A','R','P','C','O','M','M','E','N','T','S',0};
4762     static const WCHAR szComments[] =
4763         {'C','o','m','m','e','n','t','s',0};
4764     static const WCHAR szProductName[] =
4765         {'P','r','o','d','u','c','t','N','a','m','e',0};
4766     static const WCHAR szDisplayName[] =
4767         {'D','i','s','p','l','a','y','N','a','m','e',0};
4768     static const WCHAR szARPHELPLINK[] =
4769         {'A','R','P','H','E','L','P','L','I','N','K',0};
4770     static const WCHAR szHelpLink[] =
4771         {'H','e','l','p','L','i','n','k',0};
4772     static const WCHAR szARPHELPTELEPHONE[] =
4773         {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4774     static const WCHAR szHelpTelephone[] =
4775         {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4776     static const WCHAR szARPINSTALLLOCATION[] =
4777         {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4778     static const WCHAR szInstallLocation[] =
4779         {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4780     static const WCHAR szManufacturer[] =
4781         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4782     static const WCHAR szPublisher[] =
4783         {'P','u','b','l','i','s','h','e','r',0};
4784     static const WCHAR szARPREADME[] =
4785         {'A','R','P','R','E','A','D','M','E',0};
4786     static const WCHAR szReadme[] =
4787         {'R','e','a','d','M','e',0};
4788     static const WCHAR szARPSIZE[] =
4789         {'A','R','P','S','I','Z','E',0};
4790     static const WCHAR szSize[] =
4791         {'S','i','z','e',0};
4792     static const WCHAR szARPURLINFOABOUT[] =
4793         {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4794     static const WCHAR szURLInfoAbout[] =
4795         {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4796     static const WCHAR szARPURLUPDATEINFO[] =
4797         {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4798     static const WCHAR szURLUpdateInfo[] =
4799         {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4800     static const WCHAR szARPSYSTEMCOMPONENT[] =
4801         {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4802     static const WCHAR szSystemComponent[] =
4803         {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4804
4805     static const WCHAR *propval[] = {
4806         szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4807         szARPCONTACT,             szContact,
4808         szARPCOMMENTS,            szComments,
4809         szProductName,            szDisplayName,
4810         szARPHELPLINK,            szHelpLink,
4811         szARPHELPTELEPHONE,       szHelpTelephone,
4812         szARPINSTALLLOCATION,     szInstallLocation,
4813         szSourceDir,              szInstallSource,
4814         szManufacturer,           szPublisher,
4815         szARPREADME,              szReadme,
4816         szARPSIZE,                szSize,
4817         szARPURLINFOABOUT,        szURLInfoAbout,
4818         szARPURLUPDATEINFO,       szURLUpdateInfo,
4819         NULL
4820     };
4821     const WCHAR **p = propval;
4822
4823     while (*p)
4824     {
4825         prop = *p++;
4826         key = *p++;
4827         val = msi_dup_property(package->db, prop);
4828         msi_reg_set_val_str(hkey, key, val);
4829         msi_free(val);
4830     }
4831
4832     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4833     if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4834     {
4835         msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4836     }
4837     size = deformat_string(package, modpath_fmt, &buffer);
4838     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4839     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4840     msi_free(buffer);
4841
4842     /* FIXME: Write real Estimated Size when we have it */
4843     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4844
4845     GetLocalTime(&systime);
4846     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4847     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4848
4849     langid = msi_get_property_int(package->db, szProductLanguage, 0);
4850     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4851
4852     buffer = msi_dup_property(package->db, szProductVersion);
4853     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4854     if (buffer)
4855     {
4856         DWORD verdword = msi_version_str_to_dword(buffer);
4857
4858         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4859         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4860         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4861         msi_free(buffer);
4862     }
4863
4864     return ERROR_SUCCESS;
4865 }
4866
4867 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4868 {
4869     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4870     MSIRECORD *uirow;
4871     LPWSTR upgrade_code;
4872     HKEY hkey, props, upgrade_key;
4873     UINT rc;
4874
4875     /* FIXME: also need to publish if the product is in advertise mode */
4876     if (!msi_check_publish(package))
4877         return ERROR_SUCCESS;
4878
4879     rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4880     if (rc != ERROR_SUCCESS)
4881         return rc;
4882
4883     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4884     if (rc != ERROR_SUCCESS)
4885         goto done;
4886
4887     rc = msi_publish_install_properties(package, hkey);
4888     if (rc != ERROR_SUCCESS)
4889         goto done;
4890
4891     rc = msi_publish_install_properties(package, props);
4892     if (rc != ERROR_SUCCESS)
4893         goto done;
4894
4895     upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4896     if (upgrade_code)
4897     {
4898         rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4899         if (rc == ERROR_SUCCESS)
4900         {
4901             squash_guid( package->ProductCode, squashed_pc );
4902             msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4903             RegCloseKey( upgrade_key );
4904         }
4905         msi_free( upgrade_code );
4906     }
4907     msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4908     package->delete_on_close = FALSE;
4909
4910 done:
4911     uirow = MSI_CreateRecord( 1 );
4912     MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4913     msi_ui_actiondata( package, szRegisterProduct, uirow );
4914     msiobj_release( &uirow->hdr );
4915
4916     RegCloseKey(hkey);
4917     return ERROR_SUCCESS;
4918 }
4919
4920 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4921 {
4922     return execute_script(package,INSTALL_SCRIPT);
4923 }
4924
4925 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4926 {
4927     MSIPACKAGE *package = param;
4928     const WCHAR *icon = MSI_RecordGetString( row, 1 );
4929     WCHAR *p, *icon_path;
4930
4931     if (!icon) return ERROR_SUCCESS;
4932     if ((icon_path = msi_build_icon_path( package, icon )))
4933     {
4934         TRACE("removing icon file %s\n", debugstr_w(icon_path));
4935         DeleteFileW( icon_path );
4936         if ((p = strrchrW( icon_path, '\\' )))
4937         {
4938             *p = 0;
4939             RemoveDirectoryW( icon_path );
4940         }
4941         msi_free( icon_path );
4942     }
4943     return ERROR_SUCCESS;
4944 }
4945
4946 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4947 {
4948     static const WCHAR query[]= {
4949         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4950     MSIQUERY *view;
4951     UINT r;
4952
4953     r = MSI_DatabaseOpenViewW( package->db, query, &view );
4954     if (r == ERROR_SUCCESS)
4955     {
4956         r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4957         msiobj_release( &view->hdr );
4958         if (r != ERROR_SUCCESS)
4959             return r;
4960     }
4961     return ERROR_SUCCESS;
4962 }
4963
4964 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4965 {
4966     static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4967     WCHAR *upgrade, **features;
4968     BOOL full_uninstall = TRUE;
4969     MSIFEATURE *feature;
4970     MSIPATCHINFO *patch;
4971     UINT i;
4972
4973     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4974     {
4975         if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4976     }
4977     features = msi_split_string( remove, ',' );
4978     for (i = 0; features && features[i]; i++)
4979     {
4980         if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4981     }
4982     msi_free(features);
4983
4984     if (!full_uninstall)
4985         return ERROR_SUCCESS;
4986
4987     MSIREG_DeleteProductKey(package->ProductCode);
4988     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4989     MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4990
4991     MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4992     MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4993     MSIREG_DeleteUserProductKey(package->ProductCode);
4994     MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4995
4996     upgrade = msi_dup_property(package->db, szUpgradeCode);
4997     if (upgrade)
4998     {
4999         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5000         MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5001         msi_free(upgrade);
5002     }
5003
5004     LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5005     {
5006         MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5007         if (!strcmpW( package->ProductCode, patch->products ))
5008         {
5009             TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5010             patch->delete_on_close = TRUE;
5011         }
5012         /* FIXME: remove local patch package if this is the last product */
5013     }
5014     TRACE("removing local package %s\n", debugstr_w(package->localfile));
5015     package->delete_on_close = TRUE;
5016
5017     msi_unpublish_icons( package );
5018     return ERROR_SUCCESS;
5019 }
5020
5021 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5022 {
5023     UINT rc;
5024     WCHAR *remove;
5025
5026     /* turn off scheduling */
5027     package->script->CurrentlyScripting= FALSE;
5028
5029     /* first do the same as an InstallExecute */
5030     rc = ACTION_InstallExecute(package);
5031     if (rc != ERROR_SUCCESS)
5032         return rc;
5033
5034     /* then handle Commit Actions */
5035     rc = execute_script(package,COMMIT_SCRIPT);
5036     if (rc != ERROR_SUCCESS)
5037         return rc;
5038
5039     remove = msi_dup_property(package->db, szRemove);
5040     rc = msi_unpublish_product(package, remove);
5041     msi_free(remove);
5042     return rc;
5043 }
5044
5045 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5046 {
5047     static const WCHAR RunOnce[] = {
5048     'S','o','f','t','w','a','r','e','\\',
5049     'M','i','c','r','o','s','o','f','t','\\',
5050     'W','i','n','d','o','w','s','\\',
5051     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5052     'R','u','n','O','n','c','e',0};
5053     static const WCHAR InstallRunOnce[] = {
5054     'S','o','f','t','w','a','r','e','\\',
5055     'M','i','c','r','o','s','o','f','t','\\',
5056     'W','i','n','d','o','w','s','\\',
5057     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5058     'I','n','s','t','a','l','l','e','r','\\',
5059     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5060
5061     static const WCHAR msiexec_fmt[] = {
5062     '%','s',
5063     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5064     '\"','%','s','\"',0};
5065     static const WCHAR install_fmt[] = {
5066     '/','I',' ','\"','%','s','\"',' ',
5067     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5068     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5069     WCHAR buffer[256], sysdir[MAX_PATH];
5070     HKEY hkey;
5071     WCHAR squished_pc[100];
5072
5073     squash_guid(package->ProductCode,squished_pc);
5074
5075     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5076     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5077     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5078      squished_pc);
5079
5080     msi_reg_set_val_str( hkey, squished_pc, buffer );
5081     RegCloseKey(hkey);
5082
5083     TRACE("Reboot command %s\n",debugstr_w(buffer));
5084
5085     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5086     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5087
5088     msi_reg_set_val_str( hkey, squished_pc, buffer );
5089     RegCloseKey(hkey);
5090
5091     return ERROR_INSTALL_SUSPEND;
5092 }
5093
5094 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5095 {
5096     static const WCHAR query[] =
5097         {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5098          'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5099          '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5100     MSIRECORD *rec, *row;
5101     DWORD i, size = 0;
5102     va_list va;
5103     const WCHAR *str;
5104     WCHAR *data;
5105
5106     if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5107
5108     rec = MSI_CreateRecord( count + 2 );
5109     str = MSI_RecordGetString( row, 1 );
5110     MSI_RecordSetStringW( rec, 0, str );
5111     msiobj_release( &row->hdr );
5112     MSI_RecordSetInteger( rec, 1, error );
5113
5114     va_start( va, count );
5115     for (i = 0; i < count; i++)
5116     {
5117         str = va_arg( va, const WCHAR *);
5118         MSI_RecordSetStringW( rec, i + 2, str );
5119     }
5120     va_end( va );
5121
5122     MSI_FormatRecordW( package, rec, NULL, &size );
5123     size++;
5124     data = msi_alloc( size * sizeof(WCHAR) );
5125     if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5126     else data[0] = 0;
5127     msiobj_release( &rec->hdr );
5128     return data;
5129 }
5130
5131 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5132 {
5133     DWORD attrib;
5134     UINT rc;
5135
5136     /*
5137      * We are currently doing what should be done here in the top level Install
5138      * however for Administrative and uninstalls this step will be needed
5139      */
5140     if (!package->PackagePath)
5141         return ERROR_SUCCESS;
5142
5143     msi_set_sourcedir_props(package, TRUE);
5144
5145     attrib = GetFileAttributesW(package->db->path);
5146     if (attrib == INVALID_FILE_ATTRIBUTES)
5147     {
5148         LPWSTR prompt, msg;
5149         DWORD size = 0;
5150
5151         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
5152                 package->Context, MSICODE_PRODUCT,
5153                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5154         if (rc == ERROR_MORE_DATA)
5155         {
5156             prompt = msi_alloc(size * sizeof(WCHAR));
5157             MsiSourceListGetInfoW(package->ProductCode, NULL, 
5158                     package->Context, MSICODE_PRODUCT,
5159                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5160         }
5161         else
5162             prompt = strdupW(package->db->path);
5163
5164         msg = msi_build_error_string(package, 1302, 1, prompt);
5165         msi_free(prompt);
5166         while(attrib == INVALID_FILE_ATTRIBUTES)
5167         {
5168             rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5169             if (rc == IDCANCEL)
5170             {
5171                 msi_free(msg);
5172                 return ERROR_INSTALL_USEREXIT;
5173             }
5174             attrib = GetFileAttributesW(package->db->path);
5175         }
5176         msi_free(msg);
5177         rc = ERROR_SUCCESS;
5178     }
5179     else
5180         return ERROR_SUCCESS;
5181
5182     return rc;
5183 }
5184
5185 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5186 {
5187     HKEY hkey = 0;
5188     LPWSTR buffer, productid = NULL;
5189     UINT i, rc = ERROR_SUCCESS;
5190     MSIRECORD *uirow;
5191
5192     static const WCHAR szPropKeys[][80] = 
5193     {
5194         {'P','r','o','d','u','c','t','I','D',0},
5195         {'U','S','E','R','N','A','M','E',0},
5196         {'C','O','M','P','A','N','Y','N','A','M','E',0},
5197         {0},
5198     };
5199
5200     static const WCHAR szRegKeys[][80] = 
5201     {
5202         {'P','r','o','d','u','c','t','I','D',0},
5203         {'R','e','g','O','w','n','e','r',0},
5204         {'R','e','g','C','o','m','p','a','n','y',0},
5205         {0},
5206     };
5207
5208     if (msi_check_unpublish(package))
5209     {
5210         MSIREG_DeleteUserDataProductKey(package->ProductCode);
5211         goto end;
5212     }
5213
5214     productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5215     if (!productid)
5216         goto end;
5217
5218     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5219                                  NULL, &hkey, TRUE);
5220     if (rc != ERROR_SUCCESS)
5221         goto end;
5222
5223     for( i = 0; szPropKeys[i][0]; i++ )
5224     {
5225         buffer = msi_dup_property( package->db, szPropKeys[i] );
5226         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5227         msi_free( buffer );
5228     }
5229
5230 end:
5231     uirow = MSI_CreateRecord( 1 );
5232     MSI_RecordSetStringW( uirow, 1, productid );
5233     msi_ui_actiondata( package, szRegisterUser, uirow );
5234     msiobj_release( &uirow->hdr );
5235
5236     msi_free(productid);
5237     RegCloseKey(hkey);
5238     return rc;
5239 }
5240
5241
5242 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5243 {
5244     UINT rc;
5245
5246     package->script->InWhatSequence |= SEQUENCE_EXEC;
5247     rc = ACTION_ProcessExecSequence(package,FALSE);
5248     return rc;
5249 }
5250
5251 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5252 {
5253     static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5254     WCHAR productid_85[21], component_85[21], *ret;
5255     GUID clsid;
5256     DWORD sz;
5257
5258     /* > is used if there is a component GUID and < if not.  */
5259
5260     productid_85[0] = 0;
5261     component_85[0] = 0;
5262     CLSIDFromString( package->ProductCode, &clsid );
5263
5264     encode_base85_guid( &clsid, productid_85 );
5265     if (component)
5266     {
5267         CLSIDFromString( component->ComponentId, &clsid );
5268         encode_base85_guid( &clsid, component_85 );
5269     }
5270
5271     TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5272           debugstr_w(component_85));
5273
5274     sz = 20 + strlenW( feature ) + 20 + 3;
5275     ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5276     if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5277     return ret;
5278 }
5279
5280 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5281 {
5282     MSIPACKAGE *package = param;
5283     LPCWSTR compgroupid, component, feature, qualifier, text;
5284     LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5285     HKEY hkey = NULL;
5286     UINT rc;
5287     MSICOMPONENT *comp;
5288     MSIFEATURE *feat;
5289     DWORD sz;
5290     MSIRECORD *uirow;
5291     int len;
5292
5293     feature = MSI_RecordGetString(rec, 5);
5294     feat = msi_get_loaded_feature(package, feature);
5295     if (!feat)
5296         return ERROR_SUCCESS;
5297
5298     feat->Action = msi_get_feature_action( package, feat );
5299     if (feat->Action != INSTALLSTATE_LOCAL &&
5300         feat->Action != INSTALLSTATE_SOURCE &&
5301         feat->Action != INSTALLSTATE_ADVERTISED)
5302     {
5303         TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5304         return ERROR_SUCCESS;
5305     }
5306
5307     component = MSI_RecordGetString(rec, 3);
5308     comp = msi_get_loaded_component(package, component);
5309     if (!comp)
5310         return ERROR_SUCCESS;
5311
5312     compgroupid = MSI_RecordGetString(rec,1);
5313     qualifier = MSI_RecordGetString(rec,2);
5314
5315     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5316     if (rc != ERROR_SUCCESS)
5317         goto end;
5318
5319     advertise = msi_create_component_advertise_string( package, comp, feature );
5320     text = MSI_RecordGetString( rec, 4 );
5321     if (text)
5322     {
5323         p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5324         strcpyW( p, advertise );
5325         strcatW( p, text );
5326         msi_free( advertise );
5327         advertise = p;
5328     }
5329     existing = msi_reg_get_val_str( hkey, qualifier );
5330
5331     sz = strlenW( advertise ) + 1;
5332     if (existing)
5333     {
5334         for (p = existing; *p; p += len)
5335         {
5336             len = strlenW( p ) + 1;
5337             if (strcmpW( advertise, p )) sz += len;
5338         }
5339     }
5340     if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5341     {
5342         rc = ERROR_OUTOFMEMORY;
5343         goto end;
5344     }
5345     q = output;
5346     if (existing)
5347     {
5348         for (p = existing; *p; p += len)
5349         {
5350             len = strlenW( p ) + 1;
5351             if (strcmpW( advertise, p ))
5352             {
5353                 memcpy( q, p, len * sizeof(WCHAR) );
5354                 q += len;
5355             }
5356         }
5357     }
5358     strcpyW( q, advertise );
5359     q[strlenW( q ) + 1] = 0;
5360
5361     msi_reg_set_val_multi_str( hkey, qualifier, output );
5362     
5363 end:
5364     RegCloseKey(hkey);
5365     msi_free( output );
5366     msi_free( advertise );
5367     msi_free( existing );
5368
5369     /* the UI chunk */
5370     uirow = MSI_CreateRecord( 2 );
5371     MSI_RecordSetStringW( uirow, 1, compgroupid );
5372     MSI_RecordSetStringW( uirow, 2, qualifier);
5373     msi_ui_actiondata( package, szPublishComponents, uirow );
5374     msiobj_release( &uirow->hdr );
5375     /* FIXME: call ui_progress? */
5376
5377     return rc;
5378 }
5379
5380 /*
5381  * At present I am ignorning the advertised components part of this and only
5382  * focusing on the qualified component sets
5383  */
5384 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5385 {
5386     static const WCHAR query[] = {
5387         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5388         '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5389     MSIQUERY *view;
5390     UINT rc;
5391     
5392     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5393     if (rc != ERROR_SUCCESS)
5394         return ERROR_SUCCESS;
5395
5396     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5397     msiobj_release(&view->hdr);
5398     return rc;
5399 }
5400
5401 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5402 {
5403     static const WCHAR szInstallerComponents[] = {
5404         'S','o','f','t','w','a','r','e','\\',
5405         'M','i','c','r','o','s','o','f','t','\\',
5406         'I','n','s','t','a','l','l','e','r','\\',
5407         'C','o','m','p','o','n','e','n','t','s','\\',0};
5408
5409     MSIPACKAGE *package = param;
5410     LPCWSTR compgroupid, component, feature, qualifier;
5411     MSICOMPONENT *comp;
5412     MSIFEATURE *feat;
5413     MSIRECORD *uirow;
5414     WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5415     LONG res;
5416
5417     feature = MSI_RecordGetString( rec, 5 );
5418     feat = msi_get_loaded_feature( package, feature );
5419     if (!feat)
5420         return ERROR_SUCCESS;
5421
5422     feat->Action = msi_get_feature_action( package, feat );
5423     if (feat->Action != INSTALLSTATE_ABSENT)
5424     {
5425         TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5426         return ERROR_SUCCESS;
5427     }
5428
5429     component = MSI_RecordGetString( rec, 3 );
5430     comp = msi_get_loaded_component( package, component );
5431     if (!comp)
5432         return ERROR_SUCCESS;
5433
5434     compgroupid = MSI_RecordGetString( rec, 1 );
5435     qualifier = MSI_RecordGetString( rec, 2 );
5436
5437     squash_guid( compgroupid, squashed );
5438     strcpyW( keypath, szInstallerComponents );
5439     strcatW( keypath, squashed );
5440
5441     res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5442     if (res != ERROR_SUCCESS)
5443     {
5444         WARN("Unable to delete component key %d\n", res);
5445     }
5446
5447     uirow = MSI_CreateRecord( 2 );
5448     MSI_RecordSetStringW( uirow, 1, compgroupid );
5449     MSI_RecordSetStringW( uirow, 2, qualifier );
5450     msi_ui_actiondata( package, szUnpublishComponents, uirow );
5451     msiobj_release( &uirow->hdr );
5452
5453     return ERROR_SUCCESS;
5454 }
5455
5456 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5457 {
5458     static const WCHAR query[] = {
5459         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5460         '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5461     MSIQUERY *view;
5462     UINT rc;
5463
5464     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5465     if (rc != ERROR_SUCCESS)
5466         return ERROR_SUCCESS;
5467
5468     rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5469     msiobj_release( &view->hdr );
5470     return rc;
5471 }
5472
5473 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5474 {
5475     static const WCHAR query[] =
5476         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5477          '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5478          '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5479     MSIPACKAGE *package = param;
5480     MSICOMPONENT *component;
5481     MSIRECORD *row;
5482     MSIFILE *file;
5483     SC_HANDLE hscm = NULL, service = NULL;
5484     LPCWSTR comp, key;
5485     LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5486     LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5487     DWORD serv_type, start_type, err_control;
5488     SERVICE_DESCRIPTIONW sd = {NULL};
5489
5490     comp = MSI_RecordGetString( rec, 12 );
5491     component = msi_get_loaded_component( package, comp );
5492     if (!component)
5493     {
5494         WARN("service component not found\n");
5495         goto done;
5496     }
5497     component->Action = msi_get_component_action( package, component );
5498     if (component->Action != INSTALLSTATE_LOCAL)
5499     {
5500         TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5501         goto done;
5502     }
5503     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5504     if (!hscm)
5505     {
5506         ERR("Failed to open the SC Manager!\n");
5507         goto done;
5508     }
5509
5510     start_type = MSI_RecordGetInteger(rec, 5);
5511     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5512         goto done;
5513
5514     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5515     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5516     serv_type = MSI_RecordGetInteger(rec, 4);
5517     err_control = MSI_RecordGetInteger(rec, 6);
5518     deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5519     deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5520     deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5521     deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5522     deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5523     deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5524
5525     /* fetch the service path */
5526     row = MSI_QueryGetRecord(package->db, query, comp);
5527     if (!row)
5528     {
5529         ERR("Query failed\n");
5530         goto done;
5531     }
5532     key = MSI_RecordGetString(row, 6);
5533     file = msi_get_loaded_file(package, key);
5534     msiobj_release(&row->hdr);
5535     if (!file)
5536     {
5537         ERR("Failed to load the service file\n");
5538         goto done;
5539     }
5540
5541     if (!args || !args[0]) image_path = file->TargetPath;
5542     else
5543     {
5544         int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5545         if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5546             return ERROR_OUTOFMEMORY;
5547
5548         strcpyW(image_path, file->TargetPath);
5549         strcatW(image_path, szSpace);
5550         strcatW(image_path, args);
5551     }
5552     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5553                              start_type, err_control, image_path, load_order,
5554                              NULL, depends, serv_name, pass);
5555
5556     if (!service)
5557     {
5558         if (GetLastError() != ERROR_SERVICE_EXISTS)
5559             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5560     }
5561     else if (sd.lpDescription)
5562     {
5563         if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5564             WARN("failed to set service description %u\n", GetLastError());
5565     }
5566
5567     if (image_path != file->TargetPath) msi_free(image_path);
5568 done:
5569     CloseServiceHandle(service);
5570     CloseServiceHandle(hscm);
5571     msi_free(name);
5572     msi_free(disp);
5573     msi_free(sd.lpDescription);
5574     msi_free(load_order);
5575     msi_free(serv_name);
5576     msi_free(pass);
5577     msi_free(depends);
5578     msi_free(args);
5579
5580     return ERROR_SUCCESS;
5581 }
5582
5583 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5584 {
5585     static const WCHAR query[] = {
5586         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5587         'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5588     MSIQUERY *view;
5589     UINT rc;
5590     
5591     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5592     if (rc != ERROR_SUCCESS)
5593         return ERROR_SUCCESS;
5594
5595     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5596     msiobj_release(&view->hdr);
5597     return rc;
5598 }
5599
5600 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5601 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5602 {
5603     LPCWSTR *vector, *temp_vector;
5604     LPWSTR p, q;
5605     DWORD sep_len;
5606
5607     static const WCHAR separator[] = {'[','~',']',0};
5608
5609     *numargs = 0;
5610     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5611
5612     if (!args)
5613         return NULL;
5614
5615     vector = msi_alloc(sizeof(LPWSTR));
5616     if (!vector)
5617         return NULL;
5618
5619     p = args;
5620     do
5621     {
5622         (*numargs)++;
5623         vector[*numargs - 1] = p;
5624
5625         if ((q = strstrW(p, separator)))
5626         {
5627             *q = '\0';
5628
5629             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5630             if (!temp_vector)
5631             {
5632                 msi_free(vector);
5633                 return NULL;
5634             }
5635             vector = temp_vector;
5636
5637             p = q + sep_len;
5638         }
5639     } while (q);
5640
5641     return vector;
5642 }
5643
5644 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5645 {
5646     MSIPACKAGE *package = param;
5647     MSICOMPONENT *comp;
5648     MSIRECORD *uirow;
5649     SC_HANDLE scm = NULL, service = NULL;
5650     LPCWSTR component, *vector = NULL;
5651     LPWSTR name, args, display_name = NULL;
5652     DWORD event, numargs, len, wait, dummy;
5653     UINT r = ERROR_FUNCTION_FAILED;
5654     SERVICE_STATUS_PROCESS status;
5655     ULONGLONG start_time;
5656
5657     component = MSI_RecordGetString(rec, 6);
5658     comp = msi_get_loaded_component(package, component);
5659     if (!comp)
5660         return ERROR_SUCCESS;
5661
5662     comp->Action = msi_get_component_action( package, comp );
5663     if (comp->Action != INSTALLSTATE_LOCAL)
5664     {
5665         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5666         return ERROR_SUCCESS;
5667     }
5668
5669     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5670     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5671     event = MSI_RecordGetInteger(rec, 3);
5672     wait = MSI_RecordGetInteger(rec, 5);
5673
5674     if (!(event & msidbServiceControlEventStart))
5675     {
5676         r = ERROR_SUCCESS;
5677         goto done;
5678     }
5679
5680     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5681     if (!scm)
5682     {
5683         ERR("Failed to open the service control manager\n");
5684         goto done;
5685     }
5686
5687     len = 0;
5688     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5689         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5690     {
5691         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5692             GetServiceDisplayNameW( scm, name, display_name, &len );
5693     }
5694
5695     service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5696     if (!service)
5697     {
5698         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5699         goto done;
5700     }
5701
5702     vector = msi_service_args_to_vector(args, &numargs);
5703
5704     if (!StartServiceW(service, numargs, vector) &&
5705         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5706     {
5707         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5708         goto done;
5709     }
5710
5711     r = ERROR_SUCCESS;
5712     if (wait)
5713     {
5714         /* wait for at most 30 seconds for the service to be up and running */
5715         if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5716             (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5717         {
5718             TRACE("failed to query service status (%u)\n", GetLastError());
5719             goto done;
5720         }
5721         start_time = GetTickCount64();
5722         while (status.dwCurrentState == SERVICE_START_PENDING)
5723         {
5724             if (GetTickCount64() - start_time > 30000) break;
5725             Sleep(1000);
5726             if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5727                 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5728             {
5729                 TRACE("failed to query service status (%u)\n", GetLastError());
5730                 goto done;
5731             }
5732         }
5733         if (status.dwCurrentState != SERVICE_RUNNING)
5734         {
5735             WARN("service failed to start %u\n", status.dwCurrentState);
5736             r = ERROR_FUNCTION_FAILED;
5737         }
5738     }
5739
5740 done:
5741     uirow = MSI_CreateRecord( 2 );
5742     MSI_RecordSetStringW( uirow, 1, display_name );
5743     MSI_RecordSetStringW( uirow, 2, name );
5744     msi_ui_actiondata( package, szStartServices, uirow );
5745     msiobj_release( &uirow->hdr );
5746
5747     CloseServiceHandle(service);
5748     CloseServiceHandle(scm);
5749
5750     msi_free(name);
5751     msi_free(args);
5752     msi_free(vector);
5753     msi_free(display_name);
5754     return r;
5755 }
5756
5757 static UINT ACTION_StartServices( MSIPACKAGE *package )
5758 {
5759     static const WCHAR query[] = {
5760         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5761         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5762     MSIQUERY *view;
5763     UINT rc;
5764
5765     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5766     if (rc != ERROR_SUCCESS)
5767         return ERROR_SUCCESS;
5768
5769     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5770     msiobj_release(&view->hdr);
5771     return rc;
5772 }
5773
5774 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5775 {
5776     DWORD i, needed, count;
5777     ENUM_SERVICE_STATUSW *dependencies;
5778     SERVICE_STATUS ss;
5779     SC_HANDLE depserv;
5780
5781     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5782                                0, &needed, &count))
5783         return TRUE;
5784
5785     if (GetLastError() != ERROR_MORE_DATA)
5786         return FALSE;
5787
5788     dependencies = msi_alloc(needed);
5789     if (!dependencies)
5790         return FALSE;
5791
5792     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5793                                 needed, &needed, &count))
5794         goto error;
5795
5796     for (i = 0; i < count; i++)
5797     {
5798         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5799                                SERVICE_STOP | SERVICE_QUERY_STATUS);
5800         if (!depserv)
5801             goto error;
5802
5803         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5804             goto error;
5805     }
5806
5807     return TRUE;
5808
5809 error:
5810     msi_free(dependencies);
5811     return FALSE;
5812 }
5813
5814 static UINT stop_service( LPCWSTR name )
5815 {
5816     SC_HANDLE scm = NULL, service = NULL;
5817     SERVICE_STATUS status;
5818     SERVICE_STATUS_PROCESS ssp;
5819     DWORD needed;
5820
5821     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5822     if (!scm)
5823     {
5824         WARN("Failed to open the SCM: %d\n", GetLastError());
5825         goto done;
5826     }
5827
5828     service = OpenServiceW(scm, name,
5829                            SERVICE_STOP |
5830                            SERVICE_QUERY_STATUS |
5831                            SERVICE_ENUMERATE_DEPENDENTS);
5832     if (!service)
5833     {
5834         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5835         goto done;
5836     }
5837
5838     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5839                               sizeof(SERVICE_STATUS_PROCESS), &needed))
5840     {
5841         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5842         goto done;
5843     }
5844
5845     if (ssp.dwCurrentState == SERVICE_STOPPED)
5846         goto done;
5847
5848     stop_service_dependents(scm, service);
5849
5850     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5851         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5852
5853 done:
5854     CloseServiceHandle(service);
5855     CloseServiceHandle(scm);
5856
5857     return ERROR_SUCCESS;
5858 }
5859
5860 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5861 {
5862     MSIPACKAGE *package = param;
5863     MSICOMPONENT *comp;
5864     MSIRECORD *uirow;
5865     LPCWSTR component;
5866     LPWSTR name = NULL, display_name = NULL;
5867     DWORD event, len;
5868     SC_HANDLE scm;
5869
5870     event = MSI_RecordGetInteger( rec, 3 );
5871     if (!(event & msidbServiceControlEventStop))
5872         return ERROR_SUCCESS;
5873
5874     component = MSI_RecordGetString( rec, 6 );
5875     comp = msi_get_loaded_component( package, component );
5876     if (!comp)
5877         return ERROR_SUCCESS;
5878
5879     comp->Action = msi_get_component_action( package, comp );
5880     if (comp->Action != INSTALLSTATE_ABSENT)
5881     {
5882         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5883         return ERROR_SUCCESS;
5884     }
5885
5886     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5887     if (!scm)
5888     {
5889         ERR("Failed to open the service control manager\n");
5890         goto done;
5891     }
5892
5893     len = 0;
5894     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5895         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5896     {
5897         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5898             GetServiceDisplayNameW( scm, name, display_name, &len );
5899     }
5900     CloseServiceHandle( scm );
5901
5902     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5903     stop_service( name );
5904
5905 done:
5906     uirow = MSI_CreateRecord( 2 );
5907     MSI_RecordSetStringW( uirow, 1, display_name );
5908     MSI_RecordSetStringW( uirow, 2, name );
5909     msi_ui_actiondata( package, szStopServices, uirow );
5910     msiobj_release( &uirow->hdr );
5911
5912     msi_free( name );
5913     msi_free( display_name );
5914     return ERROR_SUCCESS;
5915 }
5916
5917 static UINT ACTION_StopServices( MSIPACKAGE *package )
5918 {
5919     static const WCHAR query[] = {
5920         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5921         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5922     MSIQUERY *view;
5923     UINT rc;
5924
5925     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5926     if (rc != ERROR_SUCCESS)
5927         return ERROR_SUCCESS;
5928
5929     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5930     msiobj_release(&view->hdr);
5931     return rc;
5932 }
5933
5934 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5935 {
5936     MSIPACKAGE *package = param;
5937     MSICOMPONENT *comp;
5938     MSIRECORD *uirow;
5939     LPCWSTR component;
5940     LPWSTR name = NULL, display_name = NULL;
5941     DWORD event, len;
5942     SC_HANDLE scm = NULL, service = NULL;
5943
5944     event = MSI_RecordGetInteger( rec, 3 );
5945     if (!(event & msidbServiceControlEventDelete))
5946         return ERROR_SUCCESS;
5947
5948     component = MSI_RecordGetString(rec, 6);
5949     comp = msi_get_loaded_component(package, component);
5950     if (!comp)
5951         return ERROR_SUCCESS;
5952
5953     comp->Action = msi_get_component_action( package, comp );
5954     if (comp->Action != INSTALLSTATE_ABSENT)
5955     {
5956         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5957         return ERROR_SUCCESS;
5958     }
5959
5960     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5961     stop_service( name );
5962
5963     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5964     if (!scm)
5965     {
5966         WARN("Failed to open the SCM: %d\n", GetLastError());
5967         goto done;
5968     }
5969
5970     len = 0;
5971     if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5972         GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5973     {
5974         if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5975             GetServiceDisplayNameW( scm, name, display_name, &len );
5976     }
5977
5978     service = OpenServiceW( scm, name, DELETE );
5979     if (!service)
5980     {
5981         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5982         goto done;
5983     }
5984
5985     if (!DeleteService( service ))
5986         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5987
5988 done:
5989     uirow = MSI_CreateRecord( 2 );
5990     MSI_RecordSetStringW( uirow, 1, display_name );
5991     MSI_RecordSetStringW( uirow, 2, name );
5992     msi_ui_actiondata( package, szDeleteServices, uirow );
5993     msiobj_release( &uirow->hdr );
5994
5995     CloseServiceHandle( service );
5996     CloseServiceHandle( scm );
5997     msi_free( name );
5998     msi_free( display_name );
5999
6000     return ERROR_SUCCESS;
6001 }
6002
6003 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6004 {
6005     static const WCHAR query[] = {
6006         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6007         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6008     MSIQUERY *view;
6009     UINT rc;
6010
6011     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6012     if (rc != ERROR_SUCCESS)
6013         return ERROR_SUCCESS;
6014
6015     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6016     msiobj_release( &view->hdr );
6017     return rc;
6018 }
6019
6020 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6021 {
6022     MSIPACKAGE *package = param;
6023     LPWSTR driver, driver_path, ptr;
6024     WCHAR outpath[MAX_PATH];
6025     MSIFILE *driver_file = NULL, *setup_file = NULL;
6026     MSICOMPONENT *comp;
6027     MSIRECORD *uirow;
6028     LPCWSTR desc, file_key, component;
6029     DWORD len, usage;
6030     UINT r = ERROR_SUCCESS;
6031
6032     static const WCHAR driver_fmt[] = {
6033         'D','r','i','v','e','r','=','%','s',0};
6034     static const WCHAR setup_fmt[] = {
6035         'S','e','t','u','p','=','%','s',0};
6036     static const WCHAR usage_fmt[] = {
6037         'F','i','l','e','U','s','a','g','e','=','1',0};
6038
6039     component = MSI_RecordGetString( rec, 2 );
6040     comp = msi_get_loaded_component( package, component );
6041     if (!comp)
6042         return ERROR_SUCCESS;
6043
6044     comp->Action = msi_get_component_action( package, comp );
6045     if (comp->Action != INSTALLSTATE_LOCAL)
6046     {
6047         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6048         return ERROR_SUCCESS;
6049     }
6050     desc = MSI_RecordGetString(rec, 3);
6051
6052     file_key = MSI_RecordGetString( rec, 4 );
6053     if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6054
6055     file_key = MSI_RecordGetString( rec, 5 );
6056     if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6057
6058     if (!driver_file)
6059     {
6060         ERR("ODBC Driver entry not found!\n");
6061         return ERROR_FUNCTION_FAILED;
6062     }
6063
6064     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6065     if (setup_file)
6066         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6067     len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6068
6069     driver = msi_alloc(len * sizeof(WCHAR));
6070     if (!driver)
6071         return ERROR_OUTOFMEMORY;
6072
6073     ptr = driver;
6074     lstrcpyW(ptr, desc);
6075     ptr += lstrlenW(ptr) + 1;
6076
6077     len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6078     ptr += len + 1;
6079
6080     if (setup_file)
6081     {
6082         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6083         ptr += len + 1;
6084     }
6085
6086     lstrcpyW(ptr, usage_fmt);
6087     ptr += lstrlenW(ptr) + 1;
6088     *ptr = '\0';
6089
6090     driver_path = strdupW(driver_file->TargetPath);
6091     ptr = strrchrW(driver_path, '\\');
6092     if (ptr) *ptr = '\0';
6093
6094     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6095                              NULL, ODBC_INSTALL_COMPLETE, &usage))
6096     {
6097         ERR("Failed to install SQL driver!\n");
6098         r = ERROR_FUNCTION_FAILED;
6099     }
6100
6101     uirow = MSI_CreateRecord( 5 );
6102     MSI_RecordSetStringW( uirow, 1, desc );
6103     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6104     MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6105     msi_ui_actiondata( package, szInstallODBC, uirow );
6106     msiobj_release( &uirow->hdr );
6107
6108     msi_free(driver);
6109     msi_free(driver_path);
6110
6111     return r;
6112 }
6113
6114 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6115 {
6116     MSIPACKAGE *package = param;
6117     LPWSTR translator, translator_path, ptr;
6118     WCHAR outpath[MAX_PATH];
6119     MSIFILE *translator_file = NULL, *setup_file = NULL;
6120     MSICOMPONENT *comp;
6121     MSIRECORD *uirow;
6122     LPCWSTR desc, file_key, component;
6123     DWORD len, usage;
6124     UINT r = ERROR_SUCCESS;
6125
6126     static const WCHAR translator_fmt[] = {
6127         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6128     static const WCHAR setup_fmt[] = {
6129         'S','e','t','u','p','=','%','s',0};
6130
6131     component = MSI_RecordGetString( rec, 2 );
6132     comp = msi_get_loaded_component( package, component );
6133     if (!comp)
6134         return ERROR_SUCCESS;
6135
6136     comp->Action = msi_get_component_action( package, comp );
6137     if (comp->Action != INSTALLSTATE_LOCAL)
6138     {
6139         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6140         return ERROR_SUCCESS;
6141     }
6142     desc = MSI_RecordGetString(rec, 3);
6143
6144     file_key = MSI_RecordGetString( rec, 4 );
6145     if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6146
6147     file_key = MSI_RecordGetString( rec, 5 );
6148     if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6149
6150     if (!translator_file)
6151     {
6152         ERR("ODBC Translator entry not found!\n");
6153         return ERROR_FUNCTION_FAILED;
6154     }
6155
6156     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6157     if (setup_file)
6158         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6159
6160     translator = msi_alloc(len * sizeof(WCHAR));
6161     if (!translator)
6162         return ERROR_OUTOFMEMORY;
6163
6164     ptr = translator;
6165     lstrcpyW(ptr, desc);
6166     ptr += lstrlenW(ptr) + 1;
6167
6168     len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6169     ptr += len + 1;
6170
6171     if (setup_file)
6172     {
6173         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6174         ptr += len + 1;
6175     }
6176     *ptr = '\0';
6177
6178     translator_path = strdupW(translator_file->TargetPath);
6179     ptr = strrchrW(translator_path, '\\');
6180     if (ptr) *ptr = '\0';
6181
6182     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6183                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
6184     {
6185         ERR("Failed to install SQL translator!\n");
6186         r = ERROR_FUNCTION_FAILED;
6187     }
6188
6189     uirow = MSI_CreateRecord( 5 );
6190     MSI_RecordSetStringW( uirow, 1, desc );
6191     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6192     MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6193     msi_ui_actiondata( package, szInstallODBC, uirow );
6194     msiobj_release( &uirow->hdr );
6195
6196     msi_free(translator);
6197     msi_free(translator_path);
6198
6199     return r;
6200 }
6201
6202 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6203 {
6204     MSIPACKAGE *package = param;
6205     MSICOMPONENT *comp;
6206     LPWSTR attrs;
6207     LPCWSTR desc, driver, component;
6208     WORD request = ODBC_ADD_SYS_DSN;
6209     INT registration;
6210     DWORD len;
6211     UINT r = ERROR_SUCCESS;
6212     MSIRECORD *uirow;
6213
6214     static const WCHAR attrs_fmt[] = {
6215         'D','S','N','=','%','s',0 };
6216
6217     component = MSI_RecordGetString( rec, 2 );
6218     comp = msi_get_loaded_component( package, component );
6219     if (!comp)
6220         return ERROR_SUCCESS;
6221
6222     comp->Action = msi_get_component_action( package, comp );
6223     if (comp->Action != INSTALLSTATE_LOCAL)
6224     {
6225         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6226         return ERROR_SUCCESS;
6227     }
6228
6229     desc = MSI_RecordGetString(rec, 3);
6230     driver = MSI_RecordGetString(rec, 4);
6231     registration = MSI_RecordGetInteger(rec, 5);
6232
6233     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6234     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6235
6236     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6237     attrs = msi_alloc(len * sizeof(WCHAR));
6238     if (!attrs)
6239         return ERROR_OUTOFMEMORY;
6240
6241     len = sprintfW(attrs, attrs_fmt, desc);
6242     attrs[len + 1] = 0;
6243
6244     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6245     {
6246         ERR("Failed to install SQL data source!\n");
6247         r = ERROR_FUNCTION_FAILED;
6248     }
6249
6250     uirow = MSI_CreateRecord( 5 );
6251     MSI_RecordSetStringW( uirow, 1, desc );
6252     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6253     MSI_RecordSetInteger( uirow, 3, request );
6254     msi_ui_actiondata( package, szInstallODBC, uirow );
6255     msiobj_release( &uirow->hdr );
6256
6257     msi_free(attrs);
6258
6259     return r;
6260 }
6261
6262 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6263 {
6264     static const WCHAR driver_query[] = {
6265         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6266         'O','D','B','C','D','r','i','v','e','r',0};
6267     static const WCHAR translator_query[] = {
6268         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6269         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6270     static const WCHAR source_query[] = {
6271         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6272         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6273     MSIQUERY *view;
6274     UINT rc;
6275
6276     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6277     if (rc == ERROR_SUCCESS)
6278     {
6279         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6280         msiobj_release(&view->hdr);
6281         if (rc != ERROR_SUCCESS)
6282             return rc;
6283     }
6284     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6285     if (rc == ERROR_SUCCESS)
6286     {
6287         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6288         msiobj_release(&view->hdr);
6289         if (rc != ERROR_SUCCESS)
6290             return rc;
6291     }
6292     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6293     if (rc == ERROR_SUCCESS)
6294     {
6295         rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6296         msiobj_release(&view->hdr);
6297         if (rc != ERROR_SUCCESS)
6298             return rc;
6299     }
6300     return ERROR_SUCCESS;
6301 }
6302
6303 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6304 {
6305     MSIPACKAGE *package = param;
6306     MSICOMPONENT *comp;
6307     MSIRECORD *uirow;
6308     DWORD usage;
6309     LPCWSTR desc, component;
6310
6311     component = MSI_RecordGetString( rec, 2 );
6312     comp = msi_get_loaded_component( package, component );
6313     if (!comp)
6314         return ERROR_SUCCESS;
6315
6316     comp->Action = msi_get_component_action( package, comp );
6317     if (comp->Action != INSTALLSTATE_ABSENT)
6318     {
6319         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6320         return ERROR_SUCCESS;
6321     }
6322
6323     desc = MSI_RecordGetString( rec, 3 );
6324     if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6325     {
6326         WARN("Failed to remove ODBC driver\n");
6327     }
6328     else if (!usage)
6329     {
6330         FIXME("Usage count reached 0\n");
6331     }
6332
6333     uirow = MSI_CreateRecord( 2 );
6334     MSI_RecordSetStringW( uirow, 1, desc );
6335     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6336     msi_ui_actiondata( package, szRemoveODBC, uirow );
6337     msiobj_release( &uirow->hdr );
6338
6339     return ERROR_SUCCESS;
6340 }
6341
6342 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6343 {
6344     MSIPACKAGE *package = param;
6345     MSICOMPONENT *comp;
6346     MSIRECORD *uirow;
6347     DWORD usage;
6348     LPCWSTR desc, component;
6349
6350     component = MSI_RecordGetString( rec, 2 );
6351     comp = msi_get_loaded_component( package, component );
6352     if (!comp)
6353         return ERROR_SUCCESS;
6354
6355     comp->Action = msi_get_component_action( package, comp );
6356     if (comp->Action != INSTALLSTATE_ABSENT)
6357     {
6358         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6359         return ERROR_SUCCESS;
6360     }
6361
6362     desc = MSI_RecordGetString( rec, 3 );
6363     if (!SQLRemoveTranslatorW( desc, &usage ))
6364     {
6365         WARN("Failed to remove ODBC translator\n");
6366     }
6367     else if (!usage)
6368     {
6369         FIXME("Usage count reached 0\n");
6370     }
6371
6372     uirow = MSI_CreateRecord( 2 );
6373     MSI_RecordSetStringW( uirow, 1, desc );
6374     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6375     msi_ui_actiondata( package, szRemoveODBC, uirow );
6376     msiobj_release( &uirow->hdr );
6377
6378     return ERROR_SUCCESS;
6379 }
6380
6381 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6382 {
6383     MSIPACKAGE *package = param;
6384     MSICOMPONENT *comp;
6385     MSIRECORD *uirow;
6386     LPWSTR attrs;
6387     LPCWSTR desc, driver, component;
6388     WORD request = ODBC_REMOVE_SYS_DSN;
6389     INT registration;
6390     DWORD len;
6391
6392     static const WCHAR attrs_fmt[] = {
6393         'D','S','N','=','%','s',0 };
6394
6395     component = MSI_RecordGetString( rec, 2 );
6396     comp = msi_get_loaded_component( package, component );
6397     if (!comp)
6398         return ERROR_SUCCESS;
6399
6400     comp->Action = msi_get_component_action( package, comp );
6401     if (comp->Action != INSTALLSTATE_ABSENT)
6402     {
6403         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6404         return ERROR_SUCCESS;
6405     }
6406
6407     desc = MSI_RecordGetString( rec, 3 );
6408     driver = MSI_RecordGetString( rec, 4 );
6409     registration = MSI_RecordGetInteger( rec, 5 );
6410
6411     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6412     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6413
6414     len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6415     attrs = msi_alloc( len * sizeof(WCHAR) );
6416     if (!attrs)
6417         return ERROR_OUTOFMEMORY;
6418
6419     FIXME("Use ODBCSourceAttribute table\n");
6420
6421     len = sprintfW( attrs, attrs_fmt, desc );
6422     attrs[len + 1] = 0;
6423
6424     if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6425     {
6426         WARN("Failed to remove ODBC data source\n");
6427     }
6428     msi_free( attrs );
6429
6430     uirow = MSI_CreateRecord( 3 );
6431     MSI_RecordSetStringW( uirow, 1, desc );
6432     MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6433     MSI_RecordSetInteger( uirow, 3, request );
6434     msi_ui_actiondata( package, szRemoveODBC, uirow );
6435     msiobj_release( &uirow->hdr );
6436
6437     return ERROR_SUCCESS;
6438 }
6439
6440 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6441 {
6442     static const WCHAR driver_query[] = {
6443         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6444         'O','D','B','C','D','r','i','v','e','r',0};
6445     static const WCHAR translator_query[] = {
6446         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6447         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6448     static const WCHAR source_query[] = {
6449         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6450         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6451     MSIQUERY *view;
6452     UINT rc;
6453
6454     rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6455     if (rc == ERROR_SUCCESS)
6456     {
6457         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6458         msiobj_release( &view->hdr );
6459         if (rc != ERROR_SUCCESS)
6460             return rc;
6461     }
6462     rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6463     if (rc == ERROR_SUCCESS)
6464     {
6465         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6466         msiobj_release( &view->hdr );
6467         if (rc != ERROR_SUCCESS)
6468             return rc;
6469     }
6470     rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6471     if (rc == ERROR_SUCCESS)
6472     {
6473         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6474         msiobj_release( &view->hdr );
6475         if (rc != ERROR_SUCCESS)
6476             return rc;
6477     }
6478     return ERROR_SUCCESS;
6479 }
6480
6481 #define ENV_ACT_SETALWAYS   0x1
6482 #define ENV_ACT_SETABSENT   0x2
6483 #define ENV_ACT_REMOVE      0x4
6484 #define ENV_ACT_REMOVEMATCH 0x8
6485
6486 #define ENV_MOD_MACHINE     0x20000000
6487 #define ENV_MOD_APPEND      0x40000000
6488 #define ENV_MOD_PREFIX      0x80000000
6489 #define ENV_MOD_MASK        0xC0000000
6490
6491 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6492
6493 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6494 {
6495     LPCWSTR cptr = *name;
6496
6497     static const WCHAR prefix[] = {'[','~',']',0};
6498     static const int prefix_len = 3;
6499
6500     *flags = 0;
6501     while (*cptr)
6502     {
6503         if (*cptr == '=')
6504             *flags |= ENV_ACT_SETALWAYS;
6505         else if (*cptr == '+')
6506             *flags |= ENV_ACT_SETABSENT;
6507         else if (*cptr == '-')
6508             *flags |= ENV_ACT_REMOVE;
6509         else if (*cptr == '!')
6510             *flags |= ENV_ACT_REMOVEMATCH;
6511         else if (*cptr == '*')
6512             *flags |= ENV_MOD_MACHINE;
6513         else
6514             break;
6515
6516         cptr++;
6517         (*name)++;
6518     }
6519
6520     if (!*cptr)
6521     {
6522         ERR("Missing environment variable\n");
6523         return ERROR_FUNCTION_FAILED;
6524     }
6525
6526     if (*value)
6527     {
6528         LPCWSTR ptr = *value;
6529         if (!strncmpW(ptr, prefix, prefix_len))
6530         {
6531             if (ptr[prefix_len] == szSemiColon[0])
6532             {
6533                 *flags |= ENV_MOD_APPEND;
6534                 *value += lstrlenW(prefix);
6535             }
6536             else
6537             {
6538                 *value = NULL;
6539             }
6540         }
6541         else if (lstrlenW(*value) >= prefix_len)
6542         {
6543             ptr += lstrlenW(ptr) - prefix_len;
6544             if (!strcmpW( ptr, prefix ))
6545             {
6546                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6547                 {
6548                     *flags |= ENV_MOD_PREFIX;
6549                     /* the "[~]" will be removed by deformat_string */;
6550                 }
6551                 else
6552                 {
6553                     *value = NULL;
6554                 }
6555             }
6556         }
6557     }
6558
6559     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6560         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6561         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6562         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6563     {
6564         ERR("Invalid flags: %08x\n", *flags);
6565         return ERROR_FUNCTION_FAILED;
6566     }
6567
6568     if (!*flags)
6569         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6570
6571     return ERROR_SUCCESS;
6572 }
6573
6574 static UINT open_env_key( DWORD flags, HKEY *key )
6575 {
6576     static const WCHAR user_env[] =
6577         {'E','n','v','i','r','o','n','m','e','n','t',0};
6578     static const WCHAR machine_env[] =
6579         {'S','y','s','t','e','m','\\',
6580          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6581          'C','o','n','t','r','o','l','\\',
6582          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6583          'E','n','v','i','r','o','n','m','e','n','t',0};
6584     const WCHAR *env;
6585     HKEY root;
6586     LONG res;
6587
6588     if (flags & ENV_MOD_MACHINE)
6589     {
6590         env = machine_env;
6591         root = HKEY_LOCAL_MACHINE;
6592     }
6593     else
6594     {
6595         env = user_env;
6596         root = HKEY_CURRENT_USER;
6597     }
6598
6599     res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6600     if (res != ERROR_SUCCESS)
6601     {
6602         WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6603         return ERROR_FUNCTION_FAILED;
6604     }
6605
6606     return ERROR_SUCCESS;
6607 }
6608
6609 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6610 {
6611     MSIPACKAGE *package = param;
6612     LPCWSTR name, value, component;
6613     LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6614     DWORD flags, type, size;
6615     UINT res;
6616     HKEY env = NULL;
6617     MSICOMPONENT *comp;
6618     MSIRECORD *uirow;
6619     int action = 0;
6620
6621     component = MSI_RecordGetString(rec, 4);
6622     comp = msi_get_loaded_component(package, component);
6623     if (!comp)
6624         return ERROR_SUCCESS;
6625
6626     comp->Action = msi_get_component_action( package, comp );
6627     if (comp->Action != INSTALLSTATE_LOCAL)
6628     {
6629         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6630         return ERROR_SUCCESS;
6631     }
6632     name = MSI_RecordGetString(rec, 2);
6633     value = MSI_RecordGetString(rec, 3);
6634
6635     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6636
6637     res = env_parse_flags(&name, &value, &flags);
6638     if (res != ERROR_SUCCESS || !value)
6639        goto done;
6640
6641     if (value && !deformat_string(package, value, &deformatted))
6642     {
6643         res = ERROR_OUTOFMEMORY;
6644         goto done;
6645     }
6646
6647     value = deformatted;
6648
6649     res = open_env_key( flags, &env );
6650     if (res != ERROR_SUCCESS)
6651         goto done;
6652
6653     if (flags & ENV_MOD_MACHINE)
6654         action |= 0x20000000;
6655
6656     size = 0;
6657     type = REG_SZ;
6658     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6659     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6660         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6661         goto done;
6662
6663     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6664     {
6665         action = 0x2;
6666
6667         /* Nothing to do. */
6668         if (!value)
6669         {
6670             res = ERROR_SUCCESS;
6671             goto done;
6672         }
6673
6674         /* If we are appending but the string was empty, strip ; */
6675         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6676
6677         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6678         newval = strdupW(value);
6679         if (!newval)
6680         {
6681             res = ERROR_OUTOFMEMORY;
6682             goto done;
6683         }
6684     }
6685     else
6686     {
6687         action = 0x1;
6688
6689         /* Contrary to MSDN, +-variable to [~];path works */
6690         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6691         {
6692             res = ERROR_SUCCESS;
6693             goto done;
6694         }
6695
6696         data = msi_alloc(size);
6697         if (!data)
6698         {
6699             RegCloseKey(env);
6700             return ERROR_OUTOFMEMORY;
6701         }
6702
6703         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6704         if (res != ERROR_SUCCESS)
6705             goto done;
6706
6707         if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6708         {
6709             action = 0x4;
6710             res = RegDeleteValueW(env, name);
6711             if (res != ERROR_SUCCESS)
6712                 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6713             goto done;
6714         }
6715
6716         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6717         if (flags & ENV_MOD_MASK)
6718         {
6719             DWORD mod_size;
6720             int multiplier = 0;
6721             if (flags & ENV_MOD_APPEND) multiplier++;
6722             if (flags & ENV_MOD_PREFIX) multiplier++;
6723             mod_size = lstrlenW(value) * multiplier;
6724             size += mod_size * sizeof(WCHAR);
6725         }
6726
6727         newval = msi_alloc(size);
6728         ptr = newval;
6729         if (!newval)
6730         {
6731             res = ERROR_OUTOFMEMORY;
6732             goto done;
6733         }
6734
6735         if (flags & ENV_MOD_PREFIX)
6736         {
6737             lstrcpyW(newval, value);
6738             ptr = newval + lstrlenW(value);
6739             action |= 0x80000000;
6740         }
6741
6742         lstrcpyW(ptr, data);
6743
6744         if (flags & ENV_MOD_APPEND)
6745         {
6746             lstrcatW(newval, value);
6747             action |= 0x40000000;
6748         }
6749     }
6750     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6751     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6752     if (res)
6753     {
6754         WARN("Failed to set %s to %s (%d)\n",  debugstr_w(name), debugstr_w(newval), res);
6755     }
6756
6757 done:
6758     uirow = MSI_CreateRecord( 3 );
6759     MSI_RecordSetStringW( uirow, 1, name );
6760     MSI_RecordSetStringW( uirow, 2, newval );
6761     MSI_RecordSetInteger( uirow, 3, action );
6762     msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6763     msiobj_release( &uirow->hdr );
6764
6765     if (env) RegCloseKey(env);
6766     msi_free(deformatted);
6767     msi_free(data);
6768     msi_free(newval);
6769     return res;
6770 }
6771
6772 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6773 {
6774     static const WCHAR query[] = {
6775         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6776         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6777     MSIQUERY *view;
6778     UINT rc;
6779
6780     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6781     if (rc != ERROR_SUCCESS)
6782         return ERROR_SUCCESS;
6783
6784     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6785     msiobj_release(&view->hdr);
6786     return rc;
6787 }
6788
6789 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6790 {
6791     MSIPACKAGE *package = param;
6792     LPCWSTR name, value, component;
6793     LPWSTR deformatted = NULL;
6794     DWORD flags;
6795     HKEY env;
6796     MSICOMPONENT *comp;
6797     MSIRECORD *uirow;
6798     int action = 0;
6799     LONG res;
6800     UINT r;
6801
6802     component = MSI_RecordGetString( rec, 4 );
6803     comp = msi_get_loaded_component( package, component );
6804     if (!comp)
6805         return ERROR_SUCCESS;
6806
6807     comp->Action = msi_get_component_action( package, comp );
6808     if (comp->Action != INSTALLSTATE_ABSENT)
6809     {
6810         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6811         return ERROR_SUCCESS;
6812     }
6813     name = MSI_RecordGetString( rec, 2 );
6814     value = MSI_RecordGetString( rec, 3 );
6815
6816     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6817
6818     r = env_parse_flags( &name, &value, &flags );
6819     if (r != ERROR_SUCCESS)
6820        return r;
6821
6822     if (!(flags & ENV_ACT_REMOVE))
6823     {
6824         TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6825         return ERROR_SUCCESS;
6826     }
6827
6828     if (value && !deformat_string( package, value, &deformatted ))
6829         return ERROR_OUTOFMEMORY;
6830
6831     value = deformatted;
6832
6833     r = open_env_key( flags, &env );
6834     if (r != ERROR_SUCCESS)
6835     {
6836         r = ERROR_SUCCESS;
6837         goto done;
6838     }
6839
6840     if (flags & ENV_MOD_MACHINE)
6841         action |= 0x20000000;
6842
6843     TRACE("Removing %s\n", debugstr_w(name));
6844
6845     res = RegDeleteValueW( env, name );
6846     if (res != ERROR_SUCCESS)
6847     {
6848         WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6849         r = ERROR_SUCCESS;
6850     }
6851
6852 done:
6853     uirow = MSI_CreateRecord( 3 );
6854     MSI_RecordSetStringW( uirow, 1, name );
6855     MSI_RecordSetStringW( uirow, 2, value );
6856     MSI_RecordSetInteger( uirow, 3, action );
6857     msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6858     msiobj_release( &uirow->hdr );
6859
6860     if (env) RegCloseKey( env );
6861     msi_free( deformatted );
6862     return r;
6863 }
6864
6865 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6866 {
6867     static const WCHAR query[] = {
6868         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6869         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6870     MSIQUERY *view;
6871     UINT rc;
6872
6873     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6874     if (rc != ERROR_SUCCESS)
6875         return ERROR_SUCCESS;
6876
6877     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6878     msiobj_release( &view->hdr );
6879     return rc;
6880 }
6881
6882 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6883 {
6884     LPWSTR key, template, id;
6885     UINT r = ERROR_SUCCESS;
6886
6887     id = msi_dup_property( package->db, szProductID );
6888     if (id)
6889     {
6890         msi_free( id );
6891         return ERROR_SUCCESS;
6892     }
6893     template = msi_dup_property( package->db, szPIDTemplate );
6894     key = msi_dup_property( package->db, szPIDKEY );
6895
6896     if (key && template)
6897     {
6898         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6899         r = msi_set_property( package->db, szProductID, key );
6900     }
6901     msi_free( template );
6902     msi_free( key );
6903     return r;
6904 }
6905
6906 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6907 {
6908     TRACE("\n");
6909     package->need_reboot = 1;
6910     return ERROR_SUCCESS;
6911 }
6912
6913 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6914 {
6915     static const WCHAR szAvailableFreeReg[] =
6916         {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6917     MSIRECORD *uirow;
6918     int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6919
6920     TRACE("%p %d kilobytes\n", package, space);
6921
6922     uirow = MSI_CreateRecord( 1 );
6923     MSI_RecordSetInteger( uirow, 1, space );
6924     msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6925     msiobj_release( &uirow->hdr );
6926
6927     return ERROR_SUCCESS;
6928 }
6929
6930 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6931 {
6932     TRACE("%p\n", package);
6933
6934     msi_set_property( package->db, szRollbackDisabled, szOne );
6935     return ERROR_SUCCESS;
6936 }
6937
6938 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6939 {
6940     FIXME("%p\n", package);
6941     return ERROR_SUCCESS;
6942 }
6943
6944 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6945 {
6946     static const WCHAR driver_query[] = {
6947         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6948         'O','D','B','C','D','r','i','v','e','r',0};
6949     static const WCHAR translator_query[] = {
6950         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6951         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6952     MSIQUERY *view;
6953     UINT r, count;
6954
6955     r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6956     if (r == ERROR_SUCCESS)
6957     {
6958         count = 0;
6959         r = MSI_IterateRecords( view, &count, NULL, package );
6960         msiobj_release( &view->hdr );
6961         if (r != ERROR_SUCCESS)
6962             return r;
6963         if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6964     }
6965     r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6966     if (r == ERROR_SUCCESS)
6967     {
6968         count = 0;
6969         r = MSI_IterateRecords( view, &count, NULL, package );
6970         msiobj_release( &view->hdr );
6971         if (r != ERROR_SUCCESS)
6972             return r;
6973         if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6974     }
6975     return ERROR_SUCCESS;
6976 }
6977
6978 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6979 {
6980     MSIPACKAGE *package = param;
6981     const WCHAR *property = MSI_RecordGetString( rec, 1 );
6982     WCHAR *value;
6983
6984     if ((value = msi_dup_property( package->db, property )))
6985     {
6986         FIXME("remove %s\n", debugstr_w(value));
6987         msi_free( value );
6988     }
6989     return ERROR_SUCCESS;
6990 }
6991
6992 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6993 {
6994     static const WCHAR query[] = {
6995         'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6996         'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6997     MSIQUERY *view;
6998     UINT r;
6999
7000     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7001     if (r == ERROR_SUCCESS)
7002     {
7003         r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7004         msiobj_release( &view->hdr );
7005         if (r != ERROR_SUCCESS)
7006             return r;
7007     }
7008     return ERROR_SUCCESS;
7009 }
7010
7011 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7012 {
7013     MSIPACKAGE *package = param;
7014     int attributes = MSI_RecordGetInteger( rec, 5 );
7015
7016     if (attributes & msidbUpgradeAttributesMigrateFeatures)
7017     {
7018         const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7019         const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7020         const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7021         const WCHAR *language = MSI_RecordGetString( rec, 4 );
7022         HKEY hkey;
7023         UINT r;
7024
7025         if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7026         {
7027             r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7028             if (r != ERROR_SUCCESS)
7029                 return ERROR_SUCCESS;
7030         }
7031         else
7032         {
7033             r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7034             if (r != ERROR_SUCCESS)
7035                 return ERROR_SUCCESS;
7036         }
7037         RegCloseKey( hkey );
7038
7039         FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7040               debugstr_w(upgrade_code), debugstr_w(version_min),
7041               debugstr_w(version_max), debugstr_w(language));
7042     }
7043     return ERROR_SUCCESS;
7044 }
7045
7046 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7047 {
7048     static const WCHAR query[] = {
7049         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7050         'U','p','g','r','a','d','e',0};
7051     MSIQUERY *view;
7052     UINT r;
7053
7054     if (msi_get_property_int( package->db, szInstalled, 0 ))
7055     {
7056         TRACE("product is installed, skipping action\n");
7057         return ERROR_SUCCESS;
7058     }
7059     if (msi_get_property_int( package->db, szPreselected, 0 ))
7060     {
7061         TRACE("Preselected property is set, not migrating feature states\n");
7062         return ERROR_SUCCESS;
7063     }
7064     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7065     if (r == ERROR_SUCCESS)
7066     {
7067         r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7068         msiobj_release( &view->hdr );
7069         if (r != ERROR_SUCCESS)
7070             return r;
7071     }
7072     return ERROR_SUCCESS;
7073 }
7074
7075 static void bind_image( const char *filename, const char *path )
7076 {
7077     if (!BindImageEx( 0, filename, path, NULL, NULL ))
7078     {
7079         WARN("failed to bind image %u\n", GetLastError());
7080     }
7081 }
7082
7083 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7084 {
7085     UINT i;
7086     MSIFILE *file;
7087     MSIPACKAGE *package = param;
7088     const WCHAR *key = MSI_RecordGetString( rec, 1 );
7089     const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7090     char *filenameA, *pathA;
7091     WCHAR *pathW, **path_list;
7092
7093     if (!(file = msi_get_loaded_file( package, key )))
7094     {
7095         WARN("file %s not found\n", debugstr_w(key));
7096         return ERROR_SUCCESS;
7097     }
7098     if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7099     path_list = msi_split_string( paths, ';' );
7100     if (!path_list) bind_image( filenameA, NULL );
7101     else
7102     {
7103         for (i = 0; path_list[i] && path_list[i][0]; i++)
7104         {
7105             deformat_string( package, path_list[i], &pathW );
7106             if ((pathA = strdupWtoA( pathW )))
7107             {
7108                 bind_image( filenameA, pathA );
7109                 msi_free( pathA );
7110             }
7111             msi_free( pathW );
7112         }
7113     }
7114     msi_free( path_list );
7115     msi_free( filenameA );
7116     return ERROR_SUCCESS;
7117 }
7118
7119 static UINT ACTION_BindImage( MSIPACKAGE *package )
7120 {
7121     static const WCHAR query[] = {
7122         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7123         'B','i','n','d','I','m','a','g','e',0};
7124     MSIQUERY *view;
7125     UINT r;
7126
7127     r = MSI_DatabaseOpenViewW( package->db, query, &view );
7128     if (r == ERROR_SUCCESS)
7129     {
7130         r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7131         msiobj_release( &view->hdr );
7132         if (r != ERROR_SUCCESS)
7133             return r;
7134     }
7135     return ERROR_SUCCESS;
7136 }
7137
7138 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7139 {
7140     static const WCHAR query[] = {
7141         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7142     MSIQUERY *view;
7143     DWORD count = 0;
7144     UINT r;
7145     
7146     r = MSI_OpenQuery( package->db, &view, query, table );
7147     if (r == ERROR_SUCCESS)
7148     {
7149         r = MSI_IterateRecords(view, &count, NULL, package);
7150         msiobj_release(&view->hdr);
7151         if (r != ERROR_SUCCESS)
7152             return r;
7153     }
7154     if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7155     return ERROR_SUCCESS;
7156 }
7157
7158 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7159 {
7160     static const WCHAR table[] = {
7161         'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7162     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7163 }
7164
7165 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7166 {
7167     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7168     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7169 }
7170
7171 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7172 {
7173     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7174     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7175 }
7176
7177 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7178 {
7179     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7180     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7181 }
7182
7183 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7184 {
7185     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7186     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7187 }
7188
7189 static const struct
7190 {
7191     const WCHAR *action;
7192     UINT (*handler)(MSIPACKAGE *);
7193     const WCHAR *action_rollback;
7194 }
7195 StandardActions[] =
7196 {
7197     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7198     { szAppSearch, ACTION_AppSearch, NULL },
7199     { szBindImage, ACTION_BindImage, NULL },
7200     { szCCPSearch, ACTION_CCPSearch, NULL },
7201     { szCostFinalize, ACTION_CostFinalize, NULL },
7202     { szCostInitialize, ACTION_CostInitialize, NULL },
7203     { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7204     { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7205     { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7206     { szDisableRollback, ACTION_DisableRollback, NULL },
7207     { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7208     { szExecuteAction, ACTION_ExecuteAction, NULL },
7209     { szFileCost, ACTION_FileCost, NULL },
7210     { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7211     { szForceReboot, ACTION_ForceReboot, NULL },
7212     { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7213     { szInstallExecute, ACTION_InstallExecute, NULL },
7214     { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7215     { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7216     { szInstallFinalize, ACTION_InstallFinalize, NULL },
7217     { szInstallInitialize, ACTION_InstallInitialize, NULL },
7218     { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7219     { szInstallServices, ACTION_InstallServices, szDeleteServices },
7220     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7221     { szInstallValidate, ACTION_InstallValidate, NULL },
7222     { szIsolateComponents, ACTION_IsolateComponents, NULL },
7223     { szLaunchConditions, ACTION_LaunchConditions, NULL },
7224     { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7225     { szMoveFiles, ACTION_MoveFiles, NULL },
7226     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7227     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7228     { szPatchFiles, ACTION_PatchFiles, NULL },
7229     { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7230     { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7231     { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7232     { szPublishProduct, ACTION_PublishProduct, NULL },
7233     { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7234     { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7235     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7236     { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7237     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7238     { szRegisterProduct, ACTION_RegisterProduct, NULL },
7239     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7240     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7241     { szRegisterUser, ACTION_RegisterUser, NULL },
7242     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7243     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7244     { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7245     { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7246     { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7247     { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7248     { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7249     { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7250     { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7251     { szResolveSource, ACTION_ResolveSource, NULL },
7252     { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7253     { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7254     { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7255     { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7256     { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7257     { szStartServices, ACTION_StartServices, szStopServices },
7258     { szStopServices, ACTION_StopServices, szStartServices },
7259     { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7260     { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7261     { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7262     { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7263     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7264     { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7265     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7266     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7267     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7268     { szValidateProductID, ACTION_ValidateProductID, NULL },
7269     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7270     { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7271     { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7272     { NULL, NULL, NULL }
7273 };
7274
7275 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7276 {
7277     BOOL ret = FALSE;
7278     UINT i;
7279
7280     i = 0;
7281     while (StandardActions[i].action != NULL)
7282     {
7283         if (!strcmpW( StandardActions[i].action, action ))
7284         {
7285             ui_actionstart( package, action );
7286             if (StandardActions[i].handler)
7287             {
7288                 ui_actioninfo( package, action, TRUE, 0 );
7289                 *rc = StandardActions[i].handler( package );
7290                 ui_actioninfo( package, action, FALSE, *rc );
7291
7292                 if (StandardActions[i].action_rollback && !package->need_rollback)
7293                 {
7294                     TRACE("scheduling rollback action\n");
7295                     msi_schedule_action( package, ROLLBACK_SCRIPT, StandardActions[i].action_rollback );
7296                 }
7297             }
7298             else
7299             {
7300                 FIXME("unhandled standard action %s\n", debugstr_w(action));
7301                 *rc = ERROR_SUCCESS;
7302             }
7303             ret = TRUE;
7304             break;
7305         }
7306         i++;
7307     }
7308     return ret;
7309 }
7310
7311 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7312 {
7313     UINT rc = ERROR_SUCCESS;
7314     BOOL handled;
7315
7316     TRACE("Performing action (%s)\n", debugstr_w(action));
7317
7318     handled = ACTION_HandleStandardAction(package, action, &rc);
7319
7320     if (!handled)
7321         handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7322
7323     if (!handled)
7324     {
7325         WARN("unhandled msi action %s\n", debugstr_w(action));
7326         rc = ERROR_FUNCTION_NOT_CALLED;
7327     }
7328
7329     return rc;
7330 }
7331
7332 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7333 {
7334     UINT rc = ERROR_SUCCESS;
7335     BOOL handled = FALSE;
7336
7337     TRACE("Performing action (%s)\n", debugstr_w(action));
7338
7339     handled = ACTION_HandleStandardAction(package, action, &rc);
7340
7341     if (!handled)
7342         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7343
7344     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7345         handled = TRUE;
7346
7347     if (!handled)
7348     {
7349         WARN("unhandled msi action %s\n", debugstr_w(action));
7350         rc = ERROR_FUNCTION_NOT_CALLED;
7351     }
7352
7353     return rc;
7354 }
7355
7356 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7357 {
7358     UINT rc = ERROR_SUCCESS;
7359     MSIRECORD *row;
7360
7361     static const WCHAR query[] =
7362         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7363          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7364          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7365          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7366     static const WCHAR ui_query[] =
7367         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7368      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7369      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7370          ' ', '=',' ','%','i',0};
7371
7372     if (needs_ui_sequence(package))
7373         row = MSI_QueryGetRecord(package->db, ui_query, seq);
7374     else
7375         row = MSI_QueryGetRecord(package->db, query, seq);
7376
7377     if (row)
7378     {
7379         LPCWSTR action, cond;
7380
7381         TRACE("Running the actions\n");
7382
7383         /* check conditions */
7384         cond = MSI_RecordGetString(row, 2);
7385
7386         /* this is a hack to skip errors in the condition code */
7387         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7388         {
7389             msiobj_release(&row->hdr);
7390             return ERROR_SUCCESS;
7391         }
7392
7393         action = MSI_RecordGetString(row, 1);
7394         if (!action)
7395         {
7396             ERR("failed to fetch action\n");
7397             msiobj_release(&row->hdr);
7398             return ERROR_FUNCTION_FAILED;
7399         }
7400
7401         if (needs_ui_sequence(package))
7402             rc = ACTION_PerformUIAction(package, action, -1);
7403         else
7404             rc = ACTION_PerformAction(package, action, -1);
7405
7406         msiobj_release(&row->hdr);
7407     }
7408
7409     return rc;
7410 }
7411
7412 /****************************************************
7413  * TOP level entry points
7414  *****************************************************/
7415
7416 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7417                          LPCWSTR szCommandLine )
7418 {
7419     UINT rc;
7420     BOOL ui_exists;
7421     static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7422     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7423     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7424
7425     msi_set_property( package->db, szAction, szInstall );
7426
7427     package->script->InWhatSequence = SEQUENCE_INSTALL;
7428
7429     if (szPackagePath)
7430     {
7431         LPWSTR p, dir;
7432         LPCWSTR file;
7433
7434         dir = strdupW(szPackagePath);
7435         p = strrchrW(dir, '\\');
7436         if (p)
7437         {
7438             *(++p) = 0;
7439             file = szPackagePath + (p - dir);
7440         }
7441         else
7442         {
7443             msi_free(dir);
7444             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7445             GetCurrentDirectoryW(MAX_PATH, dir);
7446             lstrcatW(dir, szBackSlash);
7447             file = szPackagePath;
7448         }
7449
7450         msi_free( package->PackagePath );
7451         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7452         if (!package->PackagePath)
7453         {
7454             msi_free(dir);
7455             return ERROR_OUTOFMEMORY;
7456         }
7457
7458         lstrcpyW(package->PackagePath, dir);
7459         lstrcatW(package->PackagePath, file);
7460         msi_free(dir);
7461
7462         msi_set_sourcedir_props(package, FALSE);
7463     }
7464
7465     rc = msi_parse_command_line( package, szCommandLine, FALSE );
7466     if (rc != ERROR_SUCCESS)
7467         return rc;
7468
7469     msi_apply_transforms( package );
7470     msi_apply_patches( package );
7471
7472     if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7473     {
7474         TRACE("setting reinstall property\n");
7475         msi_set_property( package->db, szReinstall, szAll );
7476     }
7477
7478     /* properties may have been added by a transform */
7479     msi_clone_properties( package );
7480
7481     msi_parse_command_line( package, szCommandLine, FALSE );
7482     msi_adjust_privilege_properties( package );
7483     msi_set_context( package );
7484
7485     if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7486     {
7487         TRACE("disabling rollback\n");
7488         msi_set_property( package->db, szRollbackDisabled, szOne );
7489     }
7490
7491     if (needs_ui_sequence( package))
7492     {
7493         package->script->InWhatSequence |= SEQUENCE_UI;
7494         rc = ACTION_ProcessUISequence(package);
7495         ui_exists = ui_sequence_exists(package);
7496         if (rc == ERROR_SUCCESS || !ui_exists)
7497         {
7498             package->script->InWhatSequence |= SEQUENCE_EXEC;
7499             rc = ACTION_ProcessExecSequence(package, ui_exists);
7500         }
7501     }
7502     else
7503         rc = ACTION_ProcessExecSequence(package, FALSE);
7504
7505     package->script->CurrentlyScripting = FALSE;
7506
7507     /* process the ending type action */
7508     if (rc == ERROR_SUCCESS)
7509         ACTION_PerformActionSequence(package, -1);
7510     else if (rc == ERROR_INSTALL_USEREXIT)
7511         ACTION_PerformActionSequence(package, -2);
7512     else if (rc == ERROR_INSTALL_SUSPEND)
7513         ACTION_PerformActionSequence(package, -4);
7514     else  /* failed */
7515     {
7516         ACTION_PerformActionSequence(package, -3);
7517         if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7518         {
7519             package->need_rollback = TRUE;
7520         }
7521     }
7522
7523     /* finish up running custom actions */
7524     ACTION_FinishCustomActions(package);
7525
7526     if (package->need_rollback)
7527     {
7528         WARN("installation failed, running rollback script\n");
7529         execute_script( package, ROLLBACK_SCRIPT );
7530     }
7531
7532     if (rc == ERROR_SUCCESS && package->need_reboot)
7533         return ERROR_SUCCESS_REBOOT_REQUIRED;
7534
7535     return rc;
7536 }