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