winmm: Fix a failing mixer test on 98 and ME.
[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 (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1485     {
1486         file->IsCompressed = FALSE;
1487     }
1488     else if (file->Attributes &
1489              (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1490     {
1491         file->IsCompressed = TRUE;
1492     }
1493     else if (file->Attributes & msidbFileAttributesNoncompressed)
1494     {
1495         file->IsCompressed = FALSE;
1496     }
1497     else
1498     {
1499         file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1500     }
1501
1502     load_file_hash(package, file);
1503
1504     TRACE("File Loaded (%s)\n",debugstr_w(file->File));  
1505
1506     list_add_tail( &package->files, &file->entry );
1507  
1508     return ERROR_SUCCESS;
1509 }
1510
1511 static UINT load_all_files(MSIPACKAGE *package)
1512 {
1513     MSIQUERY * view;
1514     UINT rc;
1515     static const WCHAR Query[] =
1516         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1517          '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1518          '`','S','e','q','u','e','n','c','e','`', 0};
1519
1520     if (!list_empty(&package->files))
1521         return ERROR_SUCCESS;
1522
1523     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1524     if (rc != ERROR_SUCCESS)
1525         return ERROR_SUCCESS;
1526
1527     rc = MSI_IterateRecords(view, NULL, load_file, package);
1528     msiobj_release(&view->hdr);
1529
1530     return ERROR_SUCCESS;
1531 }
1532
1533 static UINT load_folder( MSIRECORD *row, LPVOID param )
1534 {
1535     MSIPACKAGE *package = param;
1536     static const WCHAR szDot[] = { '.',0 };
1537     static WCHAR szEmpty[] = { 0 };
1538     LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1539     MSIFOLDER *folder;
1540
1541     folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1542     if (!folder)
1543         return ERROR_NOT_ENOUGH_MEMORY;
1544
1545     folder->Directory = msi_dup_record_field( row, 1 );
1546
1547     TRACE("%s\n", debugstr_w(folder->Directory));
1548
1549     p = msi_dup_record_field(row, 3);
1550
1551     /* split src and target dir */
1552     tgt_short = p;
1553     src_short = folder_split_path( p, ':' );
1554
1555     /* split the long and short paths */
1556     tgt_long = folder_split_path( tgt_short, '|' );
1557     src_long = folder_split_path( src_short, '|' );
1558
1559     /* check for no-op dirs */
1560     if (!lstrcmpW(szDot, tgt_short))
1561         tgt_short = szEmpty;
1562     if (!lstrcmpW(szDot, src_short))
1563         src_short = szEmpty;
1564
1565     if (!tgt_long)
1566         tgt_long = tgt_short;
1567
1568     if (!src_short) {
1569         src_short = tgt_short;
1570         src_long = tgt_long;
1571     }
1572
1573     if (!src_long)
1574         src_long = src_short;
1575
1576     /* FIXME: use the target short path too */
1577     folder->TargetDefault = strdupW(tgt_long);
1578     folder->SourceShortPath = strdupW(src_short);
1579     folder->SourceLongPath = strdupW(src_long);
1580     msi_free(p);
1581
1582     TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1583     TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1584     TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1585
1586     folder->Parent = msi_dup_record_field( row, 2 );
1587
1588     folder->Property = msi_dup_property( package, folder->Directory );
1589
1590     list_add_tail( &package->folders, &folder->entry );
1591
1592     TRACE("returning %p\n", folder);
1593
1594     return ERROR_SUCCESS;
1595 }
1596
1597 static UINT load_all_folders( MSIPACKAGE *package )
1598 {
1599     static const WCHAR query[] = {
1600         'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1601          '`','D','i','r','e','c','t','o','r','y','`',0 };
1602     MSIQUERY *view;
1603     UINT r;
1604
1605     if (!list_empty(&package->folders))
1606         return ERROR_SUCCESS;
1607
1608     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1609     if (r != ERROR_SUCCESS)
1610         return r;
1611
1612     r = MSI_IterateRecords(view, NULL, load_folder, package);
1613     msiobj_release(&view->hdr);
1614     return r;
1615 }
1616
1617 /*
1618  * I am not doing any of the costing functionality yet.
1619  * Mostly looking at doing the Component and Feature loading
1620  *
1621  * The native MSI does A LOT of modification to tables here. Mostly adding
1622  * a lot of temporary columns to the Feature and Component tables.
1623  *
1624  *    note: Native msi also tracks the short filename. But I am only going to
1625  *          track the long ones.  Also looking at this directory table
1626  *          it appears that the directory table does not get the parents
1627  *          resolved base on property only based on their entries in the
1628  *          directory table.
1629  */
1630 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1631 {
1632     static const WCHAR szCosting[] =
1633         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1634     static const WCHAR szZero[] = { '0', 0 };
1635
1636     MSI_SetPropertyW(package, szCosting, szZero);
1637     MSI_SetPropertyW(package, cszRootDrive, c_colon);
1638
1639     load_all_folders( package );
1640     load_all_components( package );
1641     load_all_features( package );
1642     load_all_files( package );
1643
1644     return ERROR_SUCCESS;
1645 }
1646
1647 static UINT execute_script(MSIPACKAGE *package, UINT script )
1648 {
1649     UINT i;
1650     UINT rc = ERROR_SUCCESS;
1651
1652     TRACE("Executing Script %i\n",script);
1653
1654     if (!package->script)
1655     {
1656         ERR("no script!\n");
1657         return ERROR_FUNCTION_FAILED;
1658     }
1659
1660     for (i = 0; i < package->script->ActionCount[script]; i++)
1661     {
1662         LPWSTR action;
1663         action = package->script->Actions[script][i];
1664         ui_actionstart(package, action);
1665         TRACE("Executing Action (%s)\n",debugstr_w(action));
1666         rc = ACTION_PerformAction(package, action, script, TRUE);
1667         if (rc != ERROR_SUCCESS)
1668             break;
1669     }
1670     msi_free_action_script(package, script);
1671     return rc;
1672 }
1673
1674 static UINT ACTION_FileCost(MSIPACKAGE *package)
1675 {
1676     return ERROR_SUCCESS;
1677 }
1678
1679 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1680 {
1681     MSICOMPONENT *comp;
1682     INSTALLSTATE state;
1683     UINT r;
1684
1685     state = MsiQueryProductStateW(package->ProductCode);
1686
1687     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1688     {
1689         if (!comp->ComponentId)
1690             continue;
1691
1692         if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1693             comp->Installed = INSTALLSTATE_ABSENT;
1694         else
1695         {
1696             r = MsiQueryComponentStateW(package->ProductCode, NULL,
1697                                         package->Context, comp->ComponentId,
1698                                         &comp->Installed);
1699             if (r != ERROR_SUCCESS)
1700                 comp->Installed = INSTALLSTATE_ABSENT;
1701         }
1702     }
1703 }
1704
1705 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1706 {
1707     MSIFEATURE *feature;
1708     INSTALLSTATE state;
1709
1710     state = MsiQueryProductStateW(package->ProductCode);
1711
1712     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1713     {
1714         if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1715             feature->Installed = INSTALLSTATE_ABSENT;
1716         else
1717         {
1718             feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1719                                                        feature->Feature);
1720         }
1721     }
1722 }
1723
1724 static BOOL process_state_property(MSIPACKAGE* package, int level,
1725                                    LPCWSTR property, INSTALLSTATE state)
1726 {
1727     static const WCHAR all[]={'A','L','L',0};
1728     static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1729     LPWSTR override;
1730     MSIFEATURE *feature;
1731
1732     override = msi_dup_property( package, property );
1733     if (!override)
1734         return FALSE;
1735
1736     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1737     {
1738         if (lstrcmpW(property, remove) &&
1739             (feature->Level <= 0 || feature->Level > level))
1740             continue;
1741
1742         if (strcmpiW(override,all)==0)
1743             msi_feature_set_state(package, feature, state);
1744         else
1745         {
1746             LPWSTR ptr = override;
1747             LPWSTR ptr2 = strchrW(override,',');
1748
1749             while (ptr)
1750             {
1751                 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1752                     || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1753                 {
1754                     msi_feature_set_state(package, feature, state);
1755                     break;
1756                 }
1757                 if (ptr2)
1758                 {
1759                     ptr=ptr2+1;
1760                     ptr2 = strchrW(ptr,',');
1761                 }
1762                 else
1763                     break;
1764             }
1765         }
1766     }
1767     msi_free(override);
1768
1769     return TRUE;
1770 }
1771
1772 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1773 {
1774     int level;
1775     static const WCHAR szlevel[] =
1776         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1777     static const WCHAR szAddLocal[] =
1778         {'A','D','D','L','O','C','A','L',0};
1779     static const WCHAR szAddSource[] =
1780         {'A','D','D','S','O','U','R','C','E',0};
1781     static const WCHAR szRemove[] =
1782         {'R','E','M','O','V','E',0};
1783     static const WCHAR szReinstall[] =
1784         {'R','E','I','N','S','T','A','L','L',0};
1785     BOOL override = FALSE;
1786     MSICOMPONENT* component;
1787     MSIFEATURE *feature;
1788
1789
1790     /* I do not know if this is where it should happen.. but */
1791
1792     TRACE("Checking Install Level\n");
1793
1794     level = msi_get_property_int(package, szlevel, 1);
1795
1796     /* ok here is the _real_ rub
1797      * all these activation/deactivation things happen in order and things
1798      * later on the list override things earlier on the list.
1799      * 1) INSTALLLEVEL processing
1800      * 2) ADDLOCAL
1801      * 3) REMOVE
1802      * 4) ADDSOURCE
1803      * 5) ADDDEFAULT
1804      * 6) REINSTALL
1805      * 7) COMPADDLOCAL
1806      * 8) COMPADDSOURCE
1807      * 9) FILEADDLOCAL
1808      * 10) FILEADDSOURCE
1809      * 11) FILEADDDEFAULT
1810      *
1811      * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1812      * REMOVE are the big ones, since we don't handle administrative installs
1813      * yet anyway.
1814      */
1815     override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1816     override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1817     override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1818     override |= process_state_property(package, level, szReinstall, INSTALLSTATE_LOCAL);
1819
1820     if (!override)
1821     {
1822         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1823         {
1824             BOOL feature_state = ((feature->Level > 0) &&
1825                                   (feature->Level <= level));
1826
1827             if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1828             {
1829                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1830                     msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1831                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1832                     msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1833                 else
1834                     msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1835             }
1836         }
1837
1838         /* disable child features of unselected parent features */
1839         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1840         {
1841             FeatureList *fl;
1842
1843             if (feature->Level > 0 && feature->Level <= level)
1844                 continue;
1845
1846             LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1847                 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1848         }
1849     }
1850     else
1851     {
1852         /* set the Preselected Property */
1853         static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1854         static const WCHAR szOne[] = { '1', 0 };
1855
1856         MSI_SetPropertyW(package,szPreselected,szOne);
1857     }
1858
1859     /*
1860      * now we want to enable or disable components base on feature
1861      */
1862
1863     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1864     {
1865         ComponentList *cl;
1866
1867         TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1868               debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1869
1870         if (!feature->Level)
1871             continue;
1872
1873         /* features with components that have compressed files are made local */
1874         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1875         {
1876             if (cl->component->Enabled &&
1877                 cl->component->ForceLocalState &&
1878                 feature->Action == INSTALLSTATE_SOURCE)
1879             {
1880                 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1881                 break;
1882             }
1883         }
1884
1885         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1886         {
1887             component = cl->component;
1888
1889             if (!component->Enabled)
1890                 continue;
1891
1892             switch (feature->Action)
1893             {
1894             case INSTALLSTATE_ABSENT:
1895                 component->anyAbsent = 1;
1896                 break;
1897             case INSTALLSTATE_ADVERTISED:
1898                 component->hasAdvertiseFeature = 1;
1899                 break;
1900             case INSTALLSTATE_SOURCE:
1901                 component->hasSourceFeature = 1;
1902                 break;
1903             case INSTALLSTATE_LOCAL:
1904                 component->hasLocalFeature = 1;
1905                 break;
1906             case INSTALLSTATE_DEFAULT:
1907                 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1908                     component->hasAdvertiseFeature = 1;
1909                 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1910                     component->hasSourceFeature = 1;
1911                 else
1912                     component->hasLocalFeature = 1;
1913                 break;
1914             default:
1915                 break;
1916             }
1917         }
1918     }
1919
1920     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1921     {
1922         /* if the component isn't enabled, leave it alone */
1923         if (!component->Enabled)
1924             continue;
1925
1926         /* check if it's local or source */
1927         if (!(component->Attributes & msidbComponentAttributesOptional) &&
1928              (component->hasLocalFeature || component->hasSourceFeature))
1929         {
1930             if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1931                  !component->ForceLocalState)
1932                 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1933             else
1934                 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1935             continue;
1936         }
1937
1938         /* if any feature is local, the component must be local too */
1939         if (component->hasLocalFeature)
1940         {
1941             msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1942             continue;
1943         }
1944
1945         if (component->hasSourceFeature)
1946         {
1947             msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1948             continue;
1949         }
1950
1951         if (component->hasAdvertiseFeature)
1952         {
1953             msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1954             continue;
1955         }
1956
1957         TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1958         if (component->anyAbsent)
1959             msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1960     }
1961
1962     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1963     {
1964         if (component->Action == INSTALLSTATE_DEFAULT)
1965         {
1966             TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1967             msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1968         }
1969
1970         TRACE("Result: Component %s (Installed %i, Action %i)\n",
1971             debugstr_w(component->Component), component->Installed, component->Action);
1972     }
1973
1974
1975     return ERROR_SUCCESS;
1976 }
1977
1978 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1979 {
1980     MSIPACKAGE *package = (MSIPACKAGE*)param;
1981     LPCWSTR name;
1982     LPWSTR path;
1983     MSIFOLDER *f;
1984
1985     name = MSI_RecordGetString(row,1);
1986
1987     f = get_loaded_folder(package, name);
1988     if (!f) return ERROR_SUCCESS;
1989
1990     /* reset the ResolvedTarget */
1991     msi_free(f->ResolvedTarget);
1992     f->ResolvedTarget = NULL;
1993
1994     /* This helper function now does ALL the work */
1995     TRACE("Dir %s ...\n",debugstr_w(name));
1996     path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1997     TRACE("resolves to %s\n",debugstr_w(path));
1998     msi_free(path);
1999
2000     return ERROR_SUCCESS;
2001 }
2002
2003 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2004 {
2005     MSIPACKAGE *package = (MSIPACKAGE*)param;
2006     LPCWSTR name;
2007     MSIFEATURE *feature;
2008
2009     name = MSI_RecordGetString( row, 1 );
2010
2011     feature = get_loaded_feature( package, name );
2012     if (!feature)
2013         ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2014     else
2015     {
2016         LPCWSTR Condition;
2017         Condition = MSI_RecordGetString(row,3);
2018
2019         if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2020         {
2021             int level = MSI_RecordGetInteger(row,2);
2022             TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2023             feature->Level = level;
2024         }
2025     }
2026     return ERROR_SUCCESS;
2027 }
2028
2029 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2030 {
2031     static const WCHAR name_fmt[] =
2032         {'%','u','.','%','u','.','%','u','.','%','u',0};
2033     static const WCHAR name[] = {'\\',0};
2034     VS_FIXEDFILEINFO *lpVer;
2035     WCHAR filever[0x100];
2036     LPVOID version;
2037     DWORD versize;
2038     DWORD handle;
2039     UINT sz;
2040
2041     TRACE("%s\n", debugstr_w(filename));
2042
2043     versize = GetFileVersionInfoSizeW( filename, &handle );
2044     if (!versize)
2045         return NULL;
2046
2047     version = msi_alloc( versize );
2048     GetFileVersionInfoW( filename, 0, versize, version );
2049
2050     if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2051     {
2052         msi_free( version );
2053         return NULL;
2054     }
2055
2056     sprintfW( filever, name_fmt,
2057         HIWORD(lpVer->dwFileVersionMS),
2058         LOWORD(lpVer->dwFileVersionMS),
2059         HIWORD(lpVer->dwFileVersionLS),
2060         LOWORD(lpVer->dwFileVersionLS));
2061
2062     msi_free( version );
2063
2064     return strdupW( filever );
2065 }
2066
2067 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2068 {
2069     LPWSTR file_version;
2070     MSIFILE *file;
2071
2072     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2073     {
2074         MSICOMPONENT* comp = file->Component;
2075         LPWSTR p;
2076
2077         if (!comp)
2078             continue;
2079
2080         if (file->IsCompressed)
2081             comp->ForceLocalState = TRUE;
2082
2083         /* calculate target */
2084         p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2085
2086         msi_free(file->TargetPath);
2087
2088         TRACE("file %s is named %s\n",
2089                debugstr_w(file->File), debugstr_w(file->FileName));
2090
2091         file->TargetPath = build_directory_name(2, p, file->FileName);
2092
2093         msi_free(p);
2094
2095         TRACE("file %s resolves to %s\n",
2096                debugstr_w(file->File), debugstr_w(file->TargetPath));
2097
2098         /* don't check files of components that aren't installed */
2099         if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2100             comp->Installed == INSTALLSTATE_ABSENT)
2101         {
2102             file->state = msifs_missing;  /* assume files are missing */
2103             continue;
2104         }
2105
2106         if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2107         {
2108             file->state = msifs_missing;
2109             comp->Cost += file->FileSize;
2110             continue;
2111         }
2112
2113         if (file->Version &&
2114             (file_version = msi_get_disk_file_version( file->TargetPath )))
2115         {
2116             TRACE("new %s old %s\n", debugstr_w(file->Version),
2117                   debugstr_w(file_version));
2118             /* FIXME: seems like a bad way to compare version numbers */
2119             if (lstrcmpiW(file_version, file->Version)<0)
2120             {
2121                 file->state = msifs_overwrite;
2122                 comp->Cost += file->FileSize;
2123             }
2124             else
2125                 file->state = msifs_present;
2126             msi_free( file_version );
2127         }
2128         else
2129             file->state = msifs_present;
2130     }
2131
2132     return ERROR_SUCCESS;
2133 }
2134
2135 /*
2136  * A lot is done in this function aside from just the costing.
2137  * The costing needs to be implemented at some point but for now I am going
2138  * to focus on the directory building
2139  *
2140  */
2141 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2142 {
2143     static const WCHAR ExecSeqQuery[] =
2144         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2145          '`','D','i','r','e','c','t','o','r','y','`',0};
2146     static const WCHAR ConditionQuery[] =
2147         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2148          '`','C','o','n','d','i','t','i','o','n','`',0};
2149     static const WCHAR szCosting[] =
2150         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2151     static const WCHAR szlevel[] =
2152         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2153     static const WCHAR szOutOfDiskSpace[] =
2154         {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2155     static const WCHAR szOne[] = { '1', 0 };
2156     static const WCHAR szZero[] = { '0', 0 };
2157     MSICOMPONENT *comp;
2158     UINT rc;
2159     MSIQUERY * view;
2160     LPWSTR level;
2161
2162     TRACE("Building Directory properties\n");
2163
2164     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2165     if (rc == ERROR_SUCCESS)
2166     {
2167         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2168                         package);
2169         msiobj_release(&view->hdr);
2170     }
2171
2172     /* read components states from the registry */
2173     ACTION_GetComponentInstallStates(package);
2174     ACTION_GetFeatureInstallStates(package);
2175
2176     TRACE("File calculations\n");
2177     msi_check_file_install_states( package );
2178
2179     TRACE("Evaluating Condition Table\n");
2180
2181     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2182     if (rc == ERROR_SUCCESS)
2183     {
2184         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2185                     package);
2186         msiobj_release(&view->hdr);
2187     }
2188
2189     TRACE("Enabling or Disabling Components\n");
2190     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2191     {
2192         if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2193         {
2194             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2195             comp->Enabled = FALSE;
2196         }
2197         else
2198             comp->Enabled = TRUE;
2199     }
2200
2201     MSI_SetPropertyW(package,szCosting,szOne);
2202     /* set default run level if not set */
2203     level = msi_dup_property( package, szlevel );
2204     if (!level)
2205         MSI_SetPropertyW(package,szlevel, szOne);
2206     msi_free(level);
2207
2208     /* FIXME: check volume disk space */
2209     MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2210
2211     return MSI_SetFeatureStates(package);
2212 }
2213
2214 /* OK this value is "interpreted" and then formatted based on the 
2215    first few characters */
2216 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2217                          DWORD *size)
2218 {
2219     LPSTR data = NULL;
2220
2221     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2222     {
2223         if (value[1]=='x')
2224         {
2225             LPWSTR ptr;
2226             CHAR byte[5];
2227             LPWSTR deformated = NULL;
2228             int count;
2229
2230             deformat_string(package, &value[2], &deformated);
2231
2232             /* binary value type */
2233             ptr = deformated;
2234             *type = REG_BINARY;
2235             if (strlenW(ptr)%2)
2236                 *size = (strlenW(ptr)/2)+1;
2237             else
2238                 *size = strlenW(ptr)/2;
2239
2240             data = msi_alloc(*size);
2241
2242             byte[0] = '0'; 
2243             byte[1] = 'x'; 
2244             byte[4] = 0; 
2245             count = 0;
2246             /* if uneven pad with a zero in front */
2247             if (strlenW(ptr)%2)
2248             {
2249                 byte[2]= '0';
2250                 byte[3]= *ptr;
2251                 ptr++;
2252                 data[count] = (BYTE)strtol(byte,NULL,0);
2253                 count ++;
2254                 TRACE("Uneven byte count\n");
2255             }
2256             while (*ptr)
2257             {
2258                 byte[2]= *ptr;
2259                 ptr++;
2260                 byte[3]= *ptr;
2261                 ptr++;
2262                 data[count] = (BYTE)strtol(byte,NULL,0);
2263                 count ++;
2264             }
2265             msi_free(deformated);
2266
2267             TRACE("Data %i bytes(%i)\n",*size,count);
2268         }
2269         else
2270         {
2271             LPWSTR deformated;
2272             LPWSTR p;
2273             DWORD d = 0;
2274             deformat_string(package, &value[1], &deformated);
2275
2276             *type=REG_DWORD; 
2277             *size = sizeof(DWORD);
2278             data = msi_alloc(*size);
2279             p = deformated;
2280             if (*p == '-')
2281                 p++;
2282             while (*p)
2283             {
2284                 if ( (*p < '0') || (*p > '9') )
2285                     break;
2286                 d *= 10;
2287                 d += (*p - '0');
2288                 p++;
2289             }
2290             if (deformated[0] == '-')
2291                 d = -d;
2292             *(LPDWORD)data = d;
2293             TRACE("DWORD %i\n",*(LPDWORD)data);
2294
2295             msi_free(deformated);
2296         }
2297     }
2298     else
2299     {
2300         static const WCHAR szMulti[] = {'[','~',']',0};
2301         LPCWSTR ptr;
2302         *type=REG_SZ;
2303
2304         if (value[0]=='#')
2305         {
2306             if (value[1]=='%')
2307             {
2308                 ptr = &value[2];
2309                 *type=REG_EXPAND_SZ;
2310             }
2311             else
2312                 ptr = &value[1];
2313          }
2314          else
2315             ptr=value;
2316
2317         if (strstrW(value,szMulti))
2318             *type = REG_MULTI_SZ;
2319
2320         /* remove initial delimiter */
2321         if (!strncmpW(value, szMulti, 3))
2322             ptr = value + 3;
2323
2324         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2325
2326         /* add double NULL terminator */
2327         if (*type == REG_MULTI_SZ)
2328         {
2329             *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2330             data = msi_realloc_zero(data, *size);
2331         }
2332     }
2333     return data;
2334 }
2335
2336 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2337 {
2338     MSIPACKAGE *package = (MSIPACKAGE*)param;
2339     static const WCHAR szHCR[] = 
2340         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2341          'R','O','O','T','\\',0};
2342     static const WCHAR szHCU[] =
2343         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2344          'U','S','E','R','\\',0};
2345     static const WCHAR szHLM[] =
2346         {'H','K','E','Y','_','L','O','C','A','L','_',
2347          'M','A','C','H','I','N','E','\\',0};
2348     static const WCHAR szHU[] =
2349         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2350
2351     LPSTR value_data = NULL;
2352     HKEY  root_key, hkey;
2353     DWORD type,size;
2354     LPWSTR  deformated;
2355     LPCWSTR szRoot, component, name, key, value;
2356     MSICOMPONENT *comp;
2357     MSIRECORD * uirow;
2358     LPWSTR uikey;
2359     INT   root;
2360     BOOL check_first = FALSE;
2361     UINT rc;
2362
2363     ui_progress(package,2,0,0,0);
2364
2365     value = NULL;
2366     key = NULL;
2367     uikey = NULL;
2368     name = NULL;
2369
2370     component = MSI_RecordGetString(row, 6);
2371     comp = get_loaded_component(package,component);
2372     if (!comp)
2373         return ERROR_SUCCESS;
2374
2375     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2376     {
2377         TRACE("Skipping write due to disabled component %s\n",
2378                         debugstr_w(component));
2379
2380         comp->Action = comp->Installed;
2381
2382         return ERROR_SUCCESS;
2383     }
2384
2385     comp->Action = INSTALLSTATE_LOCAL;
2386
2387     name = MSI_RecordGetString(row, 4);
2388     if( MSI_RecordIsNull(row,5) && name )
2389     {
2390         /* null values can have special meanings */
2391         if (name[0]=='-' && name[1] == 0)
2392                 return ERROR_SUCCESS;
2393         else if ((name[0]=='+' && name[1] == 0) || 
2394                  (name[0] == '*' && name[1] == 0))
2395                 name = NULL;
2396         check_first = TRUE;
2397     }
2398
2399     root = MSI_RecordGetInteger(row,2);
2400     key = MSI_RecordGetString(row, 3);
2401
2402     /* get the root key */
2403     switch (root)
2404     {
2405         case -1: 
2406             {
2407                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2408                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2409                 if (all_users && all_users[0] == '1')
2410                 {
2411                     root_key = HKEY_LOCAL_MACHINE;
2412                     szRoot = szHLM;
2413                 }
2414                 else
2415                 {
2416                     root_key = HKEY_CURRENT_USER;
2417                     szRoot = szHCU;
2418                 }
2419                 msi_free(all_users);
2420             }
2421                  break;
2422         case 0:  root_key = HKEY_CLASSES_ROOT; 
2423                  szRoot = szHCR;
2424                  break;
2425         case 1:  root_key = HKEY_CURRENT_USER;
2426                  szRoot = szHCU;
2427                  break;
2428         case 2:  root_key = HKEY_LOCAL_MACHINE;
2429                  szRoot = szHLM;
2430                  break;
2431         case 3:  root_key = HKEY_USERS; 
2432                  szRoot = szHU;
2433                  break;
2434         default:
2435                  ERR("Unknown root %i\n",root);
2436                  root_key=NULL;
2437                  szRoot = NULL;
2438                  break;
2439     }
2440     if (!root_key)
2441         return ERROR_SUCCESS;
2442
2443     deformat_string(package, key , &deformated);
2444     size = strlenW(deformated) + strlenW(szRoot) + 1;
2445     uikey = msi_alloc(size*sizeof(WCHAR));
2446     strcpyW(uikey,szRoot);
2447     strcatW(uikey,deformated);
2448
2449     if (RegCreateKeyW( root_key, deformated, &hkey))
2450     {
2451         ERR("Could not create key %s\n",debugstr_w(deformated));
2452         msi_free(deformated);
2453         msi_free(uikey);
2454         return ERROR_SUCCESS;
2455     }
2456     msi_free(deformated);
2457
2458     value = MSI_RecordGetString(row,5);
2459     if (value)
2460         value_data = parse_value(package, value, &type, &size); 
2461     else
2462     {
2463         static const WCHAR szEmpty[] = {0};
2464         value_data = (LPSTR)strdupW(szEmpty);
2465         size = sizeof(szEmpty);
2466         type = REG_SZ;
2467     }
2468
2469     deformat_string(package, name, &deformated);
2470
2471     if (!check_first)
2472     {
2473         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2474                         debugstr_w(uikey));
2475         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2476     }
2477     else
2478     {
2479         DWORD sz = 0;
2480         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2481         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2482         {
2483             TRACE("value %s of %s checked already exists\n",
2484                             debugstr_w(deformated), debugstr_w(uikey));
2485         }
2486         else
2487         {
2488             TRACE("Checked and setting value %s of %s\n",
2489                             debugstr_w(deformated), debugstr_w(uikey));
2490             if (deformated || size)
2491                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2492         }
2493     }
2494     RegCloseKey(hkey);
2495
2496     uirow = MSI_CreateRecord(3);
2497     MSI_RecordSetStringW(uirow,2,deformated);
2498     MSI_RecordSetStringW(uirow,1,uikey);
2499
2500     if (type == REG_SZ)
2501         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2502     else
2503         MSI_RecordSetStringW(uirow,3,value);
2504
2505     ui_actiondata(package,szWriteRegistryValues,uirow);
2506     msiobj_release( &uirow->hdr );
2507
2508     msi_free(value_data);
2509     msi_free(deformated);
2510     msi_free(uikey);
2511
2512     return ERROR_SUCCESS;
2513 }
2514
2515 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2516 {
2517     UINT rc;
2518     MSIQUERY * view;
2519     static const WCHAR ExecSeqQuery[] =
2520         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2521          '`','R','e','g','i','s','t','r','y','`',0 };
2522
2523     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2524     if (rc != ERROR_SUCCESS)
2525         return ERROR_SUCCESS;
2526
2527     /* increment progress bar each time action data is sent */
2528     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2529
2530     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2531
2532     msiobj_release(&view->hdr);
2533     return rc;
2534 }
2535
2536 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2537 {
2538     package->script->CurrentlyScripting = TRUE;
2539
2540     return ERROR_SUCCESS;
2541 }
2542
2543
2544 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2545 {
2546     MSICOMPONENT *comp;
2547     DWORD progress = 0;
2548     DWORD total = 0;
2549     static const WCHAR q1[]=
2550         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2551          '`','R','e','g','i','s','t','r','y','`',0};
2552     UINT rc;
2553     MSIQUERY * view;
2554     MSIFEATURE *feature;
2555     MSIFILE *file;
2556
2557     TRACE("InstallValidate\n");
2558
2559     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2560     if (rc == ERROR_SUCCESS)
2561     {
2562         MSI_IterateRecords( view, &progress, NULL, package );
2563         msiobj_release( &view->hdr );
2564         total += progress * REG_PROGRESS_VALUE;
2565     }
2566
2567     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2568         total += COMPONENT_PROGRESS_VALUE;
2569
2570     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2571         total += file->FileSize;
2572
2573     ui_progress(package,0,total,0,0);
2574
2575     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2576     {
2577         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2578             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2579             feature->ActionRequest);
2580     }
2581     
2582     return ERROR_SUCCESS;
2583 }
2584
2585 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2586 {
2587     MSIPACKAGE* package = (MSIPACKAGE*)param;
2588     LPCWSTR cond = NULL; 
2589     LPCWSTR message = NULL;
2590     UINT r;
2591
2592     static const WCHAR title[]=
2593         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2594
2595     cond = MSI_RecordGetString(row,1);
2596
2597     r = MSI_EvaluateConditionW(package,cond);
2598     if (r == MSICONDITION_FALSE)
2599     {
2600         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2601         {
2602             LPWSTR deformated;
2603             message = MSI_RecordGetString(row,2);
2604             deformat_string(package,message,&deformated);
2605             MessageBoxW(NULL,deformated,title,MB_OK);
2606             msi_free(deformated);
2607         }
2608
2609         return ERROR_INSTALL_FAILURE;
2610     }
2611
2612     return ERROR_SUCCESS;
2613 }
2614
2615 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2616 {
2617     UINT rc;
2618     MSIQUERY * view = NULL;
2619     static const WCHAR ExecSeqQuery[] =
2620         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2621          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2622
2623     TRACE("Checking launch conditions\n");
2624
2625     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2626     if (rc != ERROR_SUCCESS)
2627         return ERROR_SUCCESS;
2628
2629     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2630     msiobj_release(&view->hdr);
2631
2632     return rc;
2633 }
2634
2635 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2636 {
2637
2638     if (!cmp->KeyPath)
2639         return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2640
2641     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2642     {
2643         MSIRECORD * row = 0;
2644         UINT root,len;
2645         LPWSTR deformated,buffer,deformated_name;
2646         LPCWSTR key,name;
2647         static const WCHAR ExecSeqQuery[] =
2648             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2649              '`','R','e','g','i','s','t','r','y','`',' ',
2650              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2651              ' ','=',' ' ,'\'','%','s','\'',0 };
2652         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2653         static const WCHAR fmt2[]=
2654             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2655
2656         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2657         if (!row)
2658             return NULL;
2659
2660         root = MSI_RecordGetInteger(row,2);
2661         key = MSI_RecordGetString(row, 3);
2662         name = MSI_RecordGetString(row, 4);
2663         deformat_string(package, key , &deformated);
2664         deformat_string(package, name, &deformated_name);
2665
2666         len = strlenW(deformated) + 6;
2667         if (deformated_name)
2668             len+=strlenW(deformated_name);
2669
2670         buffer = msi_alloc( len *sizeof(WCHAR));
2671
2672         if (deformated_name)
2673             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2674         else
2675             sprintfW(buffer,fmt,root,deformated);
2676
2677         msi_free(deformated);
2678         msi_free(deformated_name);
2679         msiobj_release(&row->hdr);
2680
2681         return buffer;
2682     }
2683     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2684     {
2685         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2686         return NULL;
2687     }
2688     else
2689     {
2690         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2691
2692         if (file)
2693             return strdupW( file->TargetPath );
2694     }
2695     return NULL;
2696 }
2697
2698 static HKEY openSharedDLLsKey(void)
2699 {
2700     HKEY hkey=0;
2701     static const WCHAR path[] =
2702         {'S','o','f','t','w','a','r','e','\\',
2703          'M','i','c','r','o','s','o','f','t','\\',
2704          'W','i','n','d','o','w','s','\\',
2705          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2706          'S','h','a','r','e','d','D','L','L','s',0};
2707
2708     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2709     return hkey;
2710 }
2711
2712 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2713 {
2714     HKEY hkey;
2715     DWORD count=0;
2716     DWORD type;
2717     DWORD sz = sizeof(count);
2718     DWORD rc;
2719     
2720     hkey = openSharedDLLsKey();
2721     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2722     if (rc != ERROR_SUCCESS)
2723         count = 0;
2724     RegCloseKey(hkey);
2725     return count;
2726 }
2727
2728 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2729 {
2730     HKEY hkey;
2731
2732     hkey = openSharedDLLsKey();
2733     if (count > 0)
2734         msi_reg_set_val_dword( hkey, path, count );
2735     else
2736         RegDeleteValueW(hkey,path);
2737     RegCloseKey(hkey);
2738     return count;
2739 }
2740
2741 /*
2742  * Return TRUE if the count should be written out and FALSE if not
2743  */
2744 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2745 {
2746     MSIFEATURE *feature;
2747     INT count = 0;
2748     BOOL write = FALSE;
2749
2750     /* only refcount DLLs */
2751     if (comp->KeyPath == NULL || 
2752         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2753         comp->Attributes & msidbComponentAttributesODBCDataSource)
2754         write = FALSE;
2755     else
2756     {
2757         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2758         write = (count > 0);
2759
2760         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2761             write = TRUE;
2762     }
2763
2764     /* increment counts */
2765     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2766     {
2767         ComponentList *cl;
2768
2769         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2770             continue;
2771
2772         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2773         {
2774             if ( cl->component == comp )
2775                 count++;
2776         }
2777     }
2778
2779     /* decrement counts */
2780     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2781     {
2782         ComponentList *cl;
2783
2784         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
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     /* ref count all the files in the component */
2795     if (write)
2796     {
2797         MSIFILE *file;
2798
2799         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2800         {
2801             if (file->Component == comp)
2802                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2803         }
2804     }
2805     
2806     /* add a count for permanent */
2807     if (comp->Attributes & msidbComponentAttributesPermanent)
2808         count ++;
2809     
2810     comp->RefCount = count;
2811
2812     if (write)
2813         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2814 }
2815
2816 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2817 {
2818     WCHAR squished_pc[GUID_SIZE];
2819     WCHAR squished_cc[GUID_SIZE];
2820     UINT rc;
2821     MSICOMPONENT *comp;
2822     HKEY hkey;
2823
2824     TRACE("\n");
2825
2826     squash_guid(package->ProductCode,squished_pc);
2827     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2828
2829     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2830     {
2831         MSIRECORD * uirow;
2832
2833         ui_progress(package,2,0,0,0);
2834         if (!comp->ComponentId)
2835             continue;
2836
2837         squash_guid(comp->ComponentId,squished_cc);
2838
2839         msi_free(comp->FullKeypath);
2840         comp->FullKeypath = resolve_keypath( package, comp );
2841
2842         ACTION_RefCountComponent( package, comp );
2843
2844         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2845                             debugstr_w(comp->Component),
2846                             debugstr_w(squished_cc),
2847                             debugstr_w(comp->FullKeypath),
2848                             comp->RefCount);
2849
2850         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2851             ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2852         {
2853             if (!comp->FullKeypath)
2854                 continue;
2855
2856             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2857                 rc = MSIREG_OpenLocalUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2858             else
2859                 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2860
2861             if (rc != ERROR_SUCCESS)
2862                 continue;
2863
2864             if (comp->Attributes & msidbComponentAttributesPermanent)
2865             {
2866                 static const WCHAR szPermKey[] =
2867                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2868                       '0','0','0','0','0','0','0','0','0','0','0','0',
2869                       '0','0','0','0','0','0','0','0',0 };
2870
2871                 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2872             }
2873
2874             if (comp->Action == INSTALLSTATE_LOCAL)
2875                 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2876             else
2877             {
2878                 MSIFILE *file;
2879                 MSIRECORD *row;
2880                 LPWSTR ptr, ptr2;
2881                 WCHAR source[MAX_PATH];
2882                 WCHAR base[MAX_PATH];
2883                 LPWSTR sourcepath;
2884
2885                 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2886                 static const WCHAR query[] = {
2887                     'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2888                     '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2889                     '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2890                     '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2891                     '`','D','i','s','k','I','d','`',0};
2892
2893                 file = get_loaded_file(package, comp->KeyPath);
2894                 if (!file)
2895                     continue;
2896
2897                 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2898                 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2899                 ptr2 = strrchrW(source, '\\') + 1;
2900                 msiobj_release(&row->hdr);
2901
2902                 lstrcpyW(base, package->PackagePath);
2903                 ptr = strrchrW(base, '\\');
2904                 *(ptr + 1) = '\0';
2905
2906                 sourcepath = resolve_file_source(package, file);
2907                 ptr = sourcepath + lstrlenW(base);
2908                 lstrcpyW(ptr2, ptr);
2909                 msi_free(sourcepath);
2910
2911                 msi_reg_set_val_str(hkey, squished_pc, source);
2912             }
2913             RegCloseKey(hkey);
2914         }
2915         else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2916         {
2917             if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2918                 MSIREG_DeleteLocalUserDataComponentKey(comp->ComponentId);
2919             else
2920                 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2921         }
2922
2923         /* UI stuff */
2924         uirow = MSI_CreateRecord(3);
2925         MSI_RecordSetStringW(uirow,1,package->ProductCode);
2926         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2927         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2928         ui_actiondata(package,szProcessComponents,uirow);
2929         msiobj_release( &uirow->hdr );
2930     }
2931
2932     return ERROR_SUCCESS;
2933 }
2934
2935 typedef struct {
2936     CLSID       clsid;
2937     LPWSTR      source;
2938
2939     LPWSTR      path;
2940     ITypeLib    *ptLib;
2941 } typelib_struct;
2942
2943 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
2944                                        LPWSTR lpszName, LONG_PTR lParam)
2945 {
2946     TLIBATTR *attr;
2947     typelib_struct *tl_struct = (typelib_struct*) lParam;
2948     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2949     int sz; 
2950     HRESULT res;
2951
2952     if (!IS_INTRESOURCE(lpszName))
2953     {
2954         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2955         return TRUE;
2956     }
2957
2958     sz = strlenW(tl_struct->source)+4;
2959     sz *= sizeof(WCHAR);
2960
2961     if ((INT_PTR)lpszName == 1)
2962         tl_struct->path = strdupW(tl_struct->source);
2963     else
2964     {
2965         tl_struct->path = msi_alloc(sz);
2966         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2967     }
2968
2969     TRACE("trying %s\n", debugstr_w(tl_struct->path));
2970     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2971     if (FAILED(res))
2972     {
2973         msi_free(tl_struct->path);
2974         tl_struct->path = NULL;
2975
2976         return TRUE;
2977     }
2978
2979     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2980     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2981     {
2982         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2983         return FALSE;
2984     }
2985
2986     msi_free(tl_struct->path);
2987     tl_struct->path = NULL;
2988
2989     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2990     ITypeLib_Release(tl_struct->ptLib);
2991
2992     return TRUE;
2993 }
2994
2995 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2996 {
2997     MSIPACKAGE* package = (MSIPACKAGE*)param;
2998     LPCWSTR component;
2999     MSICOMPONENT *comp;
3000     MSIFILE *file;
3001     typelib_struct tl_struct;
3002     ITypeLib *tlib;
3003     HMODULE module;
3004     HRESULT hr;
3005
3006     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3007
3008     component = MSI_RecordGetString(row,3);
3009     comp = get_loaded_component(package,component);
3010     if (!comp)
3011         return ERROR_SUCCESS;
3012
3013     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3014     {
3015         TRACE("Skipping typelib reg due to disabled component\n");
3016
3017         comp->Action = comp->Installed;
3018
3019         return ERROR_SUCCESS;
3020     }
3021
3022     comp->Action = INSTALLSTATE_LOCAL;
3023
3024     file = get_loaded_file( package, comp->KeyPath ); 
3025     if (!file)
3026         return ERROR_SUCCESS;
3027
3028     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3029     if (module)
3030     {
3031         LPCWSTR guid;
3032         guid = MSI_RecordGetString(row,1);
3033         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3034         tl_struct.source = strdupW( file->TargetPath );
3035         tl_struct.path = NULL;
3036
3037         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3038                         (LONG_PTR)&tl_struct);
3039
3040         if (tl_struct.path)
3041         {
3042             LPWSTR help = NULL;
3043             LPCWSTR helpid;
3044             HRESULT res;
3045
3046             helpid = MSI_RecordGetString(row,6);
3047
3048             if (helpid)
3049                 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3050             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3051             msi_free(help);
3052
3053             if (FAILED(res))
3054                 ERR("Failed to register type library %s\n",
3055                         debugstr_w(tl_struct.path));
3056             else
3057             {
3058                 ui_actiondata(package,szRegisterTypeLibraries,row);
3059
3060                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3061             }
3062
3063             ITypeLib_Release(tl_struct.ptLib);
3064             msi_free(tl_struct.path);
3065         }
3066         else
3067             ERR("Failed to load type library %s\n",
3068                     debugstr_w(tl_struct.source));
3069
3070         FreeLibrary(module);
3071         msi_free(tl_struct.source);
3072     }
3073     else
3074     {
3075         hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3076         if (FAILED(hr))
3077         {
3078             ERR("Failed to load type library: %08x\n", hr);
3079             return ERROR_FUNCTION_FAILED;
3080         }
3081
3082         ITypeLib_Release(tlib);
3083     }
3084
3085     return ERROR_SUCCESS;
3086 }
3087
3088 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3089 {
3090     /* 
3091      * OK this is a bit confusing.. I am given a _Component key and I believe
3092      * that the file that is being registered as a type library is the "key file
3093      * of that component" which I interpret to mean "The file in the KeyPath of
3094      * that component".
3095      */
3096     UINT rc;
3097     MSIQUERY * view;
3098     static const WCHAR Query[] =
3099         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3100          '`','T','y','p','e','L','i','b','`',0};
3101
3102     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3103     if (rc != ERROR_SUCCESS)
3104         return ERROR_SUCCESS;
3105
3106     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3107     msiobj_release(&view->hdr);
3108     return rc;
3109 }
3110
3111 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3112 {
3113     MSIPACKAGE *package = (MSIPACKAGE*)param;
3114     LPWSTR target_file, target_folder, filename;
3115     LPCWSTR buffer, extension;
3116     MSICOMPONENT *comp;
3117     static const WCHAR szlnk[]={'.','l','n','k',0};
3118     IShellLinkW *sl = NULL;
3119     IPersistFile *pf = NULL;
3120     HRESULT res;
3121
3122     buffer = MSI_RecordGetString(row,4);
3123     comp = get_loaded_component(package,buffer);
3124     if (!comp)
3125         return ERROR_SUCCESS;
3126
3127     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3128     {
3129         TRACE("Skipping shortcut creation due to disabled component\n");
3130
3131         comp->Action = comp->Installed;
3132
3133         return ERROR_SUCCESS;
3134     }
3135
3136     comp->Action = INSTALLSTATE_LOCAL;
3137
3138     ui_actiondata(package,szCreateShortcuts,row);
3139
3140     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3141                     &IID_IShellLinkW, (LPVOID *) &sl );
3142
3143     if (FAILED( res ))
3144     {
3145         ERR("CLSID_ShellLink not available\n");
3146         goto err;
3147     }
3148
3149     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3150     if (FAILED( res ))
3151     {
3152         ERR("QueryInterface(IID_IPersistFile) failed\n");
3153         goto err;
3154     }
3155
3156     buffer = MSI_RecordGetString(row,2);
3157     target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3158
3159     /* may be needed because of a bug somewhere else */
3160     create_full_pathW(target_folder);
3161
3162     filename = msi_dup_record_field( row, 3 );
3163     reduce_to_longfilename(filename);
3164
3165     extension = strchrW(filename,'.');
3166     if (!extension || strcmpiW(extension,szlnk))
3167     {
3168         int len = strlenW(filename);
3169         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3170         memcpy(filename + len, szlnk, sizeof(szlnk));
3171     }
3172     target_file = build_directory_name(2, target_folder, filename);
3173     msi_free(target_folder);
3174     msi_free(filename);
3175
3176     buffer = MSI_RecordGetString(row,5);
3177     if (strchrW(buffer,'['))
3178     {
3179         LPWSTR deformated;
3180         deformat_string(package,buffer,&deformated);
3181         IShellLinkW_SetPath(sl,deformated);
3182         msi_free(deformated);
3183     }
3184     else
3185     {
3186         FIXME("poorly handled shortcut format, advertised shortcut\n");
3187         IShellLinkW_SetPath(sl,comp->FullKeypath);
3188     }
3189
3190     if (!MSI_RecordIsNull(row,6))
3191     {
3192         LPWSTR deformated;
3193         buffer = MSI_RecordGetString(row,6);
3194         deformat_string(package,buffer,&deformated);
3195         IShellLinkW_SetArguments(sl,deformated);
3196         msi_free(deformated);
3197     }
3198
3199     if (!MSI_RecordIsNull(row,7))
3200     {
3201         buffer = MSI_RecordGetString(row,7);
3202         IShellLinkW_SetDescription(sl,buffer);
3203     }
3204
3205     if (!MSI_RecordIsNull(row,8))
3206         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3207
3208     if (!MSI_RecordIsNull(row,9))
3209     {
3210         LPWSTR Path;
3211         INT index; 
3212
3213         buffer = MSI_RecordGetString(row,9);
3214
3215         Path = build_icon_path(package,buffer);
3216         index = MSI_RecordGetInteger(row,10);
3217
3218         /* no value means 0 */
3219         if (index == MSI_NULL_INTEGER)
3220             index = 0;
3221
3222         IShellLinkW_SetIconLocation(sl,Path,index);
3223         msi_free(Path);
3224     }
3225
3226     if (!MSI_RecordIsNull(row,11))
3227         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3228
3229     if (!MSI_RecordIsNull(row,12))
3230     {
3231         LPWSTR Path;
3232         buffer = MSI_RecordGetString(row,12);
3233         Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3234         if (Path)
3235             IShellLinkW_SetWorkingDirectory(sl,Path);
3236         msi_free(Path);
3237     }
3238
3239     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3240     IPersistFile_Save(pf,target_file,FALSE);
3241
3242     msi_free(target_file);    
3243
3244 err:
3245     if (pf)
3246         IPersistFile_Release( pf );
3247     if (sl)
3248         IShellLinkW_Release( sl );
3249
3250     return ERROR_SUCCESS;
3251 }
3252
3253 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3254 {
3255     UINT rc;
3256     HRESULT res;
3257     MSIQUERY * view;
3258     static const WCHAR Query[] =
3259         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3260          '`','S','h','o','r','t','c','u','t','`',0};
3261
3262     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3263     if (rc != ERROR_SUCCESS)
3264         return ERROR_SUCCESS;
3265
3266     res = CoInitialize( NULL );
3267     if (FAILED (res))
3268     {
3269         ERR("CoInitialize failed\n");
3270         return ERROR_FUNCTION_FAILED;
3271     }
3272
3273     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3274     msiobj_release(&view->hdr);
3275
3276     CoUninitialize();
3277
3278     return rc;
3279 }
3280
3281 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3282 {
3283     MSIPACKAGE* package = (MSIPACKAGE*)param;
3284     HANDLE the_file;
3285     LPWSTR FilePath;
3286     LPCWSTR FileName;
3287     CHAR buffer[1024];
3288     DWORD sz;
3289     UINT rc;
3290     MSIRECORD *uirow;
3291
3292     FileName = MSI_RecordGetString(row,1);
3293     if (!FileName)
3294     {
3295         ERR("Unable to get FileName\n");
3296         return ERROR_SUCCESS;
3297     }
3298
3299     FilePath = build_icon_path(package,FileName);
3300
3301     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3302
3303     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3304                         FILE_ATTRIBUTE_NORMAL, NULL);
3305
3306     if (the_file == INVALID_HANDLE_VALUE)
3307     {
3308         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3309         msi_free(FilePath);
3310         return ERROR_SUCCESS;
3311     }
3312
3313     do 
3314     {
3315         DWORD write;
3316         sz = 1024;
3317         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3318         if (rc != ERROR_SUCCESS)
3319         {
3320             ERR("Failed to get stream\n");
3321             CloseHandle(the_file);  
3322             DeleteFileW(FilePath);
3323             break;
3324         }
3325         WriteFile(the_file,buffer,sz,&write,NULL);
3326     } while (sz == 1024);
3327
3328     msi_free(FilePath);
3329
3330     CloseHandle(the_file);
3331
3332     uirow = MSI_CreateRecord(1);
3333     MSI_RecordSetStringW(uirow,1,FileName);
3334     ui_actiondata(package,szPublishProduct,uirow);
3335     msiobj_release( &uirow->hdr );
3336
3337     return ERROR_SUCCESS;
3338 }
3339
3340 static UINT msi_publish_icons(MSIPACKAGE *package)
3341 {
3342     UINT r;
3343     MSIQUERY *view;
3344
3345     static const WCHAR query[]= {
3346         'S','E','L','E','C','T',' ','*',' ',
3347         'F','R','O','M',' ','`','I','c','o','n','`',0};
3348
3349     r = MSI_DatabaseOpenViewW(package->db, query, &view);
3350     if (r == ERROR_SUCCESS)
3351     {
3352         MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3353         msiobj_release(&view->hdr);
3354     }
3355
3356     return ERROR_SUCCESS;
3357 }
3358
3359 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3360 {
3361     UINT r;
3362     HKEY source;
3363     LPWSTR buffer;
3364     MSIMEDIADISK *disk;
3365     MSISOURCELISTINFO *info;
3366
3367     static const WCHAR szEmpty[] = {0};
3368     static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3369
3370     r = RegCreateKeyW(hkey, szSourceList, &source);
3371     if (r != ERROR_SUCCESS)
3372         return r;
3373
3374     RegCloseKey(source);
3375
3376     buffer = strrchrW(package->PackagePath, '\\') + 1;
3377     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3378                               package->Context, MSICODE_PRODUCT,
3379                               INSTALLPROPERTY_PACKAGENAMEW, buffer);
3380     if (r != ERROR_SUCCESS)
3381         return r;
3382
3383     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3384                               package->Context, MSICODE_PRODUCT,
3385                               INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3386     if (r != ERROR_SUCCESS)
3387         return r;
3388
3389     r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3390                               package->Context, MSICODE_PRODUCT,
3391                               INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3392     if (r != ERROR_SUCCESS)
3393         return r;
3394
3395     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3396     {
3397         if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3398             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3399                                      info->options, info->value);
3400         else
3401             MsiSourceListSetInfoW(package->ProductCode, NULL,
3402                                   info->context, info->options,
3403                                   info->property, info->value);
3404     }
3405
3406     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3407     {
3408         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3409                                    disk->context, disk->options,
3410                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3411     }
3412
3413     return ERROR_SUCCESS;
3414 }
3415
3416 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3417 {
3418     MSIHANDLE hdb, suminfo;
3419     WCHAR guids[MAX_PATH];
3420     WCHAR packcode[SQUISH_GUID_SIZE];
3421     LPWSTR buffer;
3422     LPWSTR ptr;
3423     DWORD langid;
3424     DWORD size;
3425     UINT r;
3426
3427     static const WCHAR szProductLanguage[] =
3428         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3429     static const WCHAR szARPProductIcon[] =
3430         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3431     static const WCHAR szProductVersion[] =
3432         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3433     static const WCHAR szAssignment[] =
3434         {'A','s','s','i','g','n','m','e','n','t',0};
3435     static const WCHAR szAdvertiseFlags[] =
3436         {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3437     static const WCHAR szClients[] =
3438         {'C','l','i','e','n','t','s',0};
3439     static const WCHAR szColon[] = {':',0};
3440
3441     buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3442     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3443     msi_free(buffer);
3444
3445     langid = msi_get_property_int(package, szProductLanguage, 0);
3446     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3447
3448     ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3449     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3450
3451     /* FIXME */
3452     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3453
3454     buffer = msi_dup_property(package, szARPProductIcon);
3455     if (buffer)
3456     {
3457         LPWSTR path = build_icon_path(package,buffer);
3458         msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3459         msi_free(path);
3460         msi_free(buffer);
3461     }
3462
3463     buffer = msi_dup_property(package, szProductVersion);
3464     if (buffer)
3465     {
3466         DWORD verdword = msi_version_str_to_dword(buffer);
3467         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3468         msi_free(buffer);
3469     }
3470
3471     msi_reg_set_val_dword(hkey, szAssignment, 0);
3472     msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3473     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3474     msi_reg_set_val_str(hkey, szClients, szColon);
3475
3476     hdb = alloc_msihandle(&package->db->hdr);
3477     if (!hdb)
3478         return ERROR_NOT_ENOUGH_MEMORY;
3479
3480     r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3481     MsiCloseHandle(hdb);
3482     if (r != ERROR_SUCCESS)
3483         goto done;
3484
3485     size = MAX_PATH;
3486     r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3487                                    NULL, guids, &size);
3488     if (r != ERROR_SUCCESS)
3489         goto done;
3490
3491     ptr = strchrW(guids, ';');
3492     if (ptr) *ptr = 0;
3493     squash_guid(guids, packcode);
3494     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3495
3496 done:
3497     MsiCloseHandle(suminfo);
3498     return ERROR_SUCCESS;
3499 }
3500
3501 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3502 {
3503     UINT r;
3504     HKEY hkey;
3505     LPWSTR upgrade;
3506     WCHAR squashed_pc[SQUISH_GUID_SIZE];
3507
3508     static const WCHAR szUpgradeCode[] =
3509         {'U','p','g','r','a','d','e','C','o','d','e',0};
3510
3511     upgrade = msi_dup_property(package, szUpgradeCode);
3512     if (!upgrade)
3513         return ERROR_SUCCESS;
3514
3515     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3516     {
3517         r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3518         if (r != ERROR_SUCCESS)
3519             goto done;
3520     }
3521     else
3522     {
3523         r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3524         if (r != ERROR_SUCCESS)
3525             goto done;
3526     }
3527
3528     squash_guid(package->ProductCode, squashed_pc);
3529     msi_reg_set_val_str(hkey, squashed_pc, NULL);
3530
3531     RegCloseKey(hkey);
3532
3533 done:
3534     msi_free(upgrade);
3535     return r;
3536 }
3537
3538 static BOOL msi_check_publish(MSIPACKAGE *package)
3539 {
3540     MSIFEATURE *feature;
3541
3542     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3543     {
3544         if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3545             return TRUE;
3546     }
3547
3548     return FALSE;
3549 }
3550
3551 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3552 {
3553     MSIFEATURE *feature;
3554
3555     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3556     {
3557         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3558             return FALSE;
3559     }
3560
3561     return TRUE;
3562 }
3563
3564 /*
3565  * 99% of the work done here is only done for 
3566  * advertised installs. However this is where the
3567  * Icon table is processed and written out
3568  * so that is what I am going to do here.
3569  */
3570 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3571 {
3572     UINT rc;
3573     HKEY hukey=0;
3574     HKEY hudkey=0;
3575
3576     /* FIXME: also need to publish if the product is in advertise mode */
3577     if (!msi_check_publish(package))
3578         return ERROR_SUCCESS;
3579
3580     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3581     {
3582         rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3583         if (rc != ERROR_SUCCESS)
3584             goto end;
3585
3586         rc = MSIREG_OpenLocalUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3587         if (rc != ERROR_SUCCESS)
3588             goto end;
3589     }
3590     else
3591     {
3592         rc = MSIREG_OpenUserProductsKey(package->ProductCode, &hukey, TRUE);
3593         if (rc != ERROR_SUCCESS)
3594             goto end;
3595
3596         rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3597         if (rc != ERROR_SUCCESS)
3598             goto end;
3599     }
3600
3601     rc = msi_publish_upgrade_code(package);
3602     if (rc != ERROR_SUCCESS)
3603         goto end;
3604
3605     rc = msi_publish_product_properties(package, hukey);
3606     if (rc != ERROR_SUCCESS)
3607         goto end;
3608
3609     rc = msi_publish_sourcelist(package, hukey);
3610     if (rc != ERROR_SUCCESS)
3611         goto end;
3612
3613     rc = msi_publish_icons(package);
3614
3615 end:
3616     RegCloseKey(hukey);
3617     RegCloseKey(hudkey);
3618
3619     return rc;
3620 }
3621
3622 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3623 {
3624     MSIPACKAGE *package = (MSIPACKAGE*)param;
3625     LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3626     LPWSTR deformated_section, deformated_key, deformated_value;
3627     LPWSTR folder, fullname = NULL;
3628     MSIRECORD * uirow;
3629     INT action;
3630     MSICOMPONENT *comp;
3631     static const WCHAR szWindowsFolder[] =
3632           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3633
3634     component = MSI_RecordGetString(row, 8);
3635     comp = get_loaded_component(package,component);
3636
3637     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3638     {
3639         TRACE("Skipping ini file due to disabled component %s\n",
3640                         debugstr_w(component));
3641
3642         comp->Action = comp->Installed;
3643
3644         return ERROR_SUCCESS;
3645     }
3646
3647     comp->Action = INSTALLSTATE_LOCAL;
3648
3649     identifier = MSI_RecordGetString(row,1); 
3650     filename = MSI_RecordGetString(row,2);
3651     dirproperty = MSI_RecordGetString(row,3);
3652     section = MSI_RecordGetString(row,4);
3653     key = MSI_RecordGetString(row,5);
3654     value = MSI_RecordGetString(row,6);
3655     action = MSI_RecordGetInteger(row,7);
3656
3657     deformat_string(package,section,&deformated_section);
3658     deformat_string(package,key,&deformated_key);
3659     deformat_string(package,value,&deformated_value);
3660
3661     if (dirproperty)
3662     {
3663         folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3664         if (!folder)
3665             folder = msi_dup_property( package, dirproperty );
3666     }
3667     else
3668         folder = msi_dup_property( package, szWindowsFolder );
3669
3670     if (!folder)
3671     {
3672         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3673         goto cleanup;
3674     }
3675
3676     fullname = build_directory_name(2, folder, filename);
3677
3678     if (action == 0)
3679     {
3680         TRACE("Adding value %s to section %s in %s\n",
3681                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3682                 debugstr_w(fullname));
3683         WritePrivateProfileStringW(deformated_section, deformated_key,
3684                                    deformated_value, fullname);
3685     }
3686     else if (action == 1)
3687     {
3688         WCHAR returned[10];
3689         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3690                                  returned, 10, fullname);
3691         if (returned[0] == 0)
3692         {
3693             TRACE("Adding value %s to section %s in %s\n",
3694                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3695                     debugstr_w(fullname));
3696
3697             WritePrivateProfileStringW(deformated_section, deformated_key,
3698                                        deformated_value, fullname);
3699         }
3700     }
3701     else if (action == 3)
3702         FIXME("Append to existing section not yet implemented\n");
3703
3704     uirow = MSI_CreateRecord(4);
3705     MSI_RecordSetStringW(uirow,1,identifier);
3706     MSI_RecordSetStringW(uirow,2,deformated_section);
3707     MSI_RecordSetStringW(uirow,3,deformated_key);
3708     MSI_RecordSetStringW(uirow,4,deformated_value);
3709     ui_actiondata(package,szWriteIniValues,uirow);
3710     msiobj_release( &uirow->hdr );
3711 cleanup:
3712     msi_free(fullname);
3713     msi_free(folder);
3714     msi_free(deformated_key);
3715     msi_free(deformated_value);
3716     msi_free(deformated_section);
3717     return ERROR_SUCCESS;
3718 }
3719
3720 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3721 {
3722     UINT rc;
3723     MSIQUERY * view;
3724     static const WCHAR ExecSeqQuery[] = 
3725         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3726          '`','I','n','i','F','i','l','e','`',0};
3727
3728     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3729     if (rc != ERROR_SUCCESS)
3730     {
3731         TRACE("no IniFile table\n");
3732         return ERROR_SUCCESS;
3733     }
3734
3735     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3736     msiobj_release(&view->hdr);
3737     return rc;
3738 }
3739
3740 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3741 {
3742     MSIPACKAGE *package = (MSIPACKAGE*)param;
3743     LPCWSTR filename;
3744     LPWSTR FullName;
3745     MSIFILE *file;
3746     DWORD len;
3747     static const WCHAR ExeStr[] =
3748         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3749     static const WCHAR close[] =  {'\"',0};
3750     STARTUPINFOW si;
3751     PROCESS_INFORMATION info;
3752     BOOL brc;
3753     MSIRECORD *uirow;
3754     LPWSTR uipath, p;
3755
3756     memset(&si,0,sizeof(STARTUPINFOW));
3757
3758     filename = MSI_RecordGetString(row,1);
3759     file = get_loaded_file( package, filename );
3760
3761     if (!file)
3762     {
3763         ERR("Unable to find file id %s\n",debugstr_w(filename));
3764         return ERROR_SUCCESS;
3765     }
3766
3767     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3768
3769     FullName = msi_alloc(len*sizeof(WCHAR));
3770     strcpyW(FullName,ExeStr);
3771     strcatW( FullName, file->TargetPath );
3772     strcatW(FullName,close);
3773
3774     TRACE("Registering %s\n",debugstr_w(FullName));
3775     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3776                     &si, &info);
3777
3778     if (brc)
3779     {
3780         CloseHandle(info.hThread);
3781         msi_dialog_check_messages(info.hProcess);
3782         CloseHandle(info.hProcess);
3783     }
3784
3785     msi_free(FullName);
3786
3787     /* the UI chunk */
3788     uirow = MSI_CreateRecord( 2 );
3789     uipath = strdupW( file->TargetPath );
3790     p = strrchrW(uipath,'\\');
3791     if (p)
3792         p[0]=0;
3793     MSI_RecordSetStringW( uirow, 1, &p[1] );
3794     MSI_RecordSetStringW( uirow, 2, uipath);
3795     ui_actiondata( package, szSelfRegModules, uirow);
3796     msiobj_release( &uirow->hdr );
3797     msi_free( uipath );
3798     /* FIXME: call ui_progress? */
3799
3800     return ERROR_SUCCESS;
3801 }
3802
3803 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3804 {
3805     UINT rc;
3806     MSIQUERY * view;
3807     static const WCHAR ExecSeqQuery[] = 
3808         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3809          '`','S','e','l','f','R','e','g','`',0};
3810
3811     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3812     if (rc != ERROR_SUCCESS)
3813     {
3814         TRACE("no SelfReg table\n");
3815         return ERROR_SUCCESS;
3816     }
3817
3818     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3819     msiobj_release(&view->hdr);
3820
3821     return ERROR_SUCCESS;
3822 }
3823
3824 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3825 {
3826     MSIFEATURE *feature;
3827     UINT rc;
3828     HKEY hkey;
3829     HKEY userdata = NULL;
3830
3831     if (!msi_check_publish(package))
3832         return ERROR_SUCCESS;
3833
3834     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3835     {
3836         rc = MSIREG_OpenLocalClassesFeaturesKey(package->ProductCode,
3837                                                 &hkey, TRUE);
3838         if (rc != ERROR_SUCCESS)
3839             goto end;
3840
3841         rc = MSIREG_OpenLocalUserDataFeaturesKey(package->ProductCode,
3842                                                  &userdata, TRUE);
3843         if (rc != ERROR_SUCCESS)
3844             goto end;
3845     }
3846     else
3847     {
3848         rc = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, TRUE);
3849         if (rc != ERROR_SUCCESS)
3850             goto end;
3851
3852         rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode,
3853                                             &userdata, TRUE);
3854         if (rc != ERROR_SUCCESS)
3855             goto end;
3856     }
3857
3858     /* here the guids are base 85 encoded */
3859     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3860     {
3861         ComponentList *cl;
3862         LPWSTR data = NULL;
3863         GUID clsid;
3864         INT size;
3865         BOOL absent = FALSE;
3866         MSIRECORD *uirow;
3867
3868         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3869             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3870             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3871             absent = TRUE;
3872
3873         size = 1;
3874         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3875         {
3876             size += 21;
3877         }
3878         if (feature->Feature_Parent)
3879             size += strlenW( feature->Feature_Parent )+2;
3880
3881         data = msi_alloc(size * sizeof(WCHAR));
3882
3883         data[0] = 0;
3884         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3885         {
3886             MSICOMPONENT* component = cl->component;
3887             WCHAR buf[21];
3888
3889             buf[0] = 0;
3890             if (component->ComponentId)
3891             {
3892                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3893                 CLSIDFromString(component->ComponentId, &clsid);
3894                 encode_base85_guid(&clsid,buf);
3895                 TRACE("to %s\n",debugstr_w(buf));
3896                 strcatW(data,buf);
3897             }
3898         }
3899
3900         if (feature->Feature_Parent)
3901         {
3902             static const WCHAR sep[] = {'\2',0};
3903             strcatW(data,sep);
3904             strcatW(data,feature->Feature_Parent);
3905         }
3906
3907         msi_reg_set_val_str( userdata, feature->Feature, data );
3908         msi_free(data);
3909
3910         size = 0;
3911         if (feature->Feature_Parent)
3912             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3913         if (!absent)
3914         {
3915             static const WCHAR emptyW[] = {0};
3916             size += sizeof(WCHAR);
3917             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3918                            (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3919         }
3920         else
3921         {
3922             size += 2*sizeof(WCHAR);
3923             data = msi_alloc(size);
3924             data[0] = 0x6;
3925             data[1] = 0;
3926             if (feature->Feature_Parent)
3927                 strcpyW( &data[1], feature->Feature_Parent );
3928             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3929                        (LPBYTE)data,size);
3930             msi_free(data);
3931         }
3932
3933         /* the UI chunk */
3934         uirow = MSI_CreateRecord( 1 );
3935         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3936         ui_actiondata( package, szPublishFeatures, uirow);
3937         msiobj_release( &uirow->hdr );
3938         /* FIXME: call ui_progress? */
3939     }
3940
3941 end:
3942     RegCloseKey(hkey);
3943     RegCloseKey(userdata);
3944     return rc;
3945 }
3946
3947 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3948 {
3949     UINT r;
3950     HKEY hkey;
3951
3952     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3953
3954     r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3955     if (r == ERROR_SUCCESS)
3956     {
3957         RegDeleteValueW(hkey, feature->Feature);
3958         RegCloseKey(hkey);
3959     }
3960
3961     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3962     if (r == ERROR_SUCCESS)
3963     {
3964         RegDeleteValueW(hkey, feature->Feature);
3965         RegCloseKey(hkey);
3966     }
3967
3968     return ERROR_SUCCESS;
3969 }
3970
3971 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3972 {
3973     MSIFEATURE *feature;
3974
3975     if (!msi_check_unpublish(package))
3976         return ERROR_SUCCESS;
3977
3978     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3979     {
3980         msi_unpublish_feature(package, feature);
3981     }
3982
3983     return ERROR_SUCCESS;
3984 }
3985
3986 static UINT msi_get_local_package_name( LPWSTR path )
3987 {
3988     static const WCHAR szInstaller[] = {
3989         '\\','I','n','s','t','a','l','l','e','r','\\',0};
3990     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3991     DWORD time, len, i;
3992     HANDLE handle;
3993
3994     time = GetTickCount();
3995     GetWindowsDirectoryW( path, MAX_PATH );
3996     lstrcatW( path, szInstaller );
3997     CreateDirectoryW( path, NULL );
3998
3999     len = lstrlenW(path);
4000     for (i=0; i<0x10000; i++)
4001     {
4002         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4003         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4004                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4005         if (handle != INVALID_HANDLE_VALUE)
4006         {
4007             CloseHandle(handle);
4008             break;
4009         }
4010         if (GetLastError() != ERROR_FILE_EXISTS &&
4011             GetLastError() != ERROR_SHARING_VIOLATION)
4012             return ERROR_FUNCTION_FAILED;
4013     }
4014
4015     return ERROR_SUCCESS;
4016 }
4017
4018 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4019 {
4020     WCHAR packagefile[MAX_PATH];
4021     UINT r;
4022
4023     r = msi_get_local_package_name( packagefile );
4024     if (r != ERROR_SUCCESS)
4025         return r;
4026
4027     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4028
4029     r = CopyFileW( package->db->path, packagefile, FALSE);
4030
4031     if (!r)
4032     {
4033         ERR("Unable to copy package (%s -> %s) (error %d)\n",
4034             debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4035         return ERROR_FUNCTION_FAILED;
4036     }
4037
4038     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4039
4040     return ERROR_SUCCESS;
4041 }
4042
4043 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4044 {
4045     LPWSTR prop, val, key;
4046     SYSTEMTIME systime;
4047     DWORD size, langid;
4048     WCHAR date[9];
4049     LPWSTR buffer;
4050
4051     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4052     static const WCHAR szWindowsInstaller[] =
4053         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4054     static const WCHAR modpath_fmt[] =
4055         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4056          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4057     static const WCHAR szModifyPath[] =
4058         {'M','o','d','i','f','y','P','a','t','h',0};
4059     static const WCHAR szUninstallString[] =
4060         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4061     static const WCHAR szEstimatedSize[] =
4062         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4063     static const WCHAR szProductLanguage[] =
4064         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4065     static const WCHAR szProductVersion[] =
4066         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4067     static const WCHAR szProductName[] =
4068         {'P','r','o','d','u','c','t','N','a','m','e',0};
4069     static const WCHAR szDisplayName[] =
4070         {'D','i','s','p','l','a','y','N','a','m','e',0};
4071     static const WCHAR szDisplayVersion[] =
4072         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4073     static const WCHAR szManufacturer[] =
4074         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4075
4076     static const LPCSTR propval[] = {
4077         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4078         "ARPCONTACT",             "Contact",
4079         "ARPCOMMENTS",            "Comments",
4080         "ProductName",            "DisplayName",
4081         "ProductVersion",         "DisplayVersion",
4082         "ARPHELPLINK",            "HelpLink",
4083         "ARPHELPTELEPHONE",       "HelpTelephone",
4084         "ARPINSTALLLOCATION",     "InstallLocation",
4085         "SourceDir",              "InstallSource",
4086         "Manufacturer",           "Publisher",
4087         "ARPREADME",              "Readme",
4088         "ARPSIZE",                "Size",
4089         "ARPURLINFOABOUT",        "URLInfoAbout",
4090         "ARPURLUPDATEINFO",       "URLUpdateInfo",
4091         NULL,
4092     };
4093     const LPCSTR *p = propval;
4094
4095     while (*p)
4096     {
4097         prop = strdupAtoW(*p++);
4098         key = strdupAtoW(*p++);
4099         val = msi_dup_property(package, prop);
4100         msi_reg_set_val_str(hkey, key, val);
4101         msi_free(val);
4102         msi_free(key);
4103         msi_free(prop);
4104     }
4105
4106     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4107
4108     size = deformat_string(package, modpath_fmt, &buffer);
4109     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4110     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4111     msi_free(buffer);
4112
4113     /* FIXME: Write real Estimated Size when we have it */
4114     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4115
4116     buffer = msi_dup_property(package, szProductName);
4117     msi_reg_set_val_str(hkey, szDisplayName, buffer);
4118     msi_free(buffer);
4119
4120     buffer = msi_dup_property(package, cszSourceDir);
4121     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4122     msi_free(buffer);
4123
4124     buffer = msi_dup_property(package, szManufacturer);
4125     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4126     msi_free(buffer);
4127
4128     GetLocalTime(&systime);
4129     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4130     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4131
4132     langid = msi_get_property_int(package, szProductLanguage, 0);
4133     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4134
4135     buffer = msi_dup_property(package, szProductVersion);
4136     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4137     if (buffer)
4138     {
4139         DWORD verdword = msi_version_str_to_dword(buffer);
4140
4141         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4142         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4143         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4144         msi_free(buffer);
4145     }
4146
4147     return ERROR_SUCCESS;
4148 }
4149
4150 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4151 {
4152     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4153     LPWSTR upgrade_code;
4154     HKEY hkey, props;
4155     HKEY upgrade;
4156     UINT rc;
4157
4158     static const WCHAR szUpgradeCode[] = {
4159         'U','p','g','r','a','d','e','C','o','d','e',0};
4160
4161     /* FIXME: also need to publish if the product is in advertise mode */
4162     if (!msi_check_publish(package))
4163         return ERROR_SUCCESS;
4164
4165     rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4166     if (rc != ERROR_SUCCESS)
4167         return rc;
4168
4169     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4170     {
4171         rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4172         if (rc != ERROR_SUCCESS)
4173             goto done;
4174     }
4175     else
4176     {
4177         rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4178         if (rc != ERROR_SUCCESS)
4179             goto done;
4180     }
4181
4182     msi_make_package_local(package, props);
4183
4184     rc = msi_publish_install_properties(package, hkey);
4185     if (rc != ERROR_SUCCESS)
4186         goto done;
4187
4188     rc = msi_publish_install_properties(package, props);
4189     if (rc != ERROR_SUCCESS)
4190         goto done;
4191
4192     upgrade_code = msi_dup_property(package, szUpgradeCode);
4193     if (upgrade_code)
4194     {
4195         MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4196         squash_guid(package->ProductCode, squashed_pc);
4197         msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4198         RegCloseKey(upgrade);
4199         msi_free(upgrade_code);
4200     }
4201
4202 done:
4203     RegCloseKey(hkey);
4204
4205     return ERROR_SUCCESS;
4206 }
4207
4208 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4209 {
4210     return execute_script(package,INSTALL_SCRIPT);
4211 }
4212
4213 static UINT msi_unpublish_product(MSIPACKAGE *package)
4214 {
4215     LPWSTR upgrade;
4216     LPWSTR remove = NULL;
4217     LPWSTR *features = NULL;
4218     BOOL full_uninstall = TRUE;
4219     MSIFEATURE *feature;
4220
4221     static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4222     static const WCHAR szAll[] = {'A','L','L',0};
4223     static const WCHAR szUpgradeCode[] =
4224         {'U','p','g','r','a','d','e','C','o','d','e',0};
4225
4226     remove = msi_dup_property(package, szRemove);
4227     if (!remove)
4228         return ERROR_SUCCESS;
4229
4230     features = msi_split_string(remove, ',');
4231     if (!features)
4232     {
4233         msi_free(remove);
4234         ERR("REMOVE feature list is empty!\n");
4235         return ERROR_FUNCTION_FAILED;
4236     }
4237
4238     if (!lstrcmpW(features[0], szAll))
4239         full_uninstall = TRUE;
4240     else
4241     {
4242         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4243         {
4244             if (feature->Action != INSTALLSTATE_ABSENT)
4245                 full_uninstall = FALSE;
4246         }
4247     }
4248
4249     if (!full_uninstall)
4250         goto done;
4251
4252     MSIREG_DeleteProductKey(package->ProductCode);
4253     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4254     MSIREG_DeleteUninstallKey(package->ProductCode);
4255
4256     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4257     {
4258         MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4259         MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4260     }
4261     else
4262     {
4263         MSIREG_DeleteUserProductKey(package->ProductCode);
4264         MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4265     }
4266
4267     upgrade = msi_dup_property(package, szUpgradeCode);
4268     if (upgrade)
4269     {
4270         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4271         msi_free(upgrade);
4272     }
4273
4274 done:
4275     msi_free(remove);
4276     msi_free(features);
4277     return ERROR_SUCCESS;
4278 }
4279
4280 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4281 {
4282     UINT rc;
4283
4284     rc = msi_unpublish_product(package);
4285     if (rc != ERROR_SUCCESS)
4286         return rc;
4287
4288     /* turn off scheduling */
4289     package->script->CurrentlyScripting= FALSE;
4290
4291     /* first do the same as an InstallExecute */
4292     rc = ACTION_InstallExecute(package);
4293     if (rc != ERROR_SUCCESS)
4294         return rc;
4295
4296     /* then handle Commit Actions */
4297     rc = execute_script(package,COMMIT_SCRIPT);
4298
4299     return rc;
4300 }
4301
4302 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4303 {
4304     static const WCHAR RunOnce[] = {
4305     'S','o','f','t','w','a','r','e','\\',
4306     'M','i','c','r','o','s','o','f','t','\\',
4307     'W','i','n','d','o','w','s','\\',
4308     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4309     'R','u','n','O','n','c','e',0};
4310     static const WCHAR InstallRunOnce[] = {
4311     'S','o','f','t','w','a','r','e','\\',
4312     'M','i','c','r','o','s','o','f','t','\\',
4313     'W','i','n','d','o','w','s','\\',
4314     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4315     'I','n','s','t','a','l','l','e','r','\\',
4316     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4317
4318     static const WCHAR msiexec_fmt[] = {
4319     '%','s',
4320     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4321     '\"','%','s','\"',0};
4322     static const WCHAR install_fmt[] = {
4323     '/','I',' ','\"','%','s','\"',' ',
4324     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4325     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4326     WCHAR buffer[256], sysdir[MAX_PATH];
4327     HKEY hkey;
4328     WCHAR squished_pc[100];
4329
4330     squash_guid(package->ProductCode,squished_pc);
4331
4332     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4333     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4334     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4335      squished_pc);
4336
4337     msi_reg_set_val_str( hkey, squished_pc, buffer );
4338     RegCloseKey(hkey);
4339
4340     TRACE("Reboot command %s\n",debugstr_w(buffer));
4341
4342     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4343     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4344
4345     msi_reg_set_val_str( hkey, squished_pc, buffer );
4346     RegCloseKey(hkey);
4347
4348     return ERROR_INSTALL_SUSPEND;
4349 }
4350
4351 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4352 {
4353     DWORD attrib;
4354     UINT rc;
4355
4356     /*
4357      * We are currently doing what should be done here in the top level Install
4358      * however for Administrative and uninstalls this step will be needed
4359      */
4360     if (!package->PackagePath)
4361         return ERROR_SUCCESS;
4362
4363     msi_set_sourcedir_props(package, TRUE);
4364
4365     attrib = GetFileAttributesW(package->db->path);
4366     if (attrib == INVALID_FILE_ATTRIBUTES)
4367     {
4368         LPWSTR prompt;
4369         LPWSTR msg;
4370         DWORD size = 0;
4371
4372         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4373                 package->Context, MSICODE_PRODUCT,
4374                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4375         if (rc == ERROR_MORE_DATA)
4376         {
4377             prompt = msi_alloc(size * sizeof(WCHAR));
4378             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4379                     package->Context, MSICODE_PRODUCT,
4380                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4381         }
4382         else
4383             prompt = strdupW(package->db->path);
4384
4385         msg = generate_error_string(package,1302,1,prompt);
4386         while(attrib == INVALID_FILE_ATTRIBUTES)
4387         {
4388             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4389             if (rc == IDCANCEL)
4390             {
4391                 rc = ERROR_INSTALL_USEREXIT;
4392                 break;
4393             }
4394             attrib = GetFileAttributesW(package->db->path);
4395         }
4396         msi_free(prompt);
4397         rc = ERROR_SUCCESS;
4398     }
4399     else
4400         return ERROR_SUCCESS;
4401
4402     return rc;
4403 }
4404
4405 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4406 {
4407     HKEY hkey=0;
4408     LPWSTR buffer;
4409     LPWSTR productid;
4410     UINT rc,i;
4411
4412     static const WCHAR szPropKeys[][80] = 
4413     {
4414         {'P','r','o','d','u','c','t','I','D',0},
4415         {'U','S','E','R','N','A','M','E',0},
4416         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4417         {0},
4418     };
4419
4420     static const WCHAR szRegKeys[][80] = 
4421     {
4422         {'P','r','o','d','u','c','t','I','D',0},
4423         {'R','e','g','O','w','n','e','r',0},
4424         {'R','e','g','C','o','m','p','a','n','y',0},
4425         {0},
4426     };
4427
4428     if (msi_check_unpublish(package))
4429     {
4430         MSIREG_DeleteUserDataProductKey(package->ProductCode);
4431         return ERROR_SUCCESS;
4432     }
4433
4434     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4435     if (!productid)
4436         return ERROR_SUCCESS;
4437
4438     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4439         rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4440     else
4441         rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4442
4443     if (rc != ERROR_SUCCESS)
4444         goto end;
4445
4446     for( i = 0; szPropKeys[i][0]; i++ )
4447     {
4448         buffer = msi_dup_property( package, szPropKeys[i] );
4449         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4450         msi_free( buffer );
4451     }
4452
4453 end:
4454     msi_free(productid);
4455     RegCloseKey(hkey);
4456
4457     /* FIXME: call ui_actiondata */
4458
4459     return rc;
4460 }
4461
4462
4463 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4464 {
4465     UINT rc;
4466
4467     package->script->InWhatSequence |= SEQUENCE_EXEC;
4468     rc = ACTION_ProcessExecSequence(package,FALSE);
4469     return rc;
4470 }
4471
4472
4473 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4474 {
4475     MSIPACKAGE *package = (MSIPACKAGE*)param;
4476     LPCWSTR compgroupid=NULL;
4477     LPCWSTR feature=NULL;
4478     LPCWSTR text = NULL;
4479     LPCWSTR qualifier = NULL;
4480     LPCWSTR component = NULL;
4481     LPWSTR advertise = NULL;
4482     LPWSTR output = NULL;
4483     HKEY hkey;
4484     UINT rc = ERROR_SUCCESS;
4485     MSICOMPONENT *comp;
4486     DWORD sz = 0;
4487     MSIRECORD *uirow;
4488
4489     component = MSI_RecordGetString(rec,3);
4490     comp = get_loaded_component(package,component);
4491
4492     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4493        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4494        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4495     {
4496         TRACE("Skipping: Component %s not scheduled for install\n",
4497                         debugstr_w(component));
4498
4499         return ERROR_SUCCESS;
4500     }
4501
4502     compgroupid = MSI_RecordGetString(rec,1);
4503     qualifier = MSI_RecordGetString(rec,2);
4504
4505     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4506     if (rc != ERROR_SUCCESS)
4507         goto end;
4508     
4509     text = MSI_RecordGetString(rec,4);
4510     feature = MSI_RecordGetString(rec,5);
4511   
4512     advertise = create_component_advertise_string(package, comp, feature);
4513
4514     sz = strlenW(advertise);
4515
4516     if (text)
4517         sz += lstrlenW(text);
4518
4519     sz+=3;
4520     sz *= sizeof(WCHAR);
4521            
4522     output = msi_alloc_zero(sz);
4523     strcpyW(output,advertise);
4524     msi_free(advertise);
4525
4526     if (text)
4527         strcatW(output,text);
4528
4529     msi_reg_set_val_multi_str( hkey, qualifier, output );
4530     
4531 end:
4532     RegCloseKey(hkey);
4533     msi_free(output);
4534
4535     /* the UI chunk */
4536     uirow = MSI_CreateRecord( 2 );
4537     MSI_RecordSetStringW( uirow, 1, compgroupid );
4538     MSI_RecordSetStringW( uirow, 2, qualifier);
4539     ui_actiondata( package, szPublishComponents, uirow);
4540     msiobj_release( &uirow->hdr );
4541     /* FIXME: call ui_progress? */
4542
4543     return rc;
4544 }
4545
4546 /*
4547  * At present I am ignorning the advertised components part of this and only
4548  * focusing on the qualified component sets
4549  */
4550 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4551 {
4552     UINT rc;
4553     MSIQUERY * view;
4554     static const WCHAR ExecSeqQuery[] =
4555         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4556          '`','P','u','b','l','i','s','h',
4557          'C','o','m','p','o','n','e','n','t','`',0};
4558     
4559     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4560     if (rc != ERROR_SUCCESS)
4561         return ERROR_SUCCESS;
4562
4563     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4564     msiobj_release(&view->hdr);
4565
4566     return rc;
4567 }
4568
4569 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4570 {
4571     MSIPACKAGE *package = (MSIPACKAGE*)param;
4572     MSIRECORD *row;
4573     MSIFILE *file;
4574     SC_HANDLE hscm, service = NULL;
4575     LPCWSTR comp, depends, pass;
4576     LPWSTR name = NULL, disp = NULL;
4577     LPCWSTR load_order, serv_name, key;
4578     DWORD serv_type, start_type;
4579     DWORD err_control;
4580
4581     static const WCHAR query[] =
4582         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4583          '`','C','o','m','p','o','n','e','n','t','`',' ',
4584          'W','H','E','R','E',' ',
4585          '`','C','o','m','p','o','n','e','n','t','`',' ',
4586          '=','\'','%','s','\'',0};
4587
4588     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4589     if (!hscm)
4590     {
4591         ERR("Failed to open the SC Manager!\n");
4592         goto done;
4593     }
4594
4595     start_type = MSI_RecordGetInteger(rec, 5);
4596     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4597         goto done;
4598
4599     depends = MSI_RecordGetString(rec, 8);
4600     if (depends && *depends)
4601         FIXME("Dependency list unhandled!\n");
4602
4603     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4604     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4605     serv_type = MSI_RecordGetInteger(rec, 4);
4606     err_control = MSI_RecordGetInteger(rec, 6);
4607     load_order = MSI_RecordGetString(rec, 7);
4608     serv_name = MSI_RecordGetString(rec, 9);
4609     pass = MSI_RecordGetString(rec, 10);
4610     comp = MSI_RecordGetString(rec, 12);
4611
4612     /* fetch the service path */
4613     row = MSI_QueryGetRecord(package->db, query, comp);
4614     if (!row)
4615     {
4616         ERR("Control query failed!\n");
4617         goto done;
4618     }
4619
4620     key = MSI_RecordGetString(row, 6);
4621
4622     file = get_loaded_file(package, key);
4623     msiobj_release(&row->hdr);
4624     if (!file)
4625     {
4626         ERR("Failed to load the service file\n");
4627         goto done;
4628     }
4629
4630     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4631                              start_type, err_control, file->TargetPath,
4632                              load_order, NULL, NULL, serv_name, pass);
4633     if (!service)
4634     {
4635         if (GetLastError() != ERROR_SERVICE_EXISTS)
4636             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4637     }
4638
4639 done:
4640     CloseServiceHandle(service);
4641     CloseServiceHandle(hscm);
4642     msi_free(name);
4643     msi_free(disp);
4644
4645     return ERROR_SUCCESS;
4646 }
4647
4648 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4649 {
4650     UINT rc;
4651     MSIQUERY * view;
4652     static const WCHAR ExecSeqQuery[] =
4653         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4654          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4655     
4656     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4657     if (rc != ERROR_SUCCESS)
4658         return ERROR_SUCCESS;
4659
4660     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4661     msiobj_release(&view->hdr);
4662
4663     return rc;
4664 }
4665
4666 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4667 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4668 {
4669     LPCWSTR *vector, *temp_vector;
4670     LPWSTR p, q;
4671     DWORD sep_len;
4672
4673     static const WCHAR separator[] = {'[','~',']',0};
4674
4675     *numargs = 0;
4676     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4677
4678     if (!args)
4679         return NULL;
4680
4681     vector = msi_alloc(sizeof(LPWSTR));
4682     if (!vector)
4683         return NULL;
4684
4685     p = args;
4686     do
4687     {
4688         (*numargs)++;
4689         vector[*numargs - 1] = p;
4690
4691         if ((q = strstrW(p, separator)))
4692         {
4693             *q = '\0';
4694
4695             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4696             if (!temp_vector)
4697             {
4698                 msi_free(vector);
4699                 return NULL;
4700             }
4701             vector = temp_vector;
4702
4703             p = q + sep_len;
4704         }
4705     } while (q);
4706
4707     return vector;
4708 }
4709
4710 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4711 {
4712     MSIPACKAGE *package = (MSIPACKAGE *)param;
4713     MSICOMPONENT *comp;
4714     SC_HANDLE scm, service = NULL;
4715     LPCWSTR name, *vector = NULL;
4716     LPWSTR args;
4717     DWORD event, numargs;
4718     UINT r = ERROR_FUNCTION_FAILED;
4719
4720     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4721     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4722         return ERROR_SUCCESS;
4723
4724     name = MSI_RecordGetString(rec, 2);
4725     event = MSI_RecordGetInteger(rec, 3);
4726     args = strdupW(MSI_RecordGetString(rec, 4));
4727
4728     if (!(event & msidbServiceControlEventStart))
4729         return ERROR_SUCCESS;
4730
4731     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4732     if (!scm)
4733     {
4734         ERR("Failed to open the service control manager\n");
4735         goto done;
4736     }
4737
4738     service = OpenServiceW(scm, name, SERVICE_START);
4739     if (!service)
4740     {
4741         ERR("Failed to open service %s\n", debugstr_w(name));
4742         goto done;
4743     }
4744
4745     vector = msi_service_args_to_vector(args, &numargs);
4746
4747     if (!StartServiceW(service, numargs, vector))
4748     {
4749         ERR("Failed to start service %s\n", debugstr_w(name));
4750         goto done;
4751     }
4752
4753     r = ERROR_SUCCESS;
4754
4755 done:
4756     CloseServiceHandle(service);
4757     CloseServiceHandle(scm);
4758
4759     msi_free(args);
4760     msi_free(vector);
4761     return r;
4762 }
4763
4764 static UINT ACTION_StartServices( MSIPACKAGE *package )
4765 {
4766     UINT rc;
4767     MSIQUERY *view;
4768
4769     static const WCHAR query[] = {
4770         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4771         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4772
4773     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4774     if (rc != ERROR_SUCCESS)
4775         return ERROR_SUCCESS;
4776
4777     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4778     msiobj_release(&view->hdr);
4779
4780     return rc;
4781 }
4782
4783 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4784 {
4785     DWORD i, needed, count;
4786     ENUM_SERVICE_STATUSW *dependencies;
4787     SERVICE_STATUS ss;
4788     SC_HANDLE depserv;
4789
4790     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4791                                0, &needed, &count))
4792         return TRUE;
4793
4794     if (GetLastError() != ERROR_MORE_DATA)
4795         return FALSE;
4796
4797     dependencies = msi_alloc(needed);
4798     if (!dependencies)
4799         return FALSE;
4800
4801     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4802                                 needed, &needed, &count))
4803         goto error;
4804
4805     for (i = 0; i < count; i++)
4806     {
4807         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4808                                SERVICE_STOP | SERVICE_QUERY_STATUS);
4809         if (!depserv)
4810             goto error;
4811
4812         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4813             goto error;
4814     }
4815
4816     return TRUE;
4817
4818 error:
4819     msi_free(dependencies);
4820     return FALSE;
4821 }
4822
4823 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4824 {
4825     MSIPACKAGE *package = (MSIPACKAGE *)param;
4826     MSICOMPONENT *comp;
4827     SERVICE_STATUS status;
4828     SERVICE_STATUS_PROCESS ssp;
4829     SC_HANDLE scm = NULL, service = NULL;
4830     LPWSTR name, args;
4831     DWORD event, needed;
4832
4833     event = MSI_RecordGetInteger(rec, 3);
4834     if (!(event & msidbServiceControlEventStop))
4835         return ERROR_SUCCESS;
4836
4837     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4838     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4839         return ERROR_SUCCESS;
4840
4841     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4842     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4843     args = strdupW(MSI_RecordGetString(rec, 4));
4844
4845     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4846     if (!scm)
4847     {
4848         WARN("Failed to open the SCM: %d\n", GetLastError());
4849         goto done;
4850     }
4851
4852     service = OpenServiceW(scm, name,
4853                            SERVICE_STOP |
4854                            SERVICE_QUERY_STATUS |
4855                            SERVICE_ENUMERATE_DEPENDENTS);
4856     if (!service)
4857     {
4858         WARN("Failed to open service (%s): %d\n",
4859               debugstr_w(name), GetLastError());
4860         goto done;
4861     }
4862
4863     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4864                               sizeof(SERVICE_STATUS_PROCESS), &needed))
4865     {
4866         WARN("Failed to query service status (%s): %d\n",
4867              debugstr_w(name), GetLastError());
4868         goto done;
4869     }
4870
4871     if (ssp.dwCurrentState == SERVICE_STOPPED)
4872         goto done;
4873
4874     stop_service_dependents(scm, service);
4875
4876     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4877         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4878
4879 done:
4880     CloseServiceHandle(service);
4881     CloseServiceHandle(scm);
4882     msi_free(name);
4883     msi_free(args);
4884
4885     return ERROR_SUCCESS;
4886 }
4887
4888 static UINT ACTION_StopServices( MSIPACKAGE *package )
4889 {
4890     UINT rc;
4891     MSIQUERY *view;
4892
4893     static const WCHAR query[] = {
4894         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4895         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4896
4897     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4898     if (rc != ERROR_SUCCESS)
4899         return ERROR_SUCCESS;
4900
4901     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4902     msiobj_release(&view->hdr);
4903
4904     return rc;
4905 }
4906
4907 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4908 {
4909     MSIFILE *file;
4910
4911     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4912     {
4913         if (!lstrcmpW(file->File, filename))
4914             return file;
4915     }
4916
4917     return NULL;
4918 }
4919
4920 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4921 {
4922     MSIPACKAGE *package = (MSIPACKAGE*)param;
4923     LPWSTR driver, driver_path, ptr;
4924     WCHAR outpath[MAX_PATH];
4925     MSIFILE *driver_file, *setup_file;
4926     LPCWSTR desc;
4927     DWORD len, usage;
4928     UINT r = ERROR_SUCCESS;
4929
4930     static const WCHAR driver_fmt[] = {
4931         'D','r','i','v','e','r','=','%','s',0};
4932     static const WCHAR setup_fmt[] = {
4933         'S','e','t','u','p','=','%','s',0};
4934     static const WCHAR usage_fmt[] = {
4935         'F','i','l','e','U','s','a','g','e','=','1',0};
4936
4937     desc = MSI_RecordGetString(rec, 3);
4938
4939     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4940     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4941
4942     if (!driver_file || !setup_file)
4943     {
4944         ERR("ODBC Driver entry not found!\n");
4945         return ERROR_FUNCTION_FAILED;
4946     }
4947
4948     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4949           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4950           lstrlenW(usage_fmt) + 1;
4951     driver = msi_alloc(len * sizeof(WCHAR));
4952     if (!driver)
4953         return ERROR_OUTOFMEMORY;
4954
4955     ptr = driver;
4956     lstrcpyW(ptr, desc);
4957     ptr += lstrlenW(ptr) + 1;
4958
4959     sprintfW(ptr, driver_fmt, driver_file->FileName);
4960     ptr += lstrlenW(ptr) + 1;
4961
4962     sprintfW(ptr, setup_fmt, setup_file->FileName);
4963     ptr += lstrlenW(ptr) + 1;
4964
4965     lstrcpyW(ptr, usage_fmt);
4966     ptr += lstrlenW(ptr) + 1;
4967     *ptr = '\0';
4968
4969     driver_path = strdupW(driver_file->TargetPath);
4970     ptr = strrchrW(driver_path, '\\');
4971     if (ptr) *ptr = '\0';
4972
4973     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4974                              NULL, ODBC_INSTALL_COMPLETE, &usage))
4975     {
4976         ERR("Failed to install SQL driver!\n");
4977         r = ERROR_FUNCTION_FAILED;
4978     }
4979
4980     msi_free(driver);
4981     msi_free(driver_path);
4982
4983     return r;
4984 }
4985
4986 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4987 {
4988     MSIPACKAGE *package = (MSIPACKAGE*)param;
4989     LPWSTR translator, translator_path, ptr;
4990     WCHAR outpath[MAX_PATH];
4991     MSIFILE *translator_file, *setup_file;
4992     LPCWSTR desc;
4993     DWORD len, usage;
4994     UINT r = ERROR_SUCCESS;
4995
4996     static const WCHAR translator_fmt[] = {
4997         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4998     static const WCHAR setup_fmt[] = {
4999         'S','e','t','u','p','=','%','s',0};
5000
5001     desc = MSI_RecordGetString(rec, 3);
5002
5003     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5004     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5005
5006     if (!translator_file || !setup_file)
5007     {
5008         ERR("ODBC Translator entry not found!\n");
5009         return ERROR_FUNCTION_FAILED;
5010     }
5011
5012     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5013           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5014     translator = msi_alloc(len * sizeof(WCHAR));
5015     if (!translator)
5016         return ERROR_OUTOFMEMORY;
5017
5018     ptr = translator;
5019     lstrcpyW(ptr, desc);
5020     ptr += lstrlenW(ptr) + 1;
5021
5022     sprintfW(ptr, translator_fmt, translator_file->FileName);
5023     ptr += lstrlenW(ptr) + 1;
5024
5025     sprintfW(ptr, setup_fmt, setup_file->FileName);
5026     ptr += lstrlenW(ptr) + 1;
5027     *ptr = '\0';
5028
5029     translator_path = strdupW(translator_file->TargetPath);
5030     ptr = strrchrW(translator_path, '\\');
5031     if (ptr) *ptr = '\0';
5032
5033     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5034                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
5035     {
5036         ERR("Failed to install SQL translator!\n");
5037         r = ERROR_FUNCTION_FAILED;
5038     }
5039
5040     msi_free(translator);
5041     msi_free(translator_path);
5042
5043     return r;
5044 }
5045
5046 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5047 {
5048     LPWSTR attrs;
5049     LPCWSTR desc, driver;
5050     WORD request = ODBC_ADD_SYS_DSN;
5051     INT registration;
5052     DWORD len;
5053     UINT r = ERROR_SUCCESS;
5054
5055     static const WCHAR attrs_fmt[] = {
5056         'D','S','N','=','%','s',0 };
5057
5058     desc = MSI_RecordGetString(rec, 3);
5059     driver = MSI_RecordGetString(rec, 4);
5060     registration = MSI_RecordGetInteger(rec, 5);
5061
5062     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5063     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5064
5065     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5066     attrs = msi_alloc(len * sizeof(WCHAR));
5067     if (!attrs)
5068         return ERROR_OUTOFMEMORY;
5069
5070     sprintfW(attrs, attrs_fmt, desc);
5071     attrs[len - 1] = '\0';
5072
5073     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5074     {
5075         ERR("Failed to install SQL data source!\n");
5076         r = ERROR_FUNCTION_FAILED;
5077     }
5078
5079     msi_free(attrs);
5080
5081     return r;
5082 }
5083
5084 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5085 {
5086     UINT rc;
5087     MSIQUERY *view;
5088
5089     static const WCHAR driver_query[] = {
5090         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5091         'O','D','B','C','D','r','i','v','e','r',0 };
5092
5093     static const WCHAR translator_query[] = {
5094         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5095         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5096
5097     static const WCHAR source_query[] = {
5098         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5099         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5100
5101     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5102     if (rc != ERROR_SUCCESS)
5103         return ERROR_SUCCESS;
5104
5105     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5106     msiobj_release(&view->hdr);
5107
5108     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5109     if (rc != ERROR_SUCCESS)
5110         return ERROR_SUCCESS;
5111
5112     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5113     msiobj_release(&view->hdr);
5114
5115     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5116     if (rc != ERROR_SUCCESS)
5117         return ERROR_SUCCESS;
5118
5119     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5120     msiobj_release(&view->hdr);
5121
5122     return rc;
5123 }
5124
5125 #define ENV_ACT_SETALWAYS   0x1
5126 #define ENV_ACT_SETABSENT   0x2
5127 #define ENV_ACT_REMOVE      0x4
5128 #define ENV_ACT_REMOVEMATCH 0x8
5129
5130 #define ENV_MOD_MACHINE     0x20000000
5131 #define ENV_MOD_APPEND      0x40000000
5132 #define ENV_MOD_PREFIX      0x80000000
5133 #define ENV_MOD_MASK        0xC0000000
5134
5135 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5136
5137 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5138 {
5139     LPCWSTR cptr = *name;
5140     LPCWSTR ptr = *value;
5141
5142     static const WCHAR prefix[] = {'[','~',']',0};
5143     static const int prefix_len = 3;
5144
5145     *flags = 0;
5146     while (*cptr)
5147     {
5148         if (*cptr == '=')
5149             *flags |= ENV_ACT_SETALWAYS;
5150         else if (*cptr == '+')
5151             *flags |= ENV_ACT_SETABSENT;
5152         else if (*cptr == '-')
5153             *flags |= ENV_ACT_REMOVE;
5154         else if (*cptr == '!')
5155             *flags |= ENV_ACT_REMOVEMATCH;
5156         else if (*cptr == '*')
5157             *flags |= ENV_MOD_MACHINE;
5158         else
5159             break;
5160
5161         cptr++;
5162         (*name)++;
5163     }
5164
5165     if (!*cptr)
5166     {
5167         ERR("Missing environment variable\n");
5168         return ERROR_FUNCTION_FAILED;
5169     }
5170
5171     if (!strncmpW(ptr, prefix, prefix_len))
5172     {
5173         *flags |= ENV_MOD_APPEND;
5174         *value += lstrlenW(prefix);
5175     }
5176     else if (lstrlenW(*value) >= prefix_len)
5177     {
5178         ptr += lstrlenW(ptr) - prefix_len;
5179         if (!lstrcmpW(ptr, prefix))
5180         {
5181             *flags |= ENV_MOD_PREFIX;
5182             /* the "[~]" will be removed by deformat_string */;
5183         }
5184     }
5185
5186     if (!*flags ||
5187         check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5188         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5189         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5190         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5191     {
5192         ERR("Invalid flags: %08x\n", *flags);
5193         return ERROR_FUNCTION_FAILED;
5194     }
5195
5196     return ERROR_SUCCESS;
5197 }
5198
5199 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5200 {
5201     MSIPACKAGE *package = param;
5202     LPCWSTR name, value;
5203     LPWSTR data = NULL, newval = NULL;
5204     LPWSTR deformatted = NULL, ptr;
5205     DWORD flags, type, size;
5206     LONG res;
5207     HKEY env = NULL, root;
5208     LPCWSTR environment;
5209
5210     static const WCHAR user_env[] =
5211         {'E','n','v','i','r','o','n','m','e','n','t',0};
5212     static const WCHAR machine_env[] =
5213         {'S','y','s','t','e','m','\\',
5214          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5215          'C','o','n','t','r','o','l','\\',
5216          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5217          'E','n','v','i','r','o','n','m','e','n','t',0};
5218     static const WCHAR semicolon[] = {';',0};
5219
5220     name = MSI_RecordGetString(rec, 2);
5221     value = MSI_RecordGetString(rec, 3);
5222
5223     res = env_set_flags(&name, &value, &flags);
5224     if (res != ERROR_SUCCESS)
5225        goto done;
5226
5227     deformat_string(package, value, &deformatted);
5228     if (!deformatted)
5229     {
5230         res = ERROR_OUTOFMEMORY;
5231         goto done;
5232     }
5233
5234     value = deformatted;
5235
5236     if (flags & ENV_MOD_MACHINE)
5237     {
5238         environment = machine_env;
5239         root = HKEY_LOCAL_MACHINE;
5240     }
5241     else
5242     {
5243         environment = user_env;
5244         root = HKEY_CURRENT_USER;
5245     }
5246
5247     res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5248                           KEY_ALL_ACCESS, NULL, &env, NULL);
5249     if (res != ERROR_SUCCESS)
5250         goto done;
5251
5252     if (flags & ENV_ACT_REMOVE)
5253         FIXME("Not removing environment variable on uninstall!\n");
5254
5255     size = 0;
5256     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5257     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5258         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5259         goto done;
5260
5261     if (res != ERROR_FILE_NOT_FOUND)
5262     {
5263         if (flags & ENV_ACT_SETABSENT)
5264         {
5265             res = ERROR_SUCCESS;
5266             goto done;
5267         }
5268
5269         data = msi_alloc(size);
5270         if (!data)
5271         {
5272             RegCloseKey(env);
5273             return ERROR_OUTOFMEMORY;
5274         }
5275
5276         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5277         if (res != ERROR_SUCCESS)
5278             goto done;
5279
5280         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5281         {
5282             res = RegDeleteKeyW(env, name);
5283             goto done;
5284         }
5285
5286         size =  (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5287         newval =  msi_alloc(size);
5288         ptr = newval;
5289         if (!newval)
5290         {
5291             res = ERROR_OUTOFMEMORY;
5292             goto done;
5293         }
5294
5295         if (!(flags & ENV_MOD_MASK))
5296             lstrcpyW(newval, value);
5297         else
5298         {
5299             if (flags & ENV_MOD_PREFIX)
5300             {
5301                 lstrcpyW(newval, value);
5302                 lstrcatW(newval, semicolon);
5303                 ptr = newval + lstrlenW(value) + 1;
5304             }
5305
5306             lstrcpyW(ptr, data);
5307
5308             if (flags & ENV_MOD_APPEND)
5309             {
5310                 lstrcatW(newval, semicolon);
5311                 lstrcatW(newval, value);
5312             }
5313         }
5314     }
5315     else
5316     {
5317         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5318         newval = msi_alloc(size);
5319         if (!newval)
5320         {
5321             res = ERROR_OUTOFMEMORY;
5322             goto done;
5323         }
5324
5325         lstrcpyW(newval, value);
5326     }
5327
5328     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5329     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5330
5331 done:
5332     if (env) RegCloseKey(env);
5333     msi_free(deformatted);
5334     msi_free(data);
5335     msi_free(newval);
5336     return res;
5337 }
5338
5339 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5340 {
5341     UINT rc;
5342     MSIQUERY * view;
5343     static const WCHAR ExecSeqQuery[] =
5344         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5345          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5346     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5347     if (rc != ERROR_SUCCESS)
5348         return ERROR_SUCCESS;
5349
5350     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5351     msiobj_release(&view->hdr);
5352
5353     return rc;
5354 }
5355
5356 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5357
5358 typedef struct
5359 {
5360     struct list entry;
5361     LPWSTR sourcename;
5362     LPWSTR destname;
5363     LPWSTR source;
5364     LPWSTR dest;
5365 } FILE_LIST;
5366
5367 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5368 {
5369     BOOL ret;
5370
5371     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5372         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5373     {
5374         WARN("Source or dest is directory, not moving\n");
5375         return FALSE;
5376     }
5377
5378     if (options == msidbMoveFileOptionsMove)
5379     {
5380         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5381         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5382         if (!ret)
5383         {
5384             WARN("MoveFile failed: %d\n", GetLastError());
5385             return FALSE;
5386         }
5387     }
5388     else
5389     {
5390         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5391         ret = CopyFileW(source, dest, FALSE);
5392         if (!ret)
5393         {
5394             WARN("CopyFile failed: %d\n", GetLastError());
5395             return FALSE;
5396         }
5397     }
5398
5399     return TRUE;
5400 }
5401
5402 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5403 {
5404     LPWSTR path, ptr;
5405     DWORD dirlen, pathlen;
5406
5407     ptr = strrchrW(wildcard, '\\');
5408     dirlen = ptr - wildcard + 1;
5409
5410     pathlen = dirlen + lstrlenW(filename) + 1;
5411     path = msi_alloc(pathlen * sizeof(WCHAR));
5412
5413     lstrcpynW(path, wildcard, dirlen + 1);
5414     lstrcatW(path, filename);
5415
5416     return path;
5417 }
5418
5419 static void free_file_entry(FILE_LIST *file)
5420 {
5421     msi_free(file->source);
5422     msi_free(file->dest);
5423     msi_free(file);
5424 }
5425
5426 static void free_list(FILE_LIST *list)
5427 {
5428     while (!list_empty(&list->entry))
5429     {
5430         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5431
5432         list_remove(&file->entry);
5433         free_file_entry(file);
5434     }
5435 }
5436
5437 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5438 {
5439     FILE_LIST *new, *file;
5440     LPWSTR ptr, filename;
5441     DWORD size;
5442
5443     new = msi_alloc_zero(sizeof(FILE_LIST));
5444     if (!new)
5445         return FALSE;
5446
5447     new->source = strdupW(source);
5448     ptr = strrchrW(dest, '\\') + 1;
5449     filename = strrchrW(new->source, '\\') + 1;
5450
5451     new->sourcename = filename;
5452
5453     if (*ptr)
5454         new->destname = ptr;
5455     else
5456         new->destname = new->sourcename;
5457
5458     size = (ptr - dest) + lstrlenW(filename) + 1;
5459     new->dest = msi_alloc(size * sizeof(WCHAR));
5460     if (!new->dest)
5461     {
5462         free_file_entry(new);
5463         return FALSE;
5464     }
5465
5466     lstrcpynW(new->dest, dest, ptr - dest + 1);
5467     lstrcatW(new->dest, filename);
5468
5469     if (list_empty(&files->entry))
5470     {
5471         list_add_head(&files->entry, &new->entry);
5472         return TRUE;
5473     }
5474
5475     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5476     {
5477         if (lstrcmpW(source, file->source) < 0)
5478         {
5479             list_add_before(&file->entry, &new->entry);
5480             return TRUE;
5481         }
5482     }
5483
5484     list_add_after(&file->entry, &new->entry);
5485     return TRUE;
5486 }
5487
5488 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5489 {
5490     WIN32_FIND_DATAW wfd;
5491     HANDLE hfile;
5492     LPWSTR path;
5493     BOOL res;
5494     FILE_LIST files, *file;
5495     DWORD size;
5496
5497     hfile = FindFirstFileW(source, &wfd);
5498     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5499
5500     list_init(&files.entry);
5501
5502     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5503     {
5504         if (is_dot_dir(wfd.cFileName)) continue;
5505
5506         path = wildcard_to_file(source, wfd.cFileName);
5507         if (!path)
5508         {
5509             res = FALSE;
5510             goto done;
5511         }
5512
5513         add_wildcard(&files, path, dest);
5514         msi_free(path);
5515     }
5516
5517     /* no files match the wildcard */
5518     if (list_empty(&files.entry))
5519         goto done;
5520
5521     /* only the first wildcard match gets renamed to dest */
5522     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5523     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5524     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5525     if (!file->dest)
5526     {
5527         res = FALSE;
5528         goto done;
5529     }
5530
5531     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5532
5533     while (!list_empty(&files.entry))
5534     {
5535         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5536
5537         msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5538
5539         list_remove(&file->entry);
5540         free_file_entry(file);
5541     }
5542
5543     res = TRUE;
5544
5545 done:
5546     free_list(&files);
5547     FindClose(hfile);
5548     return res;
5549 }
5550
5551 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5552 {
5553     MSIPACKAGE *package = param;
5554     MSICOMPONENT *comp;
5555     LPCWSTR sourcename;
5556     LPWSTR destname = NULL;
5557     LPWSTR sourcedir = NULL, destdir = NULL;
5558     LPWSTR source = NULL, dest = NULL;
5559     int options;
5560     DWORD size;
5561     BOOL ret, wildcards;
5562
5563     static const WCHAR backslash[] = {'\\',0};
5564
5565     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5566     if (!comp || !comp->Enabled ||
5567         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5568     {
5569         TRACE("Component not set for install, not moving file\n");
5570         return ERROR_SUCCESS;
5571     }
5572
5573     sourcename = MSI_RecordGetString(rec, 3);
5574     options = MSI_RecordGetInteger(rec, 7);
5575
5576     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5577     if (!sourcedir)
5578         goto done;
5579
5580     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5581     if (!destdir)
5582         goto done;
5583
5584     if (!sourcename)
5585     {
5586         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5587             goto done;
5588
5589         source = strdupW(sourcedir);
5590         if (!source)
5591             goto done;
5592     }
5593     else
5594     {
5595         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5596         source = msi_alloc(size * sizeof(WCHAR));
5597         if (!source)
5598             goto done;
5599
5600         lstrcpyW(source, sourcedir);
5601         if (source[lstrlenW(source) - 1] != '\\')
5602             lstrcatW(source, backslash);
5603         lstrcatW(source, sourcename);
5604     }
5605
5606     wildcards = strchrW(source, '*') || strchrW(source, '?');
5607
5608     if (MSI_RecordIsNull(rec, 4))
5609     {
5610         if (!wildcards)
5611         {
5612             destname = strdupW(sourcename);
5613             if (!destname)
5614                 goto done;
5615         }
5616     }
5617     else
5618     {
5619         destname = strdupW(MSI_RecordGetString(rec, 4));
5620         if (destname)
5621             reduce_to_longfilename(destname);
5622     }
5623
5624     size = 0;
5625     if (destname)
5626         size = lstrlenW(destname);
5627
5628     size += lstrlenW(destdir) + 2;
5629     dest = msi_alloc(size * sizeof(WCHAR));
5630     if (!dest)
5631         goto done;
5632
5633     lstrcpyW(dest, destdir);
5634     if (dest[lstrlenW(dest) - 1] != '\\')
5635         lstrcatW(dest, backslash);
5636
5637     if (destname)
5638         lstrcatW(dest, destname);
5639
5640     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5641     {
5642         ret = CreateDirectoryW(destdir, NULL);
5643         if (!ret)
5644         {
5645             WARN("CreateDirectory failed: %d\n", GetLastError());
5646             return ERROR_SUCCESS;
5647         }
5648     }
5649
5650     if (!wildcards)
5651         msi_move_file(source, dest, options);
5652     else
5653         move_files_wildcard(source, dest, options);
5654
5655 done:
5656     msi_free(sourcedir);
5657     msi_free(destdir);
5658     msi_free(destname);
5659     msi_free(source);
5660     msi_free(dest);
5661
5662     return ERROR_SUCCESS;
5663 }
5664
5665 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5666 {
5667     UINT rc;
5668     MSIQUERY *view;
5669
5670     static const WCHAR ExecSeqQuery[] =
5671         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5672          '`','M','o','v','e','F','i','l','e','`',0};
5673
5674     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5675     if (rc != ERROR_SUCCESS)
5676         return ERROR_SUCCESS;
5677
5678     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5679     msiobj_release(&view->hdr);
5680
5681     return rc;
5682 }
5683
5684 typedef struct tagMSIASSEMBLY
5685 {
5686     struct list entry;
5687     MSICOMPONENT *component;
5688     MSIFEATURE *feature;
5689     MSIFILE *file;
5690     LPWSTR manifest;
5691     LPWSTR application;
5692     DWORD attributes;
5693     BOOL installed;
5694 } MSIASSEMBLY;
5695
5696 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5697                                               DWORD dwReserved);
5698 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5699                                           LPVOID pvReserved, HMODULE *phModDll);
5700
5701 static BOOL init_functionpointers(void)
5702 {
5703     HRESULT hr;
5704     HMODULE hfusion;
5705     HMODULE hmscoree;
5706
5707     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5708
5709     hmscoree = LoadLibraryA("mscoree.dll");
5710     if (!hmscoree)
5711     {
5712         WARN("mscoree.dll not available\n");
5713         return FALSE;
5714     }
5715
5716     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5717     if (!pLoadLibraryShim)
5718     {
5719         WARN("LoadLibraryShim not available\n");
5720         FreeLibrary(hmscoree);
5721         return FALSE;
5722     }
5723
5724     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5725     if (FAILED(hr))
5726     {
5727         WARN("fusion.dll not available\n");
5728         FreeLibrary(hmscoree);
5729         return FALSE;
5730     }
5731
5732     pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5733
5734     FreeLibrary(hmscoree);
5735     return TRUE;
5736 }
5737
5738 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5739                              LPWSTR path)
5740 {
5741     IAssemblyCache *cache;
5742     HRESULT hr;
5743     UINT r = ERROR_FUNCTION_FAILED;
5744
5745     TRACE("installing assembly: %s\n", debugstr_w(path));
5746
5747     if (assembly->feature)
5748         msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5749
5750     if (assembly->manifest)
5751         FIXME("Manifest unhandled\n");
5752
5753     if (assembly->application)
5754     {
5755         FIXME("Assembly should be privately installed\n");
5756         return ERROR_SUCCESS;
5757     }
5758
5759     if (assembly->attributes == msidbAssemblyAttributesWin32)
5760     {
5761         FIXME("Win32 assemblies not handled\n");
5762         return ERROR_SUCCESS;
5763     }
5764
5765     if (!init_functionpointers() || !pCreateAssemblyCache)
5766         return ERROR_FUNCTION_FAILED;
5767
5768     hr = pCreateAssemblyCache(&cache, 0);
5769     if (FAILED(hr))
5770         goto done;
5771
5772     hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5773     if (FAILED(hr))
5774         ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5775
5776     r = ERROR_SUCCESS;
5777
5778 done:
5779     IAssemblyCache_Release(cache);
5780     return r;
5781 }
5782
5783 typedef struct tagASSEMBLY_LIST
5784 {
5785     MSIPACKAGE *package;
5786     struct list *assemblies;
5787 } ASSEMBLY_LIST;
5788
5789 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5790 {
5791     ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
5792     MSIASSEMBLY *assembly;
5793
5794     assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5795     if (!assembly)
5796         return ERROR_OUTOFMEMORY;
5797
5798     assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5799
5800     if (!assembly->component || !assembly->component->Enabled ||
5801         !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5802     {
5803         TRACE("Component not set for install, not publishing assembly\n");
5804         msi_free(assembly);
5805         return ERROR_SUCCESS;
5806     }
5807
5808     assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5809     assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5810
5811     if (!assembly->file)
5812     {
5813         ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5814         return ERROR_FUNCTION_FAILED;
5815     }
5816
5817     assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5818     assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5819     assembly->attributes = MSI_RecordGetInteger(rec, 5);
5820     assembly->installed = FALSE;
5821
5822     list_add_head(list->assemblies, &assembly->entry);
5823
5824     return ERROR_SUCCESS;
5825 }
5826
5827 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5828 {
5829     MSIQUERY *view;
5830     ASSEMBLY_LIST list;
5831     UINT r;
5832
5833     static const WCHAR query[] =
5834         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5835          '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5836
5837     r = MSI_DatabaseOpenViewW(package->db, query, &view);
5838     if (r != ERROR_SUCCESS)
5839         return ERROR_SUCCESS;
5840
5841     list.package = package;
5842     list.assemblies = assemblies;
5843
5844     r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5845     msiobj_release(&view->hdr);
5846
5847     return r;
5848 }
5849
5850 static void free_assemblies(struct list *assemblies)
5851 {
5852     struct list *item, *cursor;
5853
5854     LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5855     {
5856         MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5857
5858         list_remove(&assembly->entry);
5859         msi_free(assembly->application);
5860         msi_free(assembly->manifest);
5861         msi_free(assembly);
5862     }
5863 }
5864
5865 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
5866 {
5867     MSIASSEMBLY *assembly;
5868
5869     LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
5870     {
5871         if (!lstrcmpW(assembly->file->File, file))
5872         {
5873             *out = assembly;
5874             return TRUE;
5875         }
5876     }
5877
5878     return FALSE;
5879 }
5880
5881 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
5882                                LPWSTR *path, DWORD *attrs, PVOID user)
5883 {
5884     MSIASSEMBLY *assembly;
5885     WCHAR temppath[MAX_PATH];
5886     struct list *assemblies = (struct list *)user;
5887     UINT r;
5888
5889     if (!find_assembly(assemblies, file, &assembly))
5890         return FALSE;
5891
5892     GetTempPathW(MAX_PATH, temppath);
5893     PathAddBackslashW(temppath);
5894     lstrcatW(temppath, assembly->file->FileName);
5895
5896     if (action == MSICABEXTRACT_BEGINEXTRACT)
5897     {
5898         if (assembly->installed)
5899             return FALSE;
5900
5901         *path = strdupW(temppath);
5902         *attrs = assembly->file->Attributes;
5903     }
5904     else if (action == MSICABEXTRACT_FILEEXTRACTED)
5905     {
5906         assembly->installed = TRUE;
5907
5908         r = install_assembly(package, assembly, temppath);
5909         if (r != ERROR_SUCCESS)
5910             ERR("Failed to install assembly\n");
5911     }
5912
5913     return TRUE;
5914 }
5915
5916 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5917 {
5918     UINT r;
5919     struct list assemblies = LIST_INIT(assemblies);
5920     MSIASSEMBLY *assembly;
5921     MSIMEDIAINFO *mi;
5922
5923     r = load_assemblies(package, &assemblies);
5924     if (r != ERROR_SUCCESS)
5925         goto done;
5926
5927     if (list_empty(&assemblies))
5928         goto done;
5929
5930     mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
5931     if (!mi)
5932     {
5933         r = ERROR_OUTOFMEMORY;
5934         goto done;
5935     }
5936
5937     LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
5938     {
5939         if (assembly->installed && !mi->is_continuous)
5940             continue;
5941
5942         if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
5943             (assembly->file->IsCompressed && !mi->is_extracted))
5944         {
5945             MSICABDATA data;
5946
5947             r = ready_media(package, assembly->file, mi);
5948             if (r != ERROR_SUCCESS)
5949             {
5950                 ERR("Failed to ready media\n");
5951                 break;
5952             }
5953
5954             data.mi = mi;
5955             data.package = package;
5956             data.cb = installassembly_cb;
5957             data.user = &assemblies;
5958
5959             if (assembly->file->IsCompressed &&
5960                 !msi_cabextract(package, mi, &data))
5961             {
5962                 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
5963                 r = ERROR_FUNCTION_FAILED;
5964                 break;
5965             }
5966         }
5967
5968         if (!assembly->file->IsCompressed)
5969         {
5970             LPWSTR source = resolve_file_source(package, assembly->file);
5971
5972             r = install_assembly(package, assembly, source);
5973             if (r != ERROR_SUCCESS)
5974                 ERR("Failed to install assembly\n");
5975
5976             msi_free(source);
5977         }
5978
5979         /* FIXME: write Installer assembly reg values */
5980     }
5981
5982 done:
5983     free_assemblies(&assemblies);
5984     return r;
5985 }
5986
5987 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5988                                            LPCSTR action, LPCWSTR table )
5989 {
5990     static const WCHAR query[] = {
5991         'S','E','L','E','C','T',' ','*',' ',
5992         'F','R','O','M',' ','`','%','s','`',0 };
5993     MSIQUERY *view = NULL;
5994     DWORD count = 0;
5995     UINT r;
5996     
5997     r = MSI_OpenQuery( package->db, &view, query, table );
5998     if (r == ERROR_SUCCESS)
5999     {
6000         r = MSI_IterateRecords(view, &count, NULL, package);
6001         msiobj_release(&view->hdr);
6002     }
6003
6004     if (count)
6005         FIXME("%s -> %u ignored %s table values\n",
6006               action, count, debugstr_w(table));
6007
6008     return ERROR_SUCCESS;
6009 }
6010
6011 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6012 {
6013     TRACE("%p\n", package);
6014     return ERROR_SUCCESS;
6015 }
6016
6017 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6018 {
6019     static const WCHAR table[] =
6020          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6021     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6022 }
6023
6024 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6025 {
6026     static const WCHAR table[] = { 'P','a','t','c','h',0 };
6027     return msi_unimplemented_action_stub( package, "PatchFiles", table );
6028 }
6029
6030 static UINT ACTION_BindImage( MSIPACKAGE *package )
6031 {
6032     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6033     return msi_unimplemented_action_stub( package, "BindImage", table );
6034 }
6035
6036 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6037 {
6038     static const WCHAR table[] = {
6039         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6040     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6041 }
6042
6043 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6044 {
6045     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6046     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6047 }
6048
6049 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6050 {
6051     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6052     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6053 }
6054
6055 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6056 {
6057     static const WCHAR table[] = {
6058         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6059     return msi_unimplemented_action_stub( package, "DeleteServices", table );
6060 }
6061 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6062 {
6063         static const WCHAR table[] = {
6064                 'P','r','o','d','u','c','t','I','D',0 };
6065         return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6066 }
6067
6068 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6069 {
6070     static const WCHAR table[] = {
6071         'E','n','v','i','r','o','n','m','e','n','t',0 };
6072     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6073 }
6074
6075 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6076 {
6077     static const WCHAR table[] = {
6078         'M','s','i','A','s','s','e','m','b','l','y',0 };
6079     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6080 }
6081
6082 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6083 {
6084     static const WCHAR table[] = { 'F','o','n','t',0 };
6085     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6086 }
6087
6088 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6089 {
6090     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6091     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6092 }
6093
6094 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6095 {
6096     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6097     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6098 }
6099
6100 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6101 {
6102     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6103     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6104 }
6105
6106 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6107 {
6108     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6109     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6110 }
6111
6112 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6113 {
6114     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6115     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6116 }
6117
6118 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6119 {
6120     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6121     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6122 }
6123
6124 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6125 {
6126     static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6127     return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6128 }
6129
6130 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6131 {
6132     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6133     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6134 }
6135
6136 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6137 {
6138     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6139     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6140 }
6141
6142 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6143 {
6144     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6145     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6146 }
6147
6148 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6149 {
6150     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6151     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6152 }
6153
6154 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6155 {
6156     static const WCHAR table[] = { 'A','p','p','I','d',0 };
6157     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6158 }
6159
6160 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6161 {
6162     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6163     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6164 }
6165
6166 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6167 {
6168     static const WCHAR table[] = { 'M','I','M','E',0 };
6169     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6170 }
6171
6172 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6173 {
6174     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6175     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6176 }
6177
6178 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6179 {
6180     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6181     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6182 }
6183
6184 static const struct _actions StandardActions[] = {
6185     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6186     { szAppSearch, ACTION_AppSearch },
6187     { szBindImage, ACTION_BindImage },
6188     { szCCPSearch, ACTION_CCPSearch },
6189     { szCostFinalize, ACTION_CostFinalize },
6190     { szCostInitialize, ACTION_CostInitialize },
6191     { szCreateFolders, ACTION_CreateFolders },
6192     { szCreateShortcuts, ACTION_CreateShortcuts },
6193     { szDeleteServices, ACTION_DeleteServices },
6194     { szDisableRollback, NULL },
6195     { szDuplicateFiles, ACTION_DuplicateFiles },
6196     { szExecuteAction, ACTION_ExecuteAction },
6197     { szFileCost, ACTION_FileCost },
6198     { szFindRelatedProducts, ACTION_FindRelatedProducts },
6199     { szForceReboot, ACTION_ForceReboot },
6200     { szInstallAdminPackage, NULL },
6201     { szInstallExecute, ACTION_InstallExecute },
6202     { szInstallExecuteAgain, ACTION_InstallExecute },
6203     { szInstallFiles, ACTION_InstallFiles},
6204     { szInstallFinalize, ACTION_InstallFinalize },
6205     { szInstallInitialize, ACTION_InstallInitialize },
6206     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6207     { szInstallValidate, ACTION_InstallValidate },
6208     { szIsolateComponents, ACTION_IsolateComponents },
6209     { szLaunchConditions, ACTION_LaunchConditions },
6210     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6211     { szMoveFiles, ACTION_MoveFiles },
6212     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6213     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6214     { szInstallODBC, ACTION_InstallODBC },
6215     { szInstallServices, ACTION_InstallServices },
6216     { szPatchFiles, ACTION_PatchFiles },
6217     { szProcessComponents, ACTION_ProcessComponents },
6218     { szPublishComponents, ACTION_PublishComponents },
6219     { szPublishFeatures, ACTION_PublishFeatures },
6220     { szPublishProduct, ACTION_PublishProduct },
6221     { szRegisterClassInfo, ACTION_RegisterClassInfo },
6222     { szRegisterComPlus, ACTION_RegisterComPlus},
6223     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6224     { szRegisterFonts, ACTION_RegisterFonts },
6225     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6226     { szRegisterProduct, ACTION_RegisterProduct },
6227     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6228     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6229     { szRegisterUser, ACTION_RegisterUser },
6230     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6231     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6232     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6233     { szRemoveFiles, ACTION_RemoveFiles },
6234     { szRemoveFolders, ACTION_RemoveFolders },
6235     { szRemoveIniValues, ACTION_RemoveIniValues },
6236     { szRemoveODBC, ACTION_RemoveODBC },
6237     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6238     { szRemoveShortcuts, ACTION_RemoveShortcuts },
6239     { szResolveSource, ACTION_ResolveSource },
6240     { szRMCCPSearch, ACTION_RMCCPSearch },
6241     { szScheduleReboot, NULL },
6242     { szSelfRegModules, ACTION_SelfRegModules },
6243     { szSelfUnregModules, ACTION_SelfUnregModules },
6244     { szSetODBCFolders, NULL },
6245     { szStartServices, ACTION_StartServices },
6246     { szStopServices, ACTION_StopServices },
6247     { szUnpublishComponents, ACTION_UnpublishComponents },
6248     { szUnpublishFeatures, ACTION_UnpublishFeatures },
6249     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6250     { szUnregisterComPlus, ACTION_UnregisterComPlus },
6251     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6252     { szUnregisterFonts, ACTION_UnregisterFonts },
6253     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6254     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6255     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6256     { szValidateProductID, ACTION_ValidateProductID },
6257     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6258     { szWriteIniValues, ACTION_WriteIniValues },
6259     { szWriteRegistryValues, ACTION_WriteRegistryValues },
6260     { NULL, NULL },
6261 };
6262
6263 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6264                                         UINT* rc, BOOL force )
6265 {
6266     BOOL ret = FALSE;
6267     BOOL run = force;
6268     int i;
6269
6270     if (!run && !package->script->CurrentlyScripting)
6271         run = TRUE;
6272
6273     if (!run)
6274     {
6275         if (strcmpW(action,szInstallFinalize) == 0 ||
6276             strcmpW(action,szInstallExecute) == 0 ||
6277             strcmpW(action,szInstallExecuteAgain) == 0)
6278                 run = TRUE;
6279     }
6280
6281     i = 0;
6282     while (StandardActions[i].action != NULL)
6283     {
6284         if (strcmpW(StandardActions[i].action, action)==0)
6285         {
6286             if (!run)
6287             {
6288                 ui_actioninfo(package, action, TRUE, 0);
6289                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6290                 ui_actioninfo(package, action, FALSE, *rc);
6291             }
6292             else
6293             {
6294                 ui_actionstart(package, action);
6295                 if (StandardActions[i].handler)
6296                 {
6297                     *rc = StandardActions[i].handler(package);
6298                 }
6299                 else
6300                 {
6301                     FIXME("unhandled standard action %s\n",debugstr_w(action));
6302                     *rc = ERROR_SUCCESS;
6303                 }
6304             }
6305             ret = TRUE;
6306             break;
6307         }
6308         i++;
6309     }
6310     return ret;
6311 }