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