msi: Store the media type and set the last used source type accordingly.
[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         *type=REG_SZ;
2326
2327         if (value[0]=='#')
2328         {
2329             if (value[1]=='%')
2330             {
2331                 ptr = &value[2];
2332                 *type=REG_EXPAND_SZ;
2333             }
2334             else
2335                 ptr = &value[1];
2336          }
2337          else
2338             ptr=value;
2339
2340         if (strstrW(value,szMulti))
2341             *type = REG_MULTI_SZ;
2342
2343         /* remove initial delimiter */
2344         if (!strncmpW(value, szMulti, 3))
2345             ptr = value + 3;
2346
2347         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2348
2349         /* add double NULL terminator */
2350         if (*type == REG_MULTI_SZ)
2351         {
2352             *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2353             data = msi_realloc_zero(data, *size);
2354         }
2355     }
2356     return data;
2357 }
2358
2359 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2360 {
2361     MSIPACKAGE *package = (MSIPACKAGE*)param;
2362     static const WCHAR szHCR[] = 
2363         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2364          'R','O','O','T','\\',0};
2365     static const WCHAR szHCU[] =
2366         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2367          'U','S','E','R','\\',0};
2368     static const WCHAR szHLM[] =
2369         {'H','K','E','Y','_','L','O','C','A','L','_',
2370          'M','A','C','H','I','N','E','\\',0};
2371     static const WCHAR szHU[] =
2372         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2373
2374     LPSTR value_data = NULL;
2375     HKEY  root_key, hkey;
2376     DWORD type,size;
2377     LPWSTR  deformated;
2378     LPCWSTR szRoot, component, name, key, value;
2379     MSICOMPONENT *comp;
2380     MSIRECORD * uirow;
2381     LPWSTR uikey;
2382     INT   root;
2383     BOOL check_first = FALSE;
2384     UINT rc;
2385
2386     ui_progress(package,2,0,0,0);
2387
2388     value = NULL;
2389     key = NULL;
2390     uikey = NULL;
2391     name = NULL;
2392
2393     component = MSI_RecordGetString(row, 6);
2394     comp = get_loaded_component(package,component);
2395     if (!comp)
2396         return ERROR_SUCCESS;
2397
2398     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2399     {
2400         TRACE("Skipping write due to disabled component %s\n",
2401                         debugstr_w(component));
2402
2403         comp->Action = comp->Installed;
2404
2405         return ERROR_SUCCESS;
2406     }
2407
2408     comp->Action = INSTALLSTATE_LOCAL;
2409
2410     name = MSI_RecordGetString(row, 4);
2411     if( MSI_RecordIsNull(row,5) && name )
2412     {
2413         /* null values can have special meanings */
2414         if (name[0]=='-' && name[1] == 0)
2415                 return ERROR_SUCCESS;
2416         else if ((name[0]=='+' && name[1] == 0) || 
2417                  (name[0] == '*' && name[1] == 0))
2418                 name = NULL;
2419         check_first = TRUE;
2420     }
2421
2422     root = MSI_RecordGetInteger(row,2);
2423     key = MSI_RecordGetString(row, 3);
2424
2425     /* get the root key */
2426     switch (root)
2427     {
2428         case -1: 
2429             {
2430                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2431                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2432                 if (all_users && all_users[0] == '1')
2433                 {
2434                     root_key = HKEY_LOCAL_MACHINE;
2435                     szRoot = szHLM;
2436                 }
2437                 else
2438                 {
2439                     root_key = HKEY_CURRENT_USER;
2440                     szRoot = szHCU;
2441                 }
2442                 msi_free(all_users);
2443             }
2444                  break;
2445         case 0:  root_key = HKEY_CLASSES_ROOT; 
2446                  szRoot = szHCR;
2447                  break;
2448         case 1:  root_key = HKEY_CURRENT_USER;
2449                  szRoot = szHCU;
2450                  break;
2451         case 2:  root_key = HKEY_LOCAL_MACHINE;
2452                  szRoot = szHLM;
2453                  break;
2454         case 3:  root_key = HKEY_USERS; 
2455                  szRoot = szHU;
2456                  break;
2457         default:
2458                  ERR("Unknown root %i\n",root);
2459                  root_key=NULL;
2460                  szRoot = NULL;
2461                  break;
2462     }
2463     if (!root_key)
2464         return ERROR_SUCCESS;
2465
2466     deformat_string(package, key , &deformated);
2467     size = strlenW(deformated) + strlenW(szRoot) + 1;
2468     uikey = msi_alloc(size*sizeof(WCHAR));
2469     strcpyW(uikey,szRoot);
2470     strcatW(uikey,deformated);
2471
2472     if (RegCreateKeyW( root_key, deformated, &hkey))
2473     {
2474         ERR("Could not create key %s\n",debugstr_w(deformated));
2475         msi_free(deformated);
2476         msi_free(uikey);
2477         return ERROR_SUCCESS;
2478     }
2479     msi_free(deformated);
2480
2481     value = MSI_RecordGetString(row,5);
2482     if (value)
2483         value_data = parse_value(package, value, &type, &size); 
2484     else
2485     {
2486         static const WCHAR szEmpty[] = {0};
2487         value_data = (LPSTR)strdupW(szEmpty);
2488         size = 0;
2489         type = REG_SZ;
2490     }
2491
2492     deformat_string(package, name, &deformated);
2493
2494     if (!check_first)
2495     {
2496         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2497                         debugstr_w(uikey));
2498         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2499     }
2500     else
2501     {
2502         DWORD sz = 0;
2503         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2504         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2505         {
2506             TRACE("value %s of %s checked already exists\n",
2507                             debugstr_w(deformated), debugstr_w(uikey));
2508         }
2509         else
2510         {
2511             TRACE("Checked and setting value %s of %s\n",
2512                             debugstr_w(deformated), debugstr_w(uikey));
2513             if (deformated || size)
2514                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2515         }
2516     }
2517     RegCloseKey(hkey);
2518
2519     uirow = MSI_CreateRecord(3);
2520     MSI_RecordSetStringW(uirow,2,deformated);
2521     MSI_RecordSetStringW(uirow,1,uikey);
2522
2523     if (type == REG_SZ)
2524         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2525     else
2526         MSI_RecordSetStringW(uirow,3,value);
2527
2528     ui_actiondata(package,szWriteRegistryValues,uirow);
2529     msiobj_release( &uirow->hdr );
2530
2531     msi_free(value_data);
2532     msi_free(deformated);
2533     msi_free(uikey);
2534
2535     return ERROR_SUCCESS;
2536 }
2537
2538 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2539 {
2540     UINT rc;
2541     MSIQUERY * view;
2542     static const WCHAR ExecSeqQuery[] =
2543         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2544          '`','R','e','g','i','s','t','r','y','`',0 };
2545
2546     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2547     if (rc != ERROR_SUCCESS)
2548         return ERROR_SUCCESS;
2549
2550     /* increment progress bar each time action data is sent */
2551     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2552
2553     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2554
2555     msiobj_release(&view->hdr);
2556     return rc;
2557 }
2558
2559 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2560 {
2561     package->script->CurrentlyScripting = TRUE;
2562
2563     return ERROR_SUCCESS;
2564 }
2565
2566
2567 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2568 {
2569     MSICOMPONENT *comp;
2570     DWORD progress = 0;
2571     DWORD total = 0;
2572     static const WCHAR q1[]=
2573         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2574          '`','R','e','g','i','s','t','r','y','`',0};
2575     UINT rc;
2576     MSIQUERY * view;
2577     MSIFEATURE *feature;
2578     MSIFILE *file;
2579
2580     TRACE("InstallValidate\n");
2581
2582     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2583     if (rc == ERROR_SUCCESS)
2584     {
2585         MSI_IterateRecords( view, &progress, NULL, package );
2586         msiobj_release( &view->hdr );
2587         total += progress * REG_PROGRESS_VALUE;
2588     }
2589
2590     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2591         total += COMPONENT_PROGRESS_VALUE;
2592
2593     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2594         total += file->FileSize;
2595
2596     ui_progress(package,0,total,0,0);
2597
2598     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2599     {
2600         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2601             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2602             feature->ActionRequest);
2603     }
2604     
2605     return ERROR_SUCCESS;
2606 }
2607
2608 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2609 {
2610     MSIPACKAGE* package = (MSIPACKAGE*)param;
2611     LPCWSTR cond = NULL; 
2612     LPCWSTR message = NULL;
2613     UINT r;
2614
2615     static const WCHAR title[]=
2616         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2617
2618     cond = MSI_RecordGetString(row,1);
2619
2620     r = MSI_EvaluateConditionW(package,cond);
2621     if (r == MSICONDITION_FALSE)
2622     {
2623         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2624         {
2625             LPWSTR deformated;
2626             message = MSI_RecordGetString(row,2);
2627             deformat_string(package,message,&deformated);
2628             MessageBoxW(NULL,deformated,title,MB_OK);
2629             msi_free(deformated);
2630         }
2631
2632         return ERROR_INSTALL_FAILURE;
2633     }
2634
2635     return ERROR_SUCCESS;
2636 }
2637
2638 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2639 {
2640     UINT rc;
2641     MSIQUERY * view = NULL;
2642     static const WCHAR ExecSeqQuery[] =
2643         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2644          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2645
2646     TRACE("Checking launch conditions\n");
2647
2648     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2649     if (rc != ERROR_SUCCESS)
2650         return ERROR_SUCCESS;
2651
2652     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2653     msiobj_release(&view->hdr);
2654
2655     return rc;
2656 }
2657
2658 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2659 {
2660
2661     if (!cmp->KeyPath)
2662         return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2663
2664     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2665     {
2666         MSIRECORD * row = 0;
2667         UINT root,len;
2668         LPWSTR deformated,buffer,deformated_name;
2669         LPCWSTR key,name;
2670         static const WCHAR ExecSeqQuery[] =
2671             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2672              '`','R','e','g','i','s','t','r','y','`',' ',
2673              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2674              ' ','=',' ' ,'\'','%','s','\'',0 };
2675         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2676         static const WCHAR fmt2[]=
2677             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2678
2679         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2680         if (!row)
2681             return NULL;
2682
2683         root = MSI_RecordGetInteger(row,2);
2684         key = MSI_RecordGetString(row, 3);
2685         name = MSI_RecordGetString(row, 4);
2686         deformat_string(package, key , &deformated);
2687         deformat_string(package, name, &deformated_name);
2688
2689         len = strlenW(deformated) + 6;
2690         if (deformated_name)
2691             len+=strlenW(deformated_name);
2692
2693         buffer = msi_alloc( len *sizeof(WCHAR));
2694
2695         if (deformated_name)
2696             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2697         else
2698             sprintfW(buffer,fmt,root,deformated);
2699
2700         msi_free(deformated);
2701         msi_free(deformated_name);
2702         msiobj_release(&row->hdr);
2703
2704         return buffer;
2705     }
2706     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2707     {
2708         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2709         return NULL;
2710     }
2711     else
2712     {
2713         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2714
2715         if (file)
2716             return strdupW( file->TargetPath );
2717     }
2718     return NULL;
2719 }
2720
2721 static HKEY openSharedDLLsKey(void)
2722 {
2723     HKEY hkey=0;
2724     static const WCHAR path[] =
2725         {'S','o','f','t','w','a','r','e','\\',
2726          'M','i','c','r','o','s','o','f','t','\\',
2727          'W','i','n','d','o','w','s','\\',
2728          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2729          'S','h','a','r','e','d','D','L','L','s',0};
2730
2731     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2732     return hkey;
2733 }
2734
2735 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2736 {
2737     HKEY hkey;
2738     DWORD count=0;
2739     DWORD type;
2740     DWORD sz = sizeof(count);
2741     DWORD rc;
2742     
2743     hkey = openSharedDLLsKey();
2744     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2745     if (rc != ERROR_SUCCESS)
2746         count = 0;
2747     RegCloseKey(hkey);
2748     return count;
2749 }
2750
2751 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2752 {
2753     HKEY hkey;
2754
2755     hkey = openSharedDLLsKey();
2756     if (count > 0)
2757         msi_reg_set_val_dword( hkey, path, count );
2758     else
2759         RegDeleteValueW(hkey,path);
2760     RegCloseKey(hkey);
2761     return count;
2762 }
2763
2764 /*
2765  * Return TRUE if the count should be written out and FALSE if not
2766  */
2767 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2768 {
2769     MSIFEATURE *feature;
2770     INT count = 0;
2771     BOOL write = FALSE;
2772
2773     /* only refcount DLLs */
2774     if (comp->KeyPath == NULL || 
2775         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2776         comp->Attributes & msidbComponentAttributesODBCDataSource)
2777         write = FALSE;
2778     else
2779     {
2780         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2781         write = (count > 0);
2782
2783         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2784             write = TRUE;
2785     }
2786
2787     /* increment counts */
2788     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2789     {
2790         ComponentList *cl;
2791
2792         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2793             continue;
2794
2795         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2796         {
2797             if ( cl->component == comp )
2798                 count++;
2799         }
2800     }
2801
2802     /* decrement counts */
2803     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2804     {
2805         ComponentList *cl;
2806
2807         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2808             continue;
2809
2810         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2811         {
2812             if ( cl->component == comp )
2813                 count--;
2814         }
2815     }
2816
2817     /* ref count all the files in the component */
2818     if (write)
2819     {
2820         MSIFILE *file;
2821
2822         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2823         {
2824             if (file->Component == comp)
2825                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2826         }
2827     }
2828     
2829     /* add a count for permenent */
2830     if (comp->Attributes & msidbComponentAttributesPermanent)
2831         count ++;
2832     
2833     comp->RefCount = count;
2834
2835     if (write)
2836         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2837 }
2838
2839 /*
2840  * Ok further analysis makes me think that this work is
2841  * actually done in the PublishComponents and PublishFeatures
2842  * step, and not here.  It appears like the keypath and all that is
2843  * resolved in this step, however actually written in the Publish steps.
2844  * But we will leave it here for now because it is unclear
2845  */
2846 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2847 {
2848     WCHAR squished_pc[GUID_SIZE];
2849     WCHAR squished_cc[GUID_SIZE];
2850     UINT rc;
2851     MSICOMPONENT *comp;
2852     HKEY hkey=0,hkey2=0;
2853
2854     TRACE("\n");
2855
2856     /* writes the Component and Features values to the registry */
2857
2858     rc = MSIREG_OpenComponents(&hkey);
2859     if (rc != ERROR_SUCCESS)
2860         return rc;
2861
2862     squash_guid(package->ProductCode,squished_pc);
2863     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2864
2865     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2866     {
2867         MSIRECORD * uirow;
2868
2869         ui_progress(package,2,0,0,0);
2870         if (!comp->ComponentId)
2871             continue;
2872
2873         squash_guid(comp->ComponentId,squished_cc);
2874
2875         msi_free(comp->FullKeypath);
2876         comp->FullKeypath = resolve_keypath( package, comp );
2877
2878         /* do the refcounting */
2879         ACTION_RefCountComponent( package, comp );
2880
2881         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2882                             debugstr_w(comp->Component),
2883                             debugstr_w(squished_cc),
2884                             debugstr_w(comp->FullKeypath),
2885                             comp->RefCount);
2886         /*
2887          * Write the keypath out if the component is to be registered
2888          * and delete the key if the component is to be deregistered
2889          */
2890         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2891         {
2892             rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2893             if (rc != ERROR_SUCCESS)
2894                 continue;
2895
2896             if (!comp->FullKeypath)
2897                 continue;
2898
2899             msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2900
2901             if (comp->Attributes & msidbComponentAttributesPermanent)
2902             {
2903                 static const WCHAR szPermKey[] =
2904                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2905                       '0','0','0','0','0','0','0','0','0','0','0','0',
2906                       '0','0','0','0','0','0','0','0',0 };
2907
2908                 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2909             }
2910
2911             RegCloseKey(hkey2);
2912
2913             rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2914             if (rc != ERROR_SUCCESS)
2915                 continue;
2916
2917             msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2918             RegCloseKey(hkey2);
2919         }
2920         else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2921         {
2922             DWORD res;
2923
2924             rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2925             if (rc != ERROR_SUCCESS)
2926                 continue;
2927
2928             RegDeleteValueW(hkey2,squished_pc);
2929
2930             /* if the key is empty delete it */
2931             res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2932             RegCloseKey(hkey2);
2933             if (res == ERROR_NO_MORE_ITEMS)
2934                 RegDeleteKeyW(hkey,squished_cc);
2935
2936             MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2937         }
2938
2939         /* UI stuff */
2940         uirow = MSI_CreateRecord(3);
2941         MSI_RecordSetStringW(uirow,1,package->ProductCode);
2942         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2943         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2944         ui_actiondata(package,szProcessComponents,uirow);
2945         msiobj_release( &uirow->hdr );
2946     }
2947     RegCloseKey(hkey);
2948     return rc;
2949 }
2950
2951 typedef struct {
2952     CLSID       clsid;
2953     LPWSTR      source;
2954
2955     LPWSTR      path;
2956     ITypeLib    *ptLib;
2957 } typelib_struct;
2958
2959 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
2960                                        LPWSTR lpszName, LONG_PTR lParam)
2961 {
2962     TLIBATTR *attr;
2963     typelib_struct *tl_struct = (typelib_struct*) lParam;
2964     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2965     int sz; 
2966     HRESULT res;
2967
2968     if (!IS_INTRESOURCE(lpszName))
2969     {
2970         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2971         return TRUE;
2972     }
2973
2974     sz = strlenW(tl_struct->source)+4;
2975     sz *= sizeof(WCHAR);
2976
2977     if ((INT_PTR)lpszName == 1)
2978         tl_struct->path = strdupW(tl_struct->source);
2979     else
2980     {
2981         tl_struct->path = msi_alloc(sz);
2982         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2983     }
2984
2985     TRACE("trying %s\n", debugstr_w(tl_struct->path));
2986     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2987     if (!SUCCEEDED(res))
2988     {
2989         msi_free(tl_struct->path);
2990         tl_struct->path = NULL;
2991
2992         return TRUE;
2993     }
2994
2995     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2996     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2997     {
2998         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2999         return FALSE;
3000     }
3001
3002     msi_free(tl_struct->path);
3003     tl_struct->path = NULL;
3004
3005     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3006     ITypeLib_Release(tl_struct->ptLib);
3007
3008     return TRUE;
3009 }
3010
3011 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3012 {
3013     MSIPACKAGE* package = (MSIPACKAGE*)param;
3014     LPCWSTR component;
3015     MSICOMPONENT *comp;
3016     MSIFILE *file;
3017     typelib_struct tl_struct;
3018     HMODULE module;
3019     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3020
3021     component = MSI_RecordGetString(row,3);
3022     comp = get_loaded_component(package,component);
3023     if (!comp)
3024         return ERROR_SUCCESS;
3025
3026     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3027     {
3028         TRACE("Skipping typelib reg due to disabled component\n");
3029
3030         comp->Action = comp->Installed;
3031
3032         return ERROR_SUCCESS;
3033     }
3034
3035     comp->Action = INSTALLSTATE_LOCAL;
3036
3037     file = get_loaded_file( package, comp->KeyPath ); 
3038     if (!file)
3039         return ERROR_SUCCESS;
3040
3041     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3042     if (module)
3043     {
3044         LPCWSTR guid;
3045         guid = MSI_RecordGetString(row,1);
3046         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3047         tl_struct.source = strdupW( file->TargetPath );
3048         tl_struct.path = NULL;
3049
3050         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3051                         (LONG_PTR)&tl_struct);
3052
3053         if (tl_struct.path)
3054         {
3055             LPWSTR help = NULL;
3056             LPCWSTR helpid;
3057             HRESULT res;
3058
3059             helpid = MSI_RecordGetString(row,6);
3060
3061             if (helpid)
3062                 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3063             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3064             msi_free(help);
3065
3066             if (!SUCCEEDED(res))
3067                 ERR("Failed to register type library %s\n",
3068                         debugstr_w(tl_struct.path));
3069             else
3070             {
3071                 ui_actiondata(package,szRegisterTypeLibraries,row);
3072
3073                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3074             }
3075
3076             ITypeLib_Release(tl_struct.ptLib);
3077             msi_free(tl_struct.path);
3078         }
3079         else
3080             ERR("Failed to load type library %s\n",
3081                     debugstr_w(tl_struct.source));
3082
3083         FreeLibrary(module);
3084         msi_free(tl_struct.source);
3085     }
3086     else
3087         ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3088
3089     return ERROR_SUCCESS;
3090 }
3091
3092 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3093 {
3094     /* 
3095      * OK this is a bit confusing.. I am given a _Component key and I believe
3096      * that the file that is being registered as a type library is the "key file
3097      * of that component" which I interpret to mean "The file in the KeyPath of
3098      * that component".
3099      */
3100     UINT rc;
3101     MSIQUERY * view;
3102     static const WCHAR Query[] =
3103         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3104          '`','T','y','p','e','L','i','b','`',0};
3105
3106     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3107     if (rc != ERROR_SUCCESS)
3108         return ERROR_SUCCESS;
3109
3110     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3111     msiobj_release(&view->hdr);
3112     return rc;
3113 }
3114
3115 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3116 {
3117     MSIPACKAGE *package = (MSIPACKAGE*)param;
3118     LPWSTR target_file, target_folder, filename;
3119     LPCWSTR buffer, extension;
3120     MSICOMPONENT *comp;
3121     static const WCHAR szlnk[]={'.','l','n','k',0};
3122     IShellLinkW *sl = NULL;
3123     IPersistFile *pf = NULL;
3124     HRESULT res;
3125
3126     buffer = MSI_RecordGetString(row,4);
3127     comp = get_loaded_component(package,buffer);
3128     if (!comp)
3129         return ERROR_SUCCESS;
3130
3131     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3132     {
3133         TRACE("Skipping shortcut creation due to disabled component\n");
3134
3135         comp->Action = comp->Installed;
3136
3137         return ERROR_SUCCESS;
3138     }
3139
3140     comp->Action = INSTALLSTATE_LOCAL;
3141
3142     ui_actiondata(package,szCreateShortcuts,row);
3143
3144     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3145                     &IID_IShellLinkW, (LPVOID *) &sl );
3146
3147     if (FAILED( res ))
3148     {
3149         ERR("CLSID_ShellLink not available\n");
3150         goto err;
3151     }
3152
3153     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3154     if (FAILED( res ))
3155     {
3156         ERR("QueryInterface(IID_IPersistFile) failed\n");
3157         goto err;
3158     }
3159
3160     buffer = MSI_RecordGetString(row,2);
3161     target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3162
3163     /* may be needed because of a bug somehwere else */
3164     create_full_pathW(target_folder);
3165
3166     filename = msi_dup_record_field( row, 3 );
3167     reduce_to_longfilename(filename);
3168
3169     extension = strchrW(filename,'.');
3170     if (!extension || strcmpiW(extension,szlnk))
3171     {
3172         int len = strlenW(filename);
3173         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3174         memcpy(filename + len, szlnk, sizeof(szlnk));
3175     }
3176     target_file = build_directory_name(2, target_folder, filename);
3177     msi_free(target_folder);
3178     msi_free(filename);
3179
3180     buffer = MSI_RecordGetString(row,5);
3181     if (strchrW(buffer,'['))
3182     {
3183         LPWSTR deformated;
3184         deformat_string(package,buffer,&deformated);
3185         IShellLinkW_SetPath(sl,deformated);
3186         msi_free(deformated);
3187     }
3188     else
3189     {
3190         FIXME("poorly handled shortcut format, advertised shortcut\n");
3191         IShellLinkW_SetPath(sl,comp->FullKeypath);
3192     }
3193
3194     if (!MSI_RecordIsNull(row,6))
3195     {
3196         LPWSTR deformated;
3197         buffer = MSI_RecordGetString(row,6);
3198         deformat_string(package,buffer,&deformated);
3199         IShellLinkW_SetArguments(sl,deformated);
3200         msi_free(deformated);
3201     }
3202
3203     if (!MSI_RecordIsNull(row,7))
3204     {
3205         buffer = MSI_RecordGetString(row,7);
3206         IShellLinkW_SetDescription(sl,buffer);
3207     }
3208
3209     if (!MSI_RecordIsNull(row,8))
3210         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3211
3212     if (!MSI_RecordIsNull(row,9))
3213     {
3214         LPWSTR Path;
3215         INT index; 
3216
3217         buffer = MSI_RecordGetString(row,9);
3218
3219         Path = build_icon_path(package,buffer);
3220         index = MSI_RecordGetInteger(row,10);
3221
3222         /* no value means 0 */
3223         if (index == MSI_NULL_INTEGER)
3224             index = 0;
3225
3226         IShellLinkW_SetIconLocation(sl,Path,index);
3227         msi_free(Path);
3228     }
3229
3230     if (!MSI_RecordIsNull(row,11))
3231         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3232
3233     if (!MSI_RecordIsNull(row,12))
3234     {
3235         LPWSTR Path;
3236         buffer = MSI_RecordGetString(row,12);
3237         Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3238         if (Path)
3239             IShellLinkW_SetWorkingDirectory(sl,Path);
3240         msi_free(Path);
3241     }
3242
3243     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3244     IPersistFile_Save(pf,target_file,FALSE);
3245
3246     msi_free(target_file);    
3247
3248 err:
3249     if (pf)
3250         IPersistFile_Release( pf );
3251     if (sl)
3252         IShellLinkW_Release( sl );
3253
3254     return ERROR_SUCCESS;
3255 }
3256
3257 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3258 {
3259     UINT rc;
3260     HRESULT res;
3261     MSIQUERY * view;
3262     static const WCHAR Query[] =
3263         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3264          '`','S','h','o','r','t','c','u','t','`',0};
3265
3266     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3267     if (rc != ERROR_SUCCESS)
3268         return ERROR_SUCCESS;
3269
3270     res = CoInitialize( NULL );
3271     if (FAILED (res))
3272     {
3273         ERR("CoInitialize failed\n");
3274         return ERROR_FUNCTION_FAILED;
3275     }
3276
3277     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3278     msiobj_release(&view->hdr);
3279
3280     CoUninitialize();
3281
3282     return rc;
3283 }
3284
3285 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3286 {
3287     MSIPACKAGE* package = (MSIPACKAGE*)param;
3288     HANDLE the_file;
3289     LPWSTR FilePath;
3290     LPCWSTR FileName;
3291     CHAR buffer[1024];
3292     DWORD sz;
3293     UINT rc;
3294     MSIRECORD *uirow;
3295
3296     FileName = MSI_RecordGetString(row,1);
3297     if (!FileName)
3298     {
3299         ERR("Unable to get FileName\n");
3300         return ERROR_SUCCESS;
3301     }
3302
3303     FilePath = build_icon_path(package,FileName);
3304
3305     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3306
3307     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3308                         FILE_ATTRIBUTE_NORMAL, NULL);
3309
3310     if (the_file == INVALID_HANDLE_VALUE)
3311     {
3312         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3313         msi_free(FilePath);
3314         return ERROR_SUCCESS;
3315     }
3316
3317     do 
3318     {
3319         DWORD write;
3320         sz = 1024;
3321         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3322         if (rc != ERROR_SUCCESS)
3323         {
3324             ERR("Failed to get stream\n");
3325             CloseHandle(the_file);  
3326             DeleteFileW(FilePath);
3327             break;
3328         }
3329         WriteFile(the_file,buffer,sz,&write,NULL);
3330     } while (sz == 1024);
3331
3332     msi_free(FilePath);
3333
3334     CloseHandle(the_file);
3335
3336     uirow = MSI_CreateRecord(1);
3337     MSI_RecordSetStringW(uirow,1,FileName);
3338     ui_actiondata(package,szPublishProduct,uirow);
3339     msiobj_release( &uirow->hdr );
3340
3341     return ERROR_SUCCESS;
3342 }
3343
3344 static BOOL msi_check_publish(MSIPACKAGE *package)
3345 {
3346     MSIFEATURE *feature;
3347
3348     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3349     {
3350         if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3351             return TRUE;
3352     }
3353
3354     return FALSE;
3355 }
3356
3357 /*
3358  * 99% of the work done here is only done for 
3359  * advertised installs. However this is where the
3360  * Icon table is processed and written out
3361  * so that is what I am going to do here.
3362  */
3363 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3364 {
3365     UINT rc;
3366     MSIQUERY * view;
3367     MSISOURCELISTINFO *info;
3368     MSIMEDIADISK *disk;
3369     static const WCHAR Query[]=
3370         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3371          '`','I','c','o','n','`',0};
3372     /* for registry stuff */
3373     HKEY hkey=0;
3374     HKEY hukey=0;
3375     HKEY hudkey=0, props=0;
3376     HKEY source;
3377     static const WCHAR szProductLanguage[] =
3378         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3379     static const WCHAR szARPProductIcon[] =
3380         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3381     static const WCHAR szProductVersion[] =
3382         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3383     static const WCHAR szSourceList[] =
3384         {'S','o','u','r','c','e','L','i','s','t',0};
3385     static const WCHAR szEmpty[] = {0};
3386     DWORD langid;
3387     LPWSTR buffer;
3388     DWORD size;
3389     MSIHANDLE hDb, hSumInfo;
3390
3391     /* FIXME: also need to publish if the product is in advertise mode */
3392     if (!msi_check_publish(package))
3393         return ERROR_SUCCESS;
3394
3395     /* write out icon files */
3396
3397     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3398     if (rc == ERROR_SUCCESS)
3399     {
3400         MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3401         msiobj_release(&view->hdr);
3402     }
3403
3404     /* ok there is a lot more done here but i need to figure out what */
3405
3406     rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3407     if (rc != ERROR_SUCCESS)
3408         goto end;
3409
3410     rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3411     if (rc != ERROR_SUCCESS)
3412         goto end;
3413
3414     rc = RegCreateKeyW(hukey, szSourceList, &source);
3415     if (rc != ERROR_SUCCESS)
3416         goto end;
3417
3418     RegCloseKey(source);
3419
3420     rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3421     if (rc != ERROR_SUCCESS)
3422         goto end;
3423
3424     rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3425     if (rc != ERROR_SUCCESS)
3426         goto end;
3427
3428     buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3429     msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3430     msi_free(buffer);
3431
3432     langid = msi_get_property_int( package, szProductLanguage, 0 );
3433     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3434
3435     buffer = msi_dup_property( package, szARPProductIcon );
3436     if (buffer)
3437     {
3438         LPWSTR path = build_icon_path(package,buffer);
3439         msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3440         msi_free( path );
3441     }
3442     msi_free(buffer);
3443
3444     buffer = msi_dup_property( package, szProductVersion );
3445     if (buffer)
3446     {
3447         DWORD verdword = msi_version_str_to_dword(buffer);
3448         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3449     }
3450     msi_free(buffer);
3451
3452     buffer = strrchrW( package->PackagePath, '\\') + 1;
3453     rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3454                                 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
3455                                 INSTALLPROPERTY_PACKAGENAMEW, buffer );
3456     if (rc != ERROR_SUCCESS)
3457         goto end;
3458
3459     rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3460                                 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
3461                                 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty );
3462     if (rc != ERROR_SUCCESS)
3463         goto end;
3464
3465     rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3466                                 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
3467                                 INSTALLPROPERTY_DISKPROMPTW, szEmpty );
3468     if (rc != ERROR_SUCCESS)
3469         goto end;
3470
3471     /* FIXME: Need to write more keys to the user registry */
3472   
3473     hDb= alloc_msihandle( &package->db->hdr );
3474     if (!hDb) {
3475         rc = ERROR_NOT_ENOUGH_MEMORY;
3476         goto end;
3477     }
3478     rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo); 
3479     MsiCloseHandle(hDb);
3480     if (rc == ERROR_SUCCESS)
3481     {
3482         WCHAR guidbuffer[0x200];
3483         size = 0x200;
3484         rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3485                                         guidbuffer, &size);
3486         if (rc == ERROR_SUCCESS)
3487         {
3488             WCHAR squashed[GUID_SIZE];
3489             /* for now we only care about the first guid */
3490             LPWSTR ptr = strchrW(guidbuffer,';');
3491             if (ptr) *ptr = 0;
3492             squash_guid(guidbuffer,squashed);
3493             msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3494         }
3495         else
3496         {
3497             ERR("Unable to query Revision_Number...\n");
3498             rc = ERROR_SUCCESS;
3499         }
3500         MsiCloseHandle(hSumInfo);
3501     }
3502     else
3503     {
3504         ERR("Unable to open Summary Information\n");
3505         rc = ERROR_SUCCESS;
3506     }
3507
3508     /* publish the SourceList info */
3509     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3510     {
3511         MsiSourceListSetInfoW(package->ProductCode, NULL,
3512                               info->context, info->options,
3513                               info->property, info->value);
3514     }
3515
3516     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3517     {
3518         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3519                                    disk->context, disk->options,
3520                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3521     }
3522
3523 end:
3524     RegCloseKey(hkey);
3525     RegCloseKey(hukey);
3526     RegCloseKey(hudkey);
3527     RegCloseKey(props);
3528
3529     return rc;
3530 }
3531
3532 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3533 {
3534     MSIPACKAGE *package = (MSIPACKAGE*)param;
3535     LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3536     LPWSTR deformated_section, deformated_key, deformated_value;
3537     LPWSTR folder, fullname = NULL;
3538     MSIRECORD * uirow;
3539     INT action;
3540     MSICOMPONENT *comp;
3541     static const WCHAR szWindowsFolder[] =
3542           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3543
3544     component = MSI_RecordGetString(row, 8);
3545     comp = get_loaded_component(package,component);
3546
3547     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3548     {
3549         TRACE("Skipping ini file due to disabled component %s\n",
3550                         debugstr_w(component));
3551
3552         comp->Action = comp->Installed;
3553
3554         return ERROR_SUCCESS;
3555     }
3556
3557     comp->Action = INSTALLSTATE_LOCAL;
3558
3559     identifier = MSI_RecordGetString(row,1); 
3560     filename = MSI_RecordGetString(row,2);
3561     dirproperty = MSI_RecordGetString(row,3);
3562     section = MSI_RecordGetString(row,4);
3563     key = MSI_RecordGetString(row,5);
3564     value = MSI_RecordGetString(row,6);
3565     action = MSI_RecordGetInteger(row,7);
3566
3567     deformat_string(package,section,&deformated_section);
3568     deformat_string(package,key,&deformated_key);
3569     deformat_string(package,value,&deformated_value);
3570
3571     if (dirproperty)
3572     {
3573         folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3574         if (!folder)
3575             folder = msi_dup_property( package, dirproperty );
3576     }
3577     else
3578         folder = msi_dup_property( package, szWindowsFolder );
3579
3580     if (!folder)
3581     {
3582         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3583         goto cleanup;
3584     }
3585
3586     fullname = build_directory_name(2, folder, filename);
3587
3588     if (action == 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         WritePrivateProfileStringW(deformated_section, deformated_key,
3594                                    deformated_value, fullname);
3595     }
3596     else if (action == 1)
3597     {
3598         WCHAR returned[10];
3599         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3600                                  returned, 10, fullname);
3601         if (returned[0] == 0)
3602         {
3603             TRACE("Adding value %s to section %s in %s\n",
3604                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3605                     debugstr_w(fullname));
3606
3607             WritePrivateProfileStringW(deformated_section, deformated_key,
3608                                        deformated_value, fullname);
3609         }
3610     }
3611     else if (action == 3)
3612         FIXME("Append to existing section not yet implemented\n");
3613
3614     uirow = MSI_CreateRecord(4);
3615     MSI_RecordSetStringW(uirow,1,identifier);
3616     MSI_RecordSetStringW(uirow,2,deformated_section);
3617     MSI_RecordSetStringW(uirow,3,deformated_key);
3618     MSI_RecordSetStringW(uirow,4,deformated_value);
3619     ui_actiondata(package,szWriteIniValues,uirow);
3620     msiobj_release( &uirow->hdr );
3621 cleanup:
3622     msi_free(fullname);
3623     msi_free(folder);
3624     msi_free(deformated_key);
3625     msi_free(deformated_value);
3626     msi_free(deformated_section);
3627     return ERROR_SUCCESS;
3628 }
3629
3630 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3631 {
3632     UINT rc;
3633     MSIQUERY * view;
3634     static const WCHAR ExecSeqQuery[] = 
3635         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3636          '`','I','n','i','F','i','l','e','`',0};
3637
3638     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3639     if (rc != ERROR_SUCCESS)
3640     {
3641         TRACE("no IniFile table\n");
3642         return ERROR_SUCCESS;
3643     }
3644
3645     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3646     msiobj_release(&view->hdr);
3647     return rc;
3648 }
3649
3650 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3651 {
3652     MSIPACKAGE *package = (MSIPACKAGE*)param;
3653     LPCWSTR filename;
3654     LPWSTR FullName;
3655     MSIFILE *file;
3656     DWORD len;
3657     static const WCHAR ExeStr[] =
3658         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3659     static const WCHAR close[] =  {'\"',0};
3660     STARTUPINFOW si;
3661     PROCESS_INFORMATION info;
3662     BOOL brc;
3663     MSIRECORD *uirow;
3664     LPWSTR uipath, p;
3665
3666     memset(&si,0,sizeof(STARTUPINFOW));
3667
3668     filename = MSI_RecordGetString(row,1);
3669     file = get_loaded_file( package, filename );
3670
3671     if (!file)
3672     {
3673         ERR("Unable to find file id %s\n",debugstr_w(filename));
3674         return ERROR_SUCCESS;
3675     }
3676
3677     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3678
3679     FullName = msi_alloc(len*sizeof(WCHAR));
3680     strcpyW(FullName,ExeStr);
3681     strcatW( FullName, file->TargetPath );
3682     strcatW(FullName,close);
3683
3684     TRACE("Registering %s\n",debugstr_w(FullName));
3685     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3686                     &si, &info);
3687
3688     if (brc)
3689         msi_dialog_check_messages(info.hProcess);
3690
3691     msi_free(FullName);
3692
3693     /* the UI chunk */
3694     uirow = MSI_CreateRecord( 2 );
3695     uipath = strdupW( file->TargetPath );
3696     p = strrchrW(uipath,'\\');
3697     if (p)
3698         p[0]=0;
3699     MSI_RecordSetStringW( uirow, 1, &p[1] );
3700     MSI_RecordSetStringW( uirow, 2, uipath);
3701     ui_actiondata( package, szSelfRegModules, uirow);
3702     msiobj_release( &uirow->hdr );
3703     msi_free( uipath );
3704     /* FIXME: call ui_progress? */
3705
3706     return ERROR_SUCCESS;
3707 }
3708
3709 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3710 {
3711     UINT rc;
3712     MSIQUERY * view;
3713     static const WCHAR ExecSeqQuery[] = 
3714         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3715          '`','S','e','l','f','R','e','g','`',0};
3716
3717     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3718     if (rc != ERROR_SUCCESS)
3719     {
3720         TRACE("no SelfReg table\n");
3721         return ERROR_SUCCESS;
3722     }
3723
3724     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3725     msiobj_release(&view->hdr);
3726
3727     return ERROR_SUCCESS;
3728 }
3729
3730 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3731 {
3732     MSIFEATURE *feature;
3733     UINT rc;
3734     HKEY hkey=0;
3735     HKEY hukey=0;
3736     HKEY userdata=0;
3737
3738     if (!msi_check_publish(package))
3739         return ERROR_SUCCESS;
3740
3741     rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3742     if (rc != ERROR_SUCCESS)
3743         goto end;
3744
3745     rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3746     if (rc != ERROR_SUCCESS)
3747         goto end;
3748
3749     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3750     if (rc != ERROR_SUCCESS)
3751         goto end;
3752
3753     /* here the guids are base 85 encoded */
3754     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3755     {
3756         ComponentList *cl;
3757         LPWSTR data = NULL;
3758         GUID clsid;
3759         INT size;
3760         BOOL absent = FALSE;
3761         MSIRECORD *uirow;
3762
3763         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3764             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3765             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3766             absent = TRUE;
3767
3768         size = 1;
3769         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3770         {
3771             size += 21;
3772         }
3773         if (feature->Feature_Parent)
3774             size += strlenW( feature->Feature_Parent )+2;
3775
3776         data = msi_alloc(size * sizeof(WCHAR));
3777
3778         data[0] = 0;
3779         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3780         {
3781             MSICOMPONENT* component = cl->component;
3782             WCHAR buf[21];
3783
3784             buf[0] = 0;
3785             if (component->ComponentId)
3786             {
3787                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3788                 CLSIDFromString(component->ComponentId, &clsid);
3789                 encode_base85_guid(&clsid,buf);
3790                 TRACE("to %s\n",debugstr_w(buf));
3791                 strcatW(data,buf);
3792             }
3793         }
3794
3795         if (feature->Feature_Parent)
3796         {
3797             static const WCHAR sep[] = {'\2',0};
3798             strcatW(data,sep);
3799             strcatW(data,feature->Feature_Parent);
3800         }
3801
3802         msi_reg_set_val_str( hkey, feature->Feature, data );
3803         msi_reg_set_val_str( userdata, feature->Feature, data );
3804         msi_free(data);
3805
3806         size = 0;
3807         if (feature->Feature_Parent)
3808             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3809         if (!absent)
3810         {
3811             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3812                        (LPBYTE)feature->Feature_Parent,size);
3813         }
3814         else
3815         {
3816             size += 2*sizeof(WCHAR);
3817             data = msi_alloc(size);
3818             data[0] = 0x6;
3819             data[1] = 0;
3820             if (feature->Feature_Parent)
3821                 strcpyW( &data[1], feature->Feature_Parent );
3822             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3823                        (LPBYTE)data,size);
3824             msi_free(data);
3825         }
3826
3827         /* the UI chunk */
3828         uirow = MSI_CreateRecord( 1 );
3829         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3830         ui_actiondata( package, szPublishFeatures, uirow);
3831         msiobj_release( &uirow->hdr );
3832         /* FIXME: call ui_progress? */
3833     }
3834
3835 end:
3836     RegCloseKey(hkey);
3837     RegCloseKey(hukey);
3838     return rc;
3839 }
3840
3841 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3842 {
3843     UINT r;
3844     HKEY hkey;
3845
3846     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3847
3848     r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3849     if (r == ERROR_SUCCESS)
3850     {
3851         RegDeleteValueW(hkey, feature->Feature);
3852         RegCloseKey(hkey);
3853     }
3854
3855     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3856     if (r == ERROR_SUCCESS)
3857     {
3858         RegDeleteValueW(hkey, feature->Feature);
3859         RegCloseKey(hkey);
3860     }
3861
3862     return ERROR_SUCCESS;
3863 }
3864
3865 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3866 {
3867     MSIFEATURE *feature;
3868
3869     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3870     {
3871         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3872             return FALSE;
3873     }
3874
3875     return TRUE;
3876 }
3877
3878 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3879 {
3880     MSIFEATURE *feature;
3881
3882     if (!msi_check_unpublish(package))
3883         return ERROR_SUCCESS;
3884
3885     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3886     {
3887         msi_unpublish_feature(package, feature);
3888     }
3889
3890     return ERROR_SUCCESS;
3891 }
3892
3893 static UINT msi_get_local_package_name( LPWSTR path )
3894 {
3895     static const WCHAR szInstaller[] = {
3896         '\\','I','n','s','t','a','l','l','e','r','\\',0};
3897     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3898     DWORD time, len, i;
3899     HANDLE handle;
3900
3901     time = GetTickCount();
3902     GetWindowsDirectoryW( path, MAX_PATH );
3903     lstrcatW( path, szInstaller );
3904     CreateDirectoryW( path, NULL );
3905
3906     len = lstrlenW(path);
3907     for (i=0; i<0x10000; i++)
3908     {
3909         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3910         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3911                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3912         if (handle != INVALID_HANDLE_VALUE)
3913         {
3914             CloseHandle(handle);
3915             break;
3916         }
3917         if (GetLastError() != ERROR_FILE_EXISTS &&
3918             GetLastError() != ERROR_SHARING_VIOLATION)
3919             return ERROR_FUNCTION_FAILED;
3920     }
3921
3922     return ERROR_SUCCESS;
3923 }
3924
3925 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3926 {
3927     WCHAR packagefile[MAX_PATH];
3928     HKEY props;
3929     UINT r;
3930
3931     r = msi_get_local_package_name( packagefile );
3932     if (r != ERROR_SUCCESS)
3933         return r;
3934
3935     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3936
3937     r = CopyFileW( package->db->path, packagefile, FALSE);
3938
3939     if (!r)
3940     {
3941         ERR("Unable to copy package (%s -> %s) (error %d)\n",
3942             debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3943         return ERROR_FUNCTION_FAILED;
3944     }
3945
3946     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3947
3948     r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3949     if (r != ERROR_SUCCESS)
3950         return r;
3951
3952     msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3953     RegCloseKey(props);
3954     return ERROR_SUCCESS;
3955 }
3956
3957 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3958 {
3959     LPWSTR prop, val, key;
3960     static const LPCSTR propval[] = {
3961         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3962         "ARPCONTACT",             "Contact",
3963         "ARPCOMMENTS",            "Comments",
3964         "ProductName",            "DisplayName",
3965         "ProductVersion",         "DisplayVersion",
3966         "ARPHELPLINK",            "HelpLink",
3967         "ARPHELPTELEPHONE",       "HelpTelephone",
3968         "ARPINSTALLLOCATION",     "InstallLocation",
3969         "SourceDir",              "InstallSource",
3970         "Manufacturer",           "Publisher",
3971         "ARPREADME",              "Readme",
3972         "ARPSIZE",                "Size",
3973         "ARPURLINFOABOUT",        "URLInfoAbout",
3974         "ARPURLUPDATEINFO",       "URLUpdateInfo",
3975         NULL,
3976     };
3977     const LPCSTR *p = propval;
3978
3979     while( *p )
3980     {
3981         prop = strdupAtoW( *p++ );
3982         key = strdupAtoW( *p++ );
3983         val = msi_dup_property( package, prop );
3984         msi_reg_set_val_str( hkey, key, val );
3985         msi_free(val);
3986         msi_free(key);
3987         msi_free(prop);
3988     }
3989     return ERROR_SUCCESS;
3990 }
3991
3992 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3993 {
3994     HKEY hkey=0;
3995     HKEY hudkey=0, props=0;
3996     LPWSTR buffer = NULL;
3997     UINT rc;
3998     DWORD size, langid;
3999     static const WCHAR szWindowsInstaller[] = 
4000         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4001     static const WCHAR szUpgradeCode[] = 
4002         {'U','p','g','r','a','d','e','C','o','d','e',0};
4003     static const WCHAR modpath_fmt[] = 
4004         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4005          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4006     static const WCHAR szModifyPath[] = 
4007         {'M','o','d','i','f','y','P','a','t','h',0};
4008     static const WCHAR szUninstallString[] = 
4009         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4010     static const WCHAR szEstimatedSize[] = 
4011         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4012     static const WCHAR szProductLanguage[] =
4013         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4014     static const WCHAR szProductVersion[] =
4015         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4016
4017     SYSTEMTIME systime;
4018     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4019     LPWSTR upgrade_code;
4020     WCHAR szDate[9];
4021
4022     /* FIXME: also need to publish if the product is in advertise mode */
4023     if (!msi_check_publish(package))
4024         return ERROR_SUCCESS;
4025
4026     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4027     if (rc != ERROR_SUCCESS)
4028         return rc;
4029
4030     /* dump all the info i can grab */
4031     /* FIXME: Flesh out more information */
4032
4033     msi_write_uninstall_property_vals( package, hkey );
4034
4035     msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4036     
4037     msi_make_package_local( package, hkey );
4038
4039     /* do ModifyPath and UninstallString */
4040     size = deformat_string(package,modpath_fmt,&buffer);
4041     RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4042     RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4043     msi_free(buffer);
4044
4045     /* FIXME: Write real Estimated Size when we have it */
4046     msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4047    
4048     GetLocalTime(&systime);
4049     sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4050     msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4051    
4052     langid = msi_get_property_int( package, szProductLanguage, 0 );
4053     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4054
4055     buffer = msi_dup_property( package, szProductVersion );
4056     if (buffer)
4057     {
4058         DWORD verdword = msi_version_str_to_dword(buffer);
4059
4060         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4061         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4062         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4063     }
4064     msi_free(buffer);
4065     
4066     /* Handle Upgrade Codes */
4067     upgrade_code = msi_dup_property( package, szUpgradeCode );
4068     if (upgrade_code)
4069     {
4070         HKEY hkey2;
4071         WCHAR squashed[33];
4072         MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4073         squash_guid(package->ProductCode,squashed);
4074         msi_reg_set_val_str( hkey2, squashed, NULL );
4075         RegCloseKey(hkey2);
4076         MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4077         squash_guid(package->ProductCode,squashed);
4078         msi_reg_set_val_str( hkey2, squashed, NULL );
4079         RegCloseKey(hkey2);
4080
4081         msi_free(upgrade_code);
4082     }
4083     
4084     RegCloseKey(hkey);
4085
4086     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4087     if (rc != ERROR_SUCCESS)
4088         return rc;
4089
4090     RegCloseKey(hudkey);
4091
4092     rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
4093     if (rc != ERROR_SUCCESS)
4094         return rc;
4095
4096     msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4097     RegCloseKey(props);
4098
4099     return ERROR_SUCCESS;
4100 }
4101
4102 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4103 {
4104     return execute_script(package,INSTALL_SCRIPT);
4105 }
4106
4107 static UINT msi_unpublish_product(MSIPACKAGE *package)
4108 {
4109     LPWSTR remove = NULL;
4110     LPWSTR *features = NULL;
4111     BOOL full_uninstall = TRUE;
4112     MSIFEATURE *feature;
4113
4114     static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4115     static const WCHAR szAll[] = {'A','L','L',0};
4116
4117     remove = msi_dup_property(package, szRemove);
4118     if (!remove)
4119         return ERROR_SUCCESS;
4120
4121     features = msi_split_string(remove, ',');
4122     if (!features)
4123     {
4124         msi_free(remove);
4125         ERR("REMOVE feature list is empty!\n");
4126         return ERROR_FUNCTION_FAILED;
4127     }
4128
4129     if (!lstrcmpW(features[0], szAll))
4130         full_uninstall = TRUE;
4131     else
4132     {
4133         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4134         {
4135             if (feature->Action != INSTALLSTATE_ABSENT)
4136                 full_uninstall = FALSE;
4137         }
4138     }
4139
4140     if (!full_uninstall)
4141         goto done;
4142
4143     MSIREG_DeleteProductKey(package->ProductCode);
4144     MSIREG_DeleteUserProductKey(package->ProductCode);
4145     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4146     MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4147     MSIREG_DeleteUninstallKey(package->ProductCode);
4148
4149 done:
4150     msi_free(remove);
4151     msi_free(features);
4152     return ERROR_SUCCESS;
4153 }
4154
4155 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4156 {
4157     UINT rc;
4158
4159     rc = msi_unpublish_product(package);
4160     if (rc != ERROR_SUCCESS)
4161         return rc;
4162
4163     /* turn off scheduling */
4164     package->script->CurrentlyScripting= FALSE;
4165
4166     /* first do the same as an InstallExecute */
4167     rc = ACTION_InstallExecute(package);
4168     if (rc != ERROR_SUCCESS)
4169         return rc;
4170
4171     /* then handle Commit Actions */
4172     rc = execute_script(package,COMMIT_SCRIPT);
4173
4174     return rc;
4175 }
4176
4177 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4178 {
4179     static const WCHAR RunOnce[] = {
4180     'S','o','f','t','w','a','r','e','\\',
4181     'M','i','c','r','o','s','o','f','t','\\',
4182     'W','i','n','d','o','w','s','\\',
4183     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4184     'R','u','n','O','n','c','e',0};
4185     static const WCHAR InstallRunOnce[] = {
4186     'S','o','f','t','w','a','r','e','\\',
4187     'M','i','c','r','o','s','o','f','t','\\',
4188     'W','i','n','d','o','w','s','\\',
4189     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4190     'I','n','s','t','a','l','l','e','r','\\',
4191     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4192
4193     static const WCHAR msiexec_fmt[] = {
4194     '%','s',
4195     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4196     '\"','%','s','\"',0};
4197     static const WCHAR install_fmt[] = {
4198     '/','I',' ','\"','%','s','\"',' ',
4199     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4200     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4201     WCHAR buffer[256], sysdir[MAX_PATH];
4202     HKEY hkey;
4203     WCHAR squished_pc[100];
4204
4205     squash_guid(package->ProductCode,squished_pc);
4206
4207     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4208     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4209     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4210      squished_pc);
4211
4212     msi_reg_set_val_str( hkey, squished_pc, buffer );
4213     RegCloseKey(hkey);
4214
4215     TRACE("Reboot command %s\n",debugstr_w(buffer));
4216
4217     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4218     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4219
4220     msi_reg_set_val_str( hkey, squished_pc, buffer );
4221     RegCloseKey(hkey);
4222
4223     return ERROR_INSTALL_SUSPEND;
4224 }
4225
4226 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4227 {
4228     DWORD attrib;
4229     UINT rc;
4230
4231     /*
4232      * We are currently doing what should be done here in the top level Install
4233      * however for Administrative and uninstalls this step will be needed
4234      */
4235     if (!package->PackagePath)
4236         return ERROR_SUCCESS;
4237
4238     msi_set_sourcedir_props(package, TRUE);
4239
4240     attrib = GetFileAttributesW(package->db->path);
4241     if (attrib == INVALID_FILE_ATTRIBUTES)
4242     {
4243         LPWSTR prompt;
4244         LPWSTR msg;
4245         DWORD size = 0;
4246
4247         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4248                 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
4249                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4250         if (rc == ERROR_MORE_DATA)
4251         {
4252             prompt = msi_alloc(size * sizeof(WCHAR));
4253             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4254                     MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
4255                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4256         }
4257         else
4258             prompt = strdupW(package->db->path);
4259
4260         msg = generate_error_string(package,1302,1,prompt);
4261         while(attrib == INVALID_FILE_ATTRIBUTES)
4262         {
4263             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4264             if (rc == IDCANCEL)
4265             {
4266                 rc = ERROR_INSTALL_USEREXIT;
4267                 break;
4268             }
4269             attrib = GetFileAttributesW(package->db->path);
4270         }
4271         msi_free(prompt);
4272         rc = ERROR_SUCCESS;
4273     }
4274     else
4275         return ERROR_SUCCESS;
4276
4277     return rc;
4278 }
4279
4280 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4281 {
4282     HKEY hkey=0;
4283     LPWSTR buffer;
4284     LPWSTR productid;
4285     UINT rc,i;
4286
4287     static const WCHAR szPropKeys[][80] = 
4288     {
4289         {'P','r','o','d','u','c','t','I','D',0},
4290         {'U','S','E','R','N','A','M','E',0},
4291         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4292         {0},
4293     };
4294
4295     static const WCHAR szRegKeys[][80] = 
4296     {
4297         {'P','r','o','d','u','c','t','I','D',0},
4298         {'R','e','g','O','w','n','e','r',0},
4299         {'R','e','g','C','o','m','p','a','n','y',0},
4300         {0},
4301     };
4302
4303     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4304     if (!productid)
4305         return ERROR_SUCCESS;
4306
4307     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4308     if (rc != ERROR_SUCCESS)
4309         goto end;
4310
4311     for( i = 0; szPropKeys[i][0]; i++ )
4312     {
4313         buffer = msi_dup_property( package, szPropKeys[i] );
4314         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4315         msi_free( buffer );
4316     }
4317
4318 end:
4319     msi_free(productid);
4320     RegCloseKey(hkey);
4321
4322     /* FIXME: call ui_actiondata */
4323
4324     return ERROR_SUCCESS;
4325 }
4326
4327
4328 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4329 {
4330     UINT rc;
4331
4332     package->script->InWhatSequence |= SEQUENCE_EXEC;
4333     rc = ACTION_ProcessExecSequence(package,FALSE);
4334     return rc;
4335 }
4336
4337
4338 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4339 {
4340     MSIPACKAGE *package = (MSIPACKAGE*)param;
4341     LPCWSTR compgroupid=NULL;
4342     LPCWSTR feature=NULL;
4343     LPCWSTR text = NULL;
4344     LPCWSTR qualifier = NULL;
4345     LPCWSTR component = NULL;
4346     LPWSTR advertise = NULL;
4347     LPWSTR output = NULL;
4348     HKEY hkey;
4349     UINT rc = ERROR_SUCCESS;
4350     MSICOMPONENT *comp;
4351     DWORD sz = 0;
4352     MSIRECORD *uirow;
4353
4354     component = MSI_RecordGetString(rec,3);
4355     comp = get_loaded_component(package,component);
4356
4357     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4358        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4359        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4360     {
4361         TRACE("Skipping: Component %s not scheduled for install\n",
4362                         debugstr_w(component));
4363
4364         return ERROR_SUCCESS;
4365     }
4366
4367     compgroupid = MSI_RecordGetString(rec,1);
4368     qualifier = MSI_RecordGetString(rec,2);
4369
4370     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4371     if (rc != ERROR_SUCCESS)
4372         goto end;
4373     
4374     text = MSI_RecordGetString(rec,4);
4375     feature = MSI_RecordGetString(rec,5);
4376   
4377     advertise = create_component_advertise_string(package, comp, feature);
4378
4379     sz = strlenW(advertise);
4380
4381     if (text)
4382         sz += lstrlenW(text);
4383
4384     sz+=3;
4385     sz *= sizeof(WCHAR);
4386            
4387     output = msi_alloc_zero(sz);
4388     strcpyW(output,advertise);
4389     msi_free(advertise);
4390
4391     if (text)
4392         strcatW(output,text);
4393
4394     msi_reg_set_val_multi_str( hkey, qualifier, output );
4395     
4396 end:
4397     RegCloseKey(hkey);
4398     msi_free(output);
4399
4400     /* the UI chunk */
4401     uirow = MSI_CreateRecord( 2 );
4402     MSI_RecordSetStringW( uirow, 1, compgroupid );
4403     MSI_RecordSetStringW( uirow, 2, qualifier);
4404     ui_actiondata( package, szPublishComponents, uirow);
4405     msiobj_release( &uirow->hdr );
4406     /* FIXME: call ui_progress? */
4407
4408     return rc;
4409 }
4410
4411 /*
4412  * At present I am ignorning the advertised components part of this and only
4413  * focusing on the qualified component sets
4414  */
4415 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4416 {
4417     UINT rc;
4418     MSIQUERY * view;
4419     static const WCHAR ExecSeqQuery[] =
4420         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4421          '`','P','u','b','l','i','s','h',
4422          'C','o','m','p','o','n','e','n','t','`',0};
4423     
4424     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4425     if (rc != ERROR_SUCCESS)
4426         return ERROR_SUCCESS;
4427
4428     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4429     msiobj_release(&view->hdr);
4430
4431     return rc;
4432 }
4433
4434 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4435 {
4436     MSIPACKAGE *package = (MSIPACKAGE*)param;
4437     MSIRECORD *row;
4438     MSIFILE *file;
4439     SC_HANDLE hscm, service = NULL;
4440     LPCWSTR name, disp, comp, depends, pass;
4441     LPCWSTR load_order, serv_name, key;
4442     DWORD serv_type, start_type;
4443     DWORD err_control;
4444
4445     static const WCHAR query[] =
4446         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4447          '`','C','o','m','p','o','n','e','n','t','`',' ',
4448          'W','H','E','R','E',' ',
4449          '`','C','o','m','p','o','n','e','n','t','`',' ',
4450          '=','\'','%','s','\'',0};
4451
4452     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4453     if (!hscm)
4454     {
4455         ERR("Failed to open the SC Manager!\n");
4456         goto done;
4457     }
4458
4459     start_type = MSI_RecordGetInteger(rec, 5);
4460     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4461         goto done;
4462
4463     depends = MSI_RecordGetString(rec, 8);
4464     if (depends && *depends)
4465         FIXME("Dependency list unhandled!\n");
4466
4467     name = MSI_RecordGetString(rec, 2);
4468     disp = MSI_RecordGetString(rec, 3);
4469     serv_type = MSI_RecordGetInteger(rec, 4);
4470     err_control = MSI_RecordGetInteger(rec, 6);
4471     load_order = MSI_RecordGetString(rec, 7);
4472     serv_name = MSI_RecordGetString(rec, 9);
4473     pass = MSI_RecordGetString(rec, 10);
4474     comp = MSI_RecordGetString(rec, 12);
4475
4476     /* fetch the service path */
4477     row = MSI_QueryGetRecord(package->db, query, comp);
4478     if (!row)
4479     {
4480         ERR("Control query failed!\n");
4481         goto done;
4482     }
4483
4484     key = MSI_RecordGetString(row, 6);
4485
4486     file = get_loaded_file(package, key);
4487     msiobj_release(&row->hdr);
4488     if (!file)
4489     {
4490         ERR("Failed to load the service file\n");
4491         goto done;
4492     }
4493
4494     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4495                              start_type, err_control, file->TargetPath,
4496                              load_order, NULL, NULL, serv_name, pass);
4497     if (!service)
4498     {
4499         if (GetLastError() != ERROR_SERVICE_EXISTS)
4500             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4501     }
4502
4503 done:
4504     CloseServiceHandle(service);
4505     CloseServiceHandle(hscm);
4506
4507     return ERROR_SUCCESS;
4508 }
4509
4510 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4511 {
4512     UINT rc;
4513     MSIQUERY * view;
4514     static const WCHAR ExecSeqQuery[] =
4515         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4516          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4517     
4518     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4519     if (rc != ERROR_SUCCESS)
4520         return ERROR_SUCCESS;
4521
4522     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4523     msiobj_release(&view->hdr);
4524
4525     return rc;
4526 }
4527
4528 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4529 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4530 {
4531     LPCWSTR *vector, *temp_vector;
4532     LPWSTR p, q;
4533     DWORD sep_len;
4534
4535     static const WCHAR separator[] = {'[','~',']',0};
4536
4537     *numargs = 0;
4538     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4539
4540     if (!args)
4541         return NULL;
4542
4543     vector = msi_alloc(sizeof(LPWSTR));
4544     if (!vector)
4545         return NULL;
4546
4547     p = args;
4548     do
4549     {
4550         (*numargs)++;
4551         vector[*numargs - 1] = p;
4552
4553         if ((q = strstrW(p, separator)))
4554         {
4555             *q = '\0';
4556
4557             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4558             if (!temp_vector)
4559             {
4560                 msi_free(vector);
4561                 return NULL;
4562             }
4563             vector = temp_vector;
4564
4565             p = q + sep_len;
4566         }
4567     } while (q);
4568
4569     return vector;
4570 }
4571
4572 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4573 {
4574     MSIPACKAGE *package = (MSIPACKAGE *)param;
4575     MSICOMPONENT *comp;
4576     SC_HANDLE scm, service = NULL;
4577     LPCWSTR name, *vector = NULL;
4578     LPWSTR args;
4579     DWORD event, numargs;
4580     UINT r = ERROR_FUNCTION_FAILED;
4581
4582     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4583     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4584         return ERROR_SUCCESS;
4585
4586     name = MSI_RecordGetString(rec, 2);
4587     event = MSI_RecordGetInteger(rec, 3);
4588     args = strdupW(MSI_RecordGetString(rec, 4));
4589
4590     if (!(event & msidbServiceControlEventStart))
4591         return ERROR_SUCCESS;
4592
4593     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4594     if (!scm)
4595     {
4596         ERR("Failed to open the service control manager\n");
4597         goto done;
4598     }
4599
4600     service = OpenServiceW(scm, name, SERVICE_START);
4601     if (!service)
4602     {
4603         ERR("Failed to open service %s\n", debugstr_w(name));
4604         goto done;
4605     }
4606
4607     vector = msi_service_args_to_vector(args, &numargs);
4608
4609     if (!StartServiceW(service, numargs, vector))
4610     {
4611         ERR("Failed to start service %s\n", debugstr_w(name));
4612         goto done;
4613     }
4614
4615     r = ERROR_SUCCESS;
4616
4617 done:
4618     CloseServiceHandle(service);
4619     CloseServiceHandle(scm);
4620
4621     msi_free(args);
4622     msi_free(vector);
4623     return r;
4624 }
4625
4626 static UINT ACTION_StartServices( MSIPACKAGE *package )
4627 {
4628     UINT rc;
4629     MSIQUERY *view;
4630
4631     static const WCHAR query[] = {
4632         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4633         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4634
4635     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4636     if (rc != ERROR_SUCCESS)
4637         return ERROR_SUCCESS;
4638
4639     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4640     msiobj_release(&view->hdr);
4641
4642     return rc;
4643 }
4644
4645 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4646 {
4647     MSIFILE *file;
4648
4649     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4650     {
4651         if (!lstrcmpW(file->File, filename))
4652             return file;
4653     }
4654
4655     return NULL;
4656 }
4657
4658 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4659 {
4660     MSIPACKAGE *package = (MSIPACKAGE*)param;
4661     LPWSTR driver, driver_path, ptr;
4662     WCHAR outpath[MAX_PATH];
4663     MSIFILE *driver_file, *setup_file;
4664     LPCWSTR desc;
4665     DWORD len, usage;
4666     UINT r = ERROR_SUCCESS;
4667
4668     static const WCHAR driver_fmt[] = {
4669         'D','r','i','v','e','r','=','%','s',0};
4670     static const WCHAR setup_fmt[] = {
4671         'S','e','t','u','p','=','%','s',0};
4672     static const WCHAR usage_fmt[] = {
4673         'F','i','l','e','U','s','a','g','e','=','1',0};
4674
4675     desc = MSI_RecordGetString(rec, 3);
4676
4677     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4678     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4679
4680     if (!driver_file || !setup_file)
4681     {
4682         ERR("ODBC Driver entry not found!\n");
4683         return ERROR_FUNCTION_FAILED;
4684     }
4685
4686     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4687           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4688           lstrlenW(usage_fmt) + 1;
4689     driver = msi_alloc(len * sizeof(WCHAR));
4690     if (!driver)
4691         return ERROR_OUTOFMEMORY;
4692
4693     ptr = driver;
4694     lstrcpyW(ptr, desc);
4695     ptr += lstrlenW(ptr) + 1;
4696
4697     sprintfW(ptr, driver_fmt, driver_file->FileName);
4698     ptr += lstrlenW(ptr) + 1;
4699
4700     sprintfW(ptr, setup_fmt, setup_file->FileName);
4701     ptr += lstrlenW(ptr) + 1;
4702
4703     lstrcpyW(ptr, usage_fmt);
4704     ptr += lstrlenW(ptr) + 1;
4705     *ptr = '\0';
4706
4707     driver_path = strdupW(driver_file->TargetPath);
4708     ptr = strrchrW(driver_path, '\\');
4709     if (ptr) *ptr = '\0';
4710
4711     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4712                              NULL, ODBC_INSTALL_COMPLETE, &usage))
4713     {
4714         ERR("Failed to install SQL driver!\n");
4715         r = ERROR_FUNCTION_FAILED;
4716     }
4717
4718     msi_free(driver);
4719     msi_free(driver_path);
4720
4721     return r;
4722 }
4723
4724 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4725 {
4726     MSIPACKAGE *package = (MSIPACKAGE*)param;
4727     LPWSTR translator, translator_path, ptr;
4728     WCHAR outpath[MAX_PATH];
4729     MSIFILE *translator_file, *setup_file;
4730     LPCWSTR desc;
4731     DWORD len, usage;
4732     UINT r = ERROR_SUCCESS;
4733
4734     static const WCHAR translator_fmt[] = {
4735         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4736     static const WCHAR setup_fmt[] = {
4737         'S','e','t','u','p','=','%','s',0};
4738
4739     desc = MSI_RecordGetString(rec, 3);
4740
4741     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4742     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4743
4744     if (!translator_file || !setup_file)
4745     {
4746         ERR("ODBC Translator entry not found!\n");
4747         return ERROR_FUNCTION_FAILED;
4748     }
4749
4750     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4751           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4752     translator = msi_alloc(len * sizeof(WCHAR));
4753     if (!translator)
4754         return ERROR_OUTOFMEMORY;
4755
4756     ptr = translator;
4757     lstrcpyW(ptr, desc);
4758     ptr += lstrlenW(ptr) + 1;
4759
4760     sprintfW(ptr, translator_fmt, translator_file->FileName);
4761     ptr += lstrlenW(ptr) + 1;
4762
4763     sprintfW(ptr, setup_fmt, setup_file->FileName);
4764     ptr += lstrlenW(ptr) + 1;
4765     *ptr = '\0';
4766
4767     translator_path = strdupW(translator_file->TargetPath);
4768     ptr = strrchrW(translator_path, '\\');
4769     if (ptr) *ptr = '\0';
4770
4771     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4772                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
4773     {
4774         ERR("Failed to install SQL translator!\n");
4775         r = ERROR_FUNCTION_FAILED;
4776     }
4777
4778     msi_free(translator);
4779     msi_free(translator_path);
4780
4781     return r;
4782 }
4783
4784 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4785 {
4786     LPWSTR attrs;
4787     LPCWSTR desc, driver;
4788     WORD request = ODBC_ADD_SYS_DSN;
4789     INT registration;
4790     DWORD len;
4791     UINT r = ERROR_SUCCESS;
4792
4793     static const WCHAR attrs_fmt[] = {
4794         'D','S','N','=','%','s',0 };
4795
4796     desc = MSI_RecordGetString(rec, 3);
4797     driver = MSI_RecordGetString(rec, 4);
4798     registration = MSI_RecordGetInteger(rec, 5);
4799
4800     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4801     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4802
4803     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4804     attrs = msi_alloc(len * sizeof(WCHAR));
4805     if (!attrs)
4806         return ERROR_OUTOFMEMORY;
4807
4808     sprintfW(attrs, attrs_fmt, desc);
4809     attrs[len - 1] = '\0';
4810
4811     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4812     {
4813         ERR("Failed to install SQL data source!\n");
4814         r = ERROR_FUNCTION_FAILED;
4815     }
4816
4817     msi_free(attrs);
4818
4819     return r;
4820 }
4821
4822 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4823 {
4824     UINT rc;
4825     MSIQUERY *view;
4826
4827     static const WCHAR driver_query[] = {
4828         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4829         'O','D','B','C','D','r','i','v','e','r',0 };
4830
4831     static const WCHAR translator_query[] = {
4832         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4833         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4834
4835     static const WCHAR source_query[] = {
4836         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4837         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4838
4839     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4840     if (rc != ERROR_SUCCESS)
4841         return ERROR_SUCCESS;
4842
4843     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4844     msiobj_release(&view->hdr);
4845
4846     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4847     if (rc != ERROR_SUCCESS)
4848         return ERROR_SUCCESS;
4849
4850     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4851     msiobj_release(&view->hdr);
4852
4853     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4854     if (rc != ERROR_SUCCESS)
4855         return ERROR_SUCCESS;
4856
4857     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4858     msiobj_release(&view->hdr);
4859
4860     return rc;
4861 }
4862
4863 #define ENV_ACT_SETALWAYS   0x1
4864 #define ENV_ACT_SETABSENT   0x2
4865 #define ENV_ACT_REMOVE      0x4
4866 #define ENV_ACT_REMOVEMATCH 0x8
4867
4868 #define ENV_MOD_MACHINE     0x20000000
4869 #define ENV_MOD_APPEND      0x40000000
4870 #define ENV_MOD_PREFIX      0x80000000
4871 #define ENV_MOD_MASK        0xC0000000
4872
4873 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4874
4875 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4876 {
4877     LPCWSTR cptr = *name;
4878     LPCWSTR ptr = *value;
4879
4880     static const WCHAR prefix[] = {'[','~',']',0};
4881     static const int prefix_len = 3;
4882
4883     *flags = 0;
4884     while (*cptr)
4885     {
4886         if (*cptr == '=')
4887             *flags |= ENV_ACT_SETALWAYS;
4888         else if (*cptr == '+')
4889             *flags |= ENV_ACT_SETABSENT;
4890         else if (*cptr == '-')
4891             *flags |= ENV_ACT_REMOVE;
4892         else if (*cptr == '!')
4893             *flags |= ENV_ACT_REMOVEMATCH;
4894         else if (*cptr == '*')
4895             *flags |= ENV_MOD_MACHINE;
4896         else
4897             break;
4898
4899         cptr++;
4900         (*name)++;
4901     }
4902
4903     if (!*cptr)
4904     {
4905         ERR("Missing environment variable\n");
4906         return ERROR_FUNCTION_FAILED;
4907     }
4908
4909     if (!strncmpW(ptr, prefix, prefix_len))
4910     {
4911         *flags |= ENV_MOD_APPEND;
4912         *value += lstrlenW(prefix);
4913     }
4914     else if (lstrlenW(*value) >= prefix_len)
4915     {
4916         ptr += lstrlenW(ptr) - prefix_len;
4917         if (!lstrcmpW(ptr, prefix))
4918         {
4919             *flags |= ENV_MOD_PREFIX;
4920             /* the "[~]" will be removed by deformat_string */;
4921         }
4922     }
4923
4924     if (!*flags ||
4925         check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4926         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4927         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4928         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4929     {
4930         ERR("Invalid flags: %08x\n", *flags);
4931         return ERROR_FUNCTION_FAILED;
4932     }
4933
4934     return ERROR_SUCCESS;
4935 }
4936
4937 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4938 {
4939     MSIPACKAGE *package = param;
4940     LPCWSTR name, value, comp;
4941     LPWSTR data = NULL, newval = NULL;
4942     LPWSTR deformatted = NULL, ptr;
4943     DWORD flags, type, size;
4944     LONG res;
4945     HKEY env = NULL, root;
4946     LPCWSTR environment;
4947
4948     static const WCHAR user_env[] =
4949         {'E','n','v','i','r','o','n','m','e','n','t',0};
4950     static const WCHAR machine_env[] =
4951         {'S','y','s','t','e','m','\\',
4952          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4953          'C','o','n','t','r','o','l','\\',
4954          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4955          'E','n','v','i','r','o','n','m','e','n','t',0};
4956     static const WCHAR semicolon[] = {';',0};
4957
4958     name = MSI_RecordGetString(rec, 2);
4959     value = MSI_RecordGetString(rec, 3);
4960     comp = MSI_RecordGetString(rec, 4);
4961
4962     res = env_set_flags(&name, &value, &flags);
4963     if (res != ERROR_SUCCESS)
4964        goto done;
4965
4966     deformat_string(package, value, &deformatted);
4967     if (!deformatted)
4968     {
4969         res = ERROR_OUTOFMEMORY;
4970         goto done;
4971     }
4972
4973     value = deformatted;
4974
4975     if (flags & ENV_MOD_MACHINE)
4976     {
4977         environment = machine_env;
4978         root = HKEY_LOCAL_MACHINE;
4979     }
4980     else
4981     {
4982         environment = user_env;
4983         root = HKEY_CURRENT_USER;
4984     }
4985
4986     res = RegCreateKeyExW(root, environment, 0, NULL, 0,
4987                           KEY_ALL_ACCESS, NULL, &env, NULL);
4988     if (res != ERROR_SUCCESS)
4989         goto done;
4990
4991     if (flags & ENV_ACT_REMOVE)
4992         FIXME("Not removing environment variable on uninstall!\n");
4993
4994     size = 0;
4995     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4996     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4997         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
4998         goto done;
4999
5000     if (res != ERROR_FILE_NOT_FOUND)
5001     {
5002         if (flags & ENV_ACT_SETABSENT)
5003         {
5004             res = ERROR_SUCCESS;
5005             goto done;
5006         }
5007
5008         data = msi_alloc(size);
5009         if (!data)
5010         {
5011             RegCloseKey(env);
5012             return ERROR_OUTOFMEMORY;
5013         }
5014
5015         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5016         if (res != ERROR_SUCCESS)
5017             goto done;
5018
5019         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5020         {
5021             res = RegDeleteKeyW(env, name);
5022             goto done;
5023         }
5024
5025         size =  (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5026         newval =  msi_alloc(size);
5027         ptr = newval;
5028         if (!newval)
5029         {
5030             res = ERROR_OUTOFMEMORY;
5031             goto done;
5032         }
5033
5034         if (!(flags & ENV_MOD_MASK))
5035             lstrcpyW(newval, value);
5036         else
5037         {
5038             if (flags & ENV_MOD_PREFIX)
5039             {
5040                 lstrcpyW(newval, value);
5041                 lstrcatW(newval, semicolon);
5042                 ptr = newval + lstrlenW(value) + 1;
5043             }
5044
5045             lstrcpyW(ptr, data);
5046
5047             if (flags & ENV_MOD_APPEND)
5048             {
5049                 lstrcatW(newval, semicolon);
5050                 lstrcatW(newval, value);
5051             }
5052         }
5053     }
5054     else
5055     {
5056         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5057         newval = msi_alloc(size);
5058         if (!newval)
5059         {
5060             res = ERROR_OUTOFMEMORY;
5061             goto done;
5062         }
5063
5064         lstrcpyW(newval, value);
5065     }
5066
5067     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5068     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5069
5070 done:
5071     if (env) RegCloseKey(env);
5072     msi_free(deformatted);
5073     msi_free(data);
5074     msi_free(newval);
5075     return res;
5076 }
5077
5078 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5079 {
5080     UINT rc;
5081     MSIQUERY * view;
5082     static const WCHAR ExecSeqQuery[] =
5083         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5084          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5085     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5086     if (rc != ERROR_SUCCESS)
5087         return ERROR_SUCCESS;
5088
5089     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5090     msiobj_release(&view->hdr);
5091
5092     return rc;
5093 }
5094
5095 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5096
5097 typedef struct
5098 {
5099     struct list entry;
5100     LPWSTR sourcename;
5101     LPWSTR destname;
5102     LPWSTR source;
5103     LPWSTR dest;
5104 } FILE_LIST;
5105
5106 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5107 {
5108     BOOL ret;
5109
5110     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5111         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5112     {
5113         WARN("Source or dest is directory, not moving\n");
5114         return FALSE;
5115     }
5116
5117     if (options == msidbMoveFileOptionsMove)
5118     {
5119         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5120         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5121         if (!ret)
5122         {
5123             WARN("MoveFile failed: %d\n", GetLastError());
5124             return FALSE;
5125         }
5126     }
5127     else
5128     {
5129         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5130         ret = CopyFileW(source, dest, FALSE);
5131         if (!ret)
5132         {
5133             WARN("CopyFile failed: %d\n", GetLastError());
5134             return FALSE;
5135         }
5136     }
5137
5138     return TRUE;
5139 }
5140
5141 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5142 {
5143     LPWSTR path, ptr;
5144     DWORD dirlen, pathlen;
5145
5146     ptr = strrchrW(wildcard, '\\');
5147     dirlen = ptr - wildcard + 1;
5148
5149     pathlen = dirlen + lstrlenW(filename) + 1;
5150     path = msi_alloc(pathlen * sizeof(WCHAR));
5151
5152     lstrcpynW(path, wildcard, dirlen + 1);
5153     lstrcatW(path, filename);
5154
5155     return path;
5156 }
5157
5158 static void free_file_entry(FILE_LIST *file)
5159 {
5160     msi_free(file->source);
5161     msi_free(file->dest);
5162     msi_free(file);
5163 }
5164
5165 static void free_list(FILE_LIST *list)
5166 {
5167     while (!list_empty(&list->entry))
5168     {
5169         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5170
5171         list_remove(&file->entry);
5172         free_file_entry(file);
5173     }
5174 }
5175
5176 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5177 {
5178     FILE_LIST *new, *file;
5179     LPWSTR ptr, filename;
5180     DWORD size;
5181
5182     new = msi_alloc_zero(sizeof(FILE_LIST));
5183     if (!new)
5184         return FALSE;
5185
5186     new->source = strdupW(source);
5187     ptr = strrchrW(dest, '\\') + 1;
5188     filename = strrchrW(new->source, '\\') + 1;
5189
5190     new->sourcename = filename;
5191
5192     if (*ptr)
5193         new->destname = ptr;
5194     else
5195         new->destname = new->sourcename;
5196
5197     size = (ptr - dest) + lstrlenW(filename) + 1;
5198     new->dest = msi_alloc(size * sizeof(WCHAR));
5199     if (!new->dest)
5200     {
5201         free_file_entry(new);
5202         return FALSE;
5203     }
5204
5205     lstrcpynW(new->dest, dest, ptr - dest + 1);
5206     lstrcatW(new->dest, filename);
5207
5208     if (list_empty(&files->entry))
5209     {
5210         list_add_head(&files->entry, &new->entry);
5211         return TRUE;
5212     }
5213
5214     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5215     {
5216         if (lstrcmpW(source, file->source) < 0)
5217         {
5218             list_add_before(&file->entry, &new->entry);
5219             return TRUE;
5220         }
5221     }
5222
5223     list_add_after(&file->entry, &new->entry);
5224     return TRUE;
5225 }
5226
5227 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5228 {
5229     WIN32_FIND_DATAW wfd;
5230     HANDLE hfile;
5231     LPWSTR path;
5232     BOOL res;
5233     FILE_LIST files, *file;
5234     DWORD size;
5235
5236     hfile = FindFirstFileW(source, &wfd);
5237     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5238
5239     list_init(&files.entry);
5240
5241     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5242     {
5243         if (is_dot_dir(wfd.cFileName)) continue;
5244
5245         path = wildcard_to_file(source, wfd.cFileName);
5246         if (!path)
5247         {
5248             res = FALSE;
5249             goto done;
5250         }
5251
5252         add_wildcard(&files, path, dest);
5253         msi_free(path);
5254     }
5255
5256     /* only the first wildcard match gets renamed to dest */
5257     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5258     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5259     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5260     if (!file->dest)
5261     {
5262         res = FALSE;
5263         goto done;
5264     }
5265
5266     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5267
5268     while (!list_empty(&files.entry))
5269     {
5270         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5271
5272         msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5273
5274         list_remove(&file->entry);
5275         free_file_entry(file);
5276     }
5277
5278     res = TRUE;
5279
5280 done:
5281     free_list(&files);
5282     FindClose(hfile);
5283     return res;
5284 }
5285
5286 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5287 {
5288     MSIPACKAGE *package = param;
5289     MSICOMPONENT *comp;
5290     LPCWSTR sourcename, destname;
5291     LPWSTR sourcedir = NULL, destdir = NULL;
5292     LPWSTR source = NULL, dest = NULL;
5293     int options;
5294     DWORD size;
5295     BOOL ret, wildcards;
5296
5297     static const WCHAR backslash[] = {'\\',0};
5298
5299     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5300     if (!comp || !comp->Enabled ||
5301         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5302     {
5303         TRACE("Component not set for install, not moving file\n");
5304         return ERROR_SUCCESS;
5305     }
5306
5307     sourcename = MSI_RecordGetString(rec, 3);
5308     destname = MSI_RecordGetString(rec, 4);
5309     options = MSI_RecordGetInteger(rec, 7);
5310
5311     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5312     if (!sourcedir)
5313         goto done;
5314
5315     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5316     if (!destdir)
5317         goto done;
5318
5319     if (!sourcename)
5320     {
5321         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5322             goto done;
5323
5324         source = strdupW(sourcedir);
5325         if (!source)
5326             goto done;
5327     }
5328     else
5329     {
5330         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5331         source = msi_alloc(size * sizeof(WCHAR));
5332         if (!source)
5333             goto done;
5334
5335         lstrcpyW(source, sourcedir);
5336         if (source[lstrlenW(source) - 1] != '\\')
5337             lstrcatW(source, backslash);
5338         lstrcatW(source, sourcename);
5339     }
5340
5341     wildcards = strchrW(source, '*') || strchrW(source, '?');
5342
5343     if (!destname && !wildcards)
5344     {
5345         destname = strdupW(sourcename);
5346         if (!destname)
5347             goto done;
5348     }
5349
5350     size = 0;
5351     if (destname)
5352         size = lstrlenW(destname);
5353
5354     size += lstrlenW(destdir) + 2;
5355     dest = msi_alloc(size * sizeof(WCHAR));
5356     if (!dest)
5357         goto done;
5358
5359     lstrcpyW(dest, destdir);
5360     if (dest[lstrlenW(dest) - 1] != '\\')
5361         lstrcatW(dest, backslash);
5362
5363     if (destname)
5364         lstrcatW(dest, destname);
5365
5366     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5367     {
5368         ret = CreateDirectoryW(destdir, NULL);
5369         if (!ret)
5370         {
5371             WARN("CreateDirectory failed: %d\n", GetLastError());
5372             return ERROR_SUCCESS;
5373         }
5374     }
5375
5376     if (!wildcards)
5377         msi_move_file(source, dest, options);
5378     else
5379         move_files_wildcard(source, dest, options);
5380
5381 done:
5382     msi_free(sourcedir);
5383     msi_free(destdir);
5384     msi_free(source);
5385     msi_free(dest);
5386
5387     return ERROR_SUCCESS;
5388 }
5389
5390 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5391 {
5392     UINT rc;
5393     MSIQUERY *view;
5394
5395     static const WCHAR ExecSeqQuery[] =
5396         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5397          '`','M','o','v','e','F','i','l','e','`',0};
5398
5399     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5400     if (rc != ERROR_SUCCESS)
5401         return ERROR_SUCCESS;
5402
5403     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5404     msiobj_release(&view->hdr);
5405
5406     return rc;
5407 }
5408
5409 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5410                                            LPCSTR action, LPCWSTR table )
5411 {
5412     static const WCHAR query[] = {
5413         'S','E','L','E','C','T',' ','*',' ',
5414         'F','R','O','M',' ','`','%','s','`',0 };
5415     MSIQUERY *view = NULL;
5416     DWORD count = 0;
5417     UINT r;
5418     
5419     r = MSI_OpenQuery( package->db, &view, query, table );
5420     if (r == ERROR_SUCCESS)
5421     {
5422         r = MSI_IterateRecords(view, &count, NULL, package);
5423         msiobj_release(&view->hdr);
5424     }
5425
5426     if (count)
5427         FIXME("%s -> %u ignored %s table values\n",
5428               action, count, debugstr_w(table));
5429
5430     return ERROR_SUCCESS;
5431 }
5432
5433 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5434 {
5435     TRACE("%p\n", package);
5436     return ERROR_SUCCESS;
5437 }
5438
5439 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5440 {
5441     static const WCHAR table[] =
5442          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5443     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5444 }
5445
5446 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5447 {
5448     static const WCHAR table[] = { 'P','a','t','c','h',0 };
5449     return msi_unimplemented_action_stub( package, "PatchFiles", table );
5450 }
5451
5452 static UINT ACTION_BindImage( MSIPACKAGE *package )
5453 {
5454     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5455     return msi_unimplemented_action_stub( package, "BindImage", table );
5456 }
5457
5458 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5459 {
5460     static const WCHAR table[] = {
5461         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5462     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5463 }
5464
5465 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5466 {
5467     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5468     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5469 }
5470
5471 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5472 {
5473     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5474     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5475 }
5476
5477 static UINT ACTION_StopServices( MSIPACKAGE *package )
5478 {
5479     static const WCHAR table[] = {
5480         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5481     return msi_unimplemented_action_stub( package, "StopServices", table );
5482 }
5483
5484 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5485 {
5486     static const WCHAR table[] = {
5487         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5488     return msi_unimplemented_action_stub( package, "DeleteServices", table );
5489 }
5490 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5491 {
5492         static const WCHAR table[] = {
5493                 'P','r','o','d','u','c','t','I','D',0 };
5494         return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5495 }
5496
5497 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5498 {
5499     static const WCHAR table[] = {
5500         'E','n','v','i','r','o','n','m','e','n','t',0 };
5501     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5502 }
5503
5504 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5505 {
5506     static const WCHAR table[] = {
5507         'M','s','i','A','s','s','e','m','b','l','y',0 };
5508     return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5509 }
5510
5511 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5512 {
5513     static const WCHAR table[] = {
5514         'M','s','i','A','s','s','e','m','b','l','y',0 };
5515     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5516 }
5517
5518 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5519 {
5520     static const WCHAR table[] = { 'F','o','n','t',0 };
5521     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5522 }
5523
5524 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5525 {
5526     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5527     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5528 }
5529
5530 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5531 {
5532     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5533     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5534 }
5535
5536 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5537 {
5538     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5539     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5540 }
5541
5542 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5543 {
5544     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5545     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5546 }
5547
5548 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5549 {
5550     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5551     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5552 }
5553
5554 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5555 {
5556     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5557     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5558 }
5559
5560 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5561 {
5562     static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5563     return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5564 }
5565
5566 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5567 {
5568     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5569     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5570 }
5571
5572 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5573 {
5574     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5575     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5576 }
5577
5578 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5579 {
5580     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5581     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5582 }
5583
5584 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5585 {
5586     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5587     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5588 }
5589
5590 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5591 {
5592     static const WCHAR table[] = { 'A','p','p','I','d',0 };
5593     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5594 }
5595
5596 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5597 {
5598     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5599     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5600 }
5601
5602 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5603 {
5604     static const WCHAR table[] = { 'M','I','M','E',0 };
5605     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5606 }
5607
5608 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5609 {
5610     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5611     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5612 }
5613
5614 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5615 {
5616     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5617     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5618 }
5619
5620 static const struct _actions StandardActions[] = {
5621     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5622     { szAppSearch, ACTION_AppSearch },
5623     { szBindImage, ACTION_BindImage },
5624     { szCCPSearch, ACTION_CCPSearch },
5625     { szCostFinalize, ACTION_CostFinalize },
5626     { szCostInitialize, ACTION_CostInitialize },
5627     { szCreateFolders, ACTION_CreateFolders },
5628     { szCreateShortcuts, ACTION_CreateShortcuts },
5629     { szDeleteServices, ACTION_DeleteServices },
5630     { szDisableRollback, NULL },
5631     { szDuplicateFiles, ACTION_DuplicateFiles },
5632     { szExecuteAction, ACTION_ExecuteAction },
5633     { szFileCost, ACTION_FileCost },
5634     { szFindRelatedProducts, ACTION_FindRelatedProducts },
5635     { szForceReboot, ACTION_ForceReboot },
5636     { szInstallAdminPackage, NULL },
5637     { szInstallExecute, ACTION_InstallExecute },
5638     { szInstallExecuteAgain, ACTION_InstallExecute },
5639     { szInstallFiles, ACTION_InstallFiles},
5640     { szInstallFinalize, ACTION_InstallFinalize },
5641     { szInstallInitialize, ACTION_InstallInitialize },
5642     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5643     { szInstallValidate, ACTION_InstallValidate },
5644     { szIsolateComponents, ACTION_IsolateComponents },
5645     { szLaunchConditions, ACTION_LaunchConditions },
5646     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5647     { szMoveFiles, ACTION_MoveFiles },
5648     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5649     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5650     { szInstallODBC, ACTION_InstallODBC },
5651     { szInstallServices, ACTION_InstallServices },
5652     { szPatchFiles, ACTION_PatchFiles },
5653     { szProcessComponents, ACTION_ProcessComponents },
5654     { szPublishComponents, ACTION_PublishComponents },
5655     { szPublishFeatures, ACTION_PublishFeatures },
5656     { szPublishProduct, ACTION_PublishProduct },
5657     { szRegisterClassInfo, ACTION_RegisterClassInfo },
5658     { szRegisterComPlus, ACTION_RegisterComPlus},
5659     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5660     { szRegisterFonts, ACTION_RegisterFonts },
5661     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5662     { szRegisterProduct, ACTION_RegisterProduct },
5663     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5664     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5665     { szRegisterUser, ACTION_RegisterUser },
5666     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5667     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5668     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5669     { szRemoveFiles, ACTION_RemoveFiles },
5670     { szRemoveFolders, ACTION_RemoveFolders },
5671     { szRemoveIniValues, ACTION_RemoveIniValues },
5672     { szRemoveODBC, ACTION_RemoveODBC },
5673     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5674     { szRemoveShortcuts, ACTION_RemoveShortcuts },
5675     { szResolveSource, ACTION_ResolveSource },
5676     { szRMCCPSearch, ACTION_RMCCPSearch },
5677     { szScheduleReboot, NULL },
5678     { szSelfRegModules, ACTION_SelfRegModules },
5679     { szSelfUnregModules, ACTION_SelfUnregModules },
5680     { szSetODBCFolders, NULL },
5681     { szStartServices, ACTION_StartServices },
5682     { szStopServices, ACTION_StopServices },
5683     { szUnpublishComponents, ACTION_UnpublishComponents },
5684     { szUnpublishFeatures, ACTION_UnpublishFeatures },
5685     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5686     { szUnregisterComPlus, ACTION_UnregisterComPlus },
5687     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5688     { szUnregisterFonts, ACTION_UnregisterFonts },
5689     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5690     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5691     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5692     { szValidateProductID, ACTION_ValidateProductID },
5693     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5694     { szWriteIniValues, ACTION_WriteIniValues },
5695     { szWriteRegistryValues, ACTION_WriteRegistryValues },
5696     { NULL, NULL },
5697 };