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