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