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