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