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