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