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