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