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