msi: Fixed the WriteEnvironmentStrings action.
[wine] / dlls / msi / action.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004,2005 Aric Stewart for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 /*
22  * Pages I need
23  *
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
25
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
27  */
28
29 #include <stdarg.h>
30
31 #define COBJMACROS
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "winsvc.h"
38 #include "odbcinst.h"
39 #include "wine/debug.h"
40 #include "msidefs.h"
41 #include "msipriv.h"
42 #include "winuser.h"
43 #include "shlobj.h"
44 #include "wine/unicode.h"
45 #include "winver.h"
46
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
49
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
51
52 /*
53  * Prototypes
54  */
55 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
56 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
57 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
58
59 /*
60  * consts and values used
61  */
62 static const WCHAR c_colon[] = {'C',':','\\',0};
63
64 static const WCHAR szCreateFolders[] =
65     {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize[] =
67     {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles[] =
69     {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles[] =
71     {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues[] =
73     {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74             'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize[] =
76     {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost[] = 
78     {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize[] = 
80     {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate[] = 
82     {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions[] = 
84     {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents[] = 
86     {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries[] = 
88     {'R','e','g','i','s','t','e','r','T','y','p','e',
89             'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo[] = 
91     {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo[] = 
93     {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts[] = 
95     {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct[] = 
97     {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues[] = 
99     {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules[] = 
101     {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures[] = 
103     {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct[] = 
105     {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute[] = 
107     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain[] = 
109     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110             'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize[] = 
112     {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot[] = 
114     {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource[] =
116     {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch[] = 
118     {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace[] = 
120     {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121             'S','p','a','c','e',0};
122 static const WCHAR szBindImage[] = 
123     {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch[] = 
125     {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices[] = 
127     {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback[] = 
129     {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction[] = 
131     {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts[] = 
133     {'F','i','n','d','R','e','l','a','t','e','d',
134             'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage[] = 
136     {'I','n','s','t','a','l','l','A','d','m','i','n',
137             'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile[] = 
139     {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
140             'F','i','l','e',0};
141 static const WCHAR szIsolateComponents[] = 
142     {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates[] = 
144     {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145             'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles[] = 
147     {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies[] = 
149     {'M','s','i','P','u','b','l','i','s','h',
150             'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies[] = 
152     {'M','s','i','U','n','p','u','b','l','i','s','h',
153             'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC[] = 
155     {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices[] = 
157     {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles[] = 
159     {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents[] = 
161     {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus[] =
163     {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo[] =
165     {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
166             'I','n','f','o',0};
167 static const WCHAR szRegisterFonts[] =
168     {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo[] =
170     {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser[] =
172     {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles[] =
174     {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175             'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings[] =
177     {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178             'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts[] =
180     {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181             'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles[] =
183     {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders[] =
185     {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues[] =
187     {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC[] =
189     {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues[] =
191     {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192             'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts[] =
194     {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch[] =
196     {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot[] =
198     {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules[] =
200     {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders[] =
202     {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices[] =
204     {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices[] =
206     {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents[] =
208     {'U','n','p','u','b','l','i','s','h',
209             'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures[] =
211     {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo[] =
213     {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
214             'I','n','f','o',0};
215 static const WCHAR szUnregisterComPlus[] =
216     {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo[] =
218     {'U','n','r','e','g','i','s','t','e','r',
219             'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts[] =
221     {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo[] =
223     {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo[] =
225     {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
226             'I','n','f','o',0};
227 static const WCHAR szUnregisterTypeLibraries[] =
228     {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229             'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID[] =
231     {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings[] =
233     {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234             'S','t','r','i','n','g','s',0};
235
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
238
239 struct _actions {
240     LPCWSTR action;
241     STANDARDACTIONHANDLER handler;
242 };
243
244 static const struct _actions StandardActions[];
245
246
247 /********************************************************
248  * helper functions
249  ********************************************************/
250
251 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
252 {
253     static const WCHAR Query_t[] = 
254         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255          '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256          'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=', 
257          ' ','\'','%','s','\'',0};
258     MSIRECORD * row;
259
260     row = MSI_QueryGetRecord( package->db, Query_t, action );
261     if (!row)
262         return;
263     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
264     msiobj_release(&row->hdr);
265 }
266
267 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, 
268                           UINT rc)
269 {
270     MSIRECORD * row;
271     static const WCHAR template_s[]=
272         {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273          '%','s', '.',0};
274     static const WCHAR template_e[]=
275         {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276          '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
277          '%','i','.',0};
278     static const WCHAR format[] = 
279         {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
280     WCHAR message[1024];
281     WCHAR timet[0x100];
282
283     GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
284     if (start)
285         sprintfW(message,template_s,timet,action);
286     else
287         sprintfW(message,template_e,timet,action,rc);
288     
289     row = MSI_CreateRecord(1);
290     MSI_RecordSetStringW(row,1,message);
291  
292     MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
293     msiobj_release(&row->hdr);
294 }
295
296 static UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
297 {
298     LPCWSTR ptr,ptr2;
299     BOOL quote;
300     DWORD len;
301     LPWSTR prop = NULL, val = NULL;
302
303     if (!szCommandLine)
304         return ERROR_SUCCESS;
305
306     ptr = szCommandLine;
307        
308     while (*ptr)
309     {
310         if (*ptr==' ')
311         {
312             ptr++;
313             continue;
314         }
315
316         TRACE("Looking at %s\n",debugstr_w(ptr));
317
318         ptr2 = strchrW(ptr,'=');
319         if (!ptr2)
320         {
321             ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
322             break;
323         }
324  
325         quote = FALSE;
326
327         len = ptr2-ptr;
328         prop = msi_alloc((len+1)*sizeof(WCHAR));
329         memcpy(prop,ptr,len*sizeof(WCHAR));
330         prop[len]=0;
331         ptr2++;
332        
333         len = 0; 
334         ptr = ptr2; 
335         while (*ptr && (quote || (!quote && *ptr!=' ')))
336         {
337             if (*ptr == '"')
338                 quote = !quote;
339             ptr++;
340             len++;
341         }
342        
343         if (*ptr2=='"')
344         {
345             ptr2++;
346             len -= 2;
347         }
348         val = msi_alloc((len+1)*sizeof(WCHAR));
349         memcpy(val,ptr2,len*sizeof(WCHAR));
350         val[len] = 0;
351
352         if (lstrlenW(prop) > 0)
353         {
354             TRACE("Found commandline property (%s) = (%s)\n", 
355                    debugstr_w(prop), debugstr_w(val));
356             MSI_SetPropertyW(package,prop,val);
357         }
358         msi_free(val);
359         msi_free(prop);
360     }
361
362     return ERROR_SUCCESS;
363 }
364
365
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
367 {
368     LPCWSTR pc;
369     LPWSTR p, *ret = NULL;
370     UINT count = 0;
371
372     if (!str)
373         return ret;
374
375     /* count the number of substrings */
376     for ( pc = str, count = 0; pc; count++ )
377     {
378         pc = strchrW( pc, sep );
379         if (pc)
380             pc++;
381     }
382
383     /* allocate space for an array of substring pointers and the substrings */
384     ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385                      (lstrlenW(str)+1) * sizeof(WCHAR) );
386     if (!ret)
387         return ret;
388
389     /* copy the string and set the pointers */
390     p = (LPWSTR) &ret[count+1];
391     lstrcpyW( p, str );
392     for( count = 0; (ret[count] = p); count++ )
393     {
394         p = strchrW( p, sep );
395         if (p)
396             *p++ = 0;
397     }
398
399     return ret;
400 }
401
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
403 {
404     WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405     LPWSTR prod_code, patch_product;
406     UINT ret;
407
408     prod_code = msi_dup_property( package, szProductCode );
409     patch_product = msi_get_suminfo_product( patch );
410
411     TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
412
413     if ( strstrW( patch_product, prod_code ) )
414         ret = ERROR_SUCCESS;
415     else
416         ret = ERROR_FUNCTION_FAILED;
417
418     msi_free( patch_product );
419     msi_free( prod_code );
420
421     return ret;
422 }
423
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425                                  MSIDATABASE *patch_db, LPCWSTR name )
426 {
427     UINT ret = ERROR_FUNCTION_FAILED;
428     IStorage *stg = NULL;
429     HRESULT r;
430
431     TRACE("%p %s\n", package, debugstr_w(name) );
432
433     if (*name++ != ':')
434     {
435         ERR("expected a colon in %s\n", debugstr_w(name));
436         return ERROR_FUNCTION_FAILED;
437     }
438
439     r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
440     if (SUCCEEDED(r))
441     {
442         ret = msi_check_transform_applicable( package, stg );
443         if (ret == ERROR_SUCCESS)
444             msi_table_apply_transform( package->db, stg );
445         else
446             TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447         IStorage_Release( stg );
448     }
449     else
450         ERR("failed to open substorage %s\n", debugstr_w(name));
451
452     return ERROR_SUCCESS;
453 }
454
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
456 {
457     static const WCHAR szProdID[] = { 'P','r','o','d','u','c','t','I','D',0 };
458     LPWSTR guid_list, *guids, product_id;
459     UINT i, ret = ERROR_FUNCTION_FAILED;
460
461     product_id = msi_dup_property( package, szProdID );
462     if (!product_id)
463     {
464         /* FIXME: the property ProductID should be written into the DB somewhere */
465         ERR("no product ID to check\n");
466         return ERROR_SUCCESS;
467     }
468
469     guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470     guids = msi_split_string( guid_list, ';' );
471     for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
472     {
473         if (!lstrcmpW( guids[i], product_id ))
474             ret = ERROR_SUCCESS;
475     }
476     msi_free( guids );
477     msi_free( guid_list );
478     msi_free( product_id );
479
480     return ret;
481 }
482
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
484 {
485     MSISUMMARYINFO *si;
486     LPWSTR str, *substorage;
487     UINT i, r = ERROR_SUCCESS;
488
489     si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490     if (!si)
491         return ERROR_FUNCTION_FAILED;
492
493     msi_check_patch_applicable( package, si );
494
495     /* enumerate the substorage */
496     str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497     substorage = msi_split_string( str, ';' );
498     for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499         r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500     msi_free( substorage );
501     msi_free( str );
502
503     /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
504
505     msiobj_release( &si->hdr );
506
507     return r;
508 }
509
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
511 {
512     MSIDATABASE *patch_db = NULL;
513     UINT r;
514
515     TRACE("%p %s\n", package, debugstr_w( file ) );
516
517     /* FIXME:
518      *  We probably want to make sure we only open a patch collection here.
519      *  Patch collections (.msp) and databases (.msi) have different GUIDs
520      *  but currently MSI_OpenDatabaseW will accept both.
521      */
522     r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523     if ( r != ERROR_SUCCESS )
524     {
525         ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526         return r;
527     }
528
529     msi_parse_patch_summary( package, patch_db );
530
531     /*
532      * There might be a CAB file in the patch package,
533      * so append it to the list of storage to search for streams.
534      */
535     append_storage_to_db( package->db, patch_db->storage );
536
537     msiobj_release( &patch_db->hdr );
538
539     return ERROR_SUCCESS;
540 }
541
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
544 {
545     static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546     LPWSTR patch_list, *patches;
547     UINT i, r = ERROR_SUCCESS;
548
549     patch_list = msi_dup_property( package, szPatch );
550
551     TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
552
553     patches = msi_split_string( patch_list, ';' );
554     for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555         r = msi_apply_patch_package( package, patches[i] );
556
557     msi_free( patches );
558     msi_free( patch_list );
559
560     return r;
561 }
562
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
564 {
565     static const WCHAR szTransforms[] = {
566         'T','R','A','N','S','F','O','R','M','S',0 };
567     LPWSTR xform_list, *xforms;
568     UINT i, r = ERROR_SUCCESS;
569
570     xform_list = msi_dup_property( package, szTransforms );
571     xforms = msi_split_string( xform_list, ';' );
572
573     for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
574     {
575         if (xforms[i][0] == ':')
576             r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
577         else
578             r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
579     }
580
581     msi_free( xforms );
582     msi_free( xform_list );
583
584     return r;
585 }
586
587 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);
752         else
753             rc = ACTION_PerformAction(package,action,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);
793     else
794         rc = ACTION_PerformAction(iap->package,action,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, BOOL force )
984 {
985     BOOL ret=FALSE;
986     UINT arc;
987
988     arc = ACTION_CustomAction(package,action, 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, 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, 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)
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, 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, 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     VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz );
1986     msi_free( version );
1987
1988     sprintfW( filever, name_fmt,
1989         HIWORD(lpVer->dwFileVersionMS),
1990         LOWORD(lpVer->dwFileVersionMS),
1991         HIWORD(lpVer->dwFileVersionLS),
1992         LOWORD(lpVer->dwFileVersionLS));
1993
1994     return strdupW( filever );
1995 }
1996
1997 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1998 {
1999     LPWSTR file_version;
2000     MSIFILE *file;
2001
2002     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2003     {
2004         MSICOMPONENT* comp = file->Component;
2005         LPWSTR p;
2006
2007         if (!comp)
2008             continue;
2009
2010         if (file->IsCompressed)
2011             comp->ForceLocalState = TRUE;
2012
2013         /* calculate target */
2014         p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2015
2016         msi_free(file->TargetPath);
2017
2018         TRACE("file %s is named %s\n",
2019                debugstr_w(file->File), debugstr_w(file->FileName));
2020
2021         file->TargetPath = build_directory_name(2, p, file->FileName);
2022
2023         msi_free(p);
2024
2025         TRACE("file %s resolves to %s\n",
2026                debugstr_w(file->File), debugstr_w(file->TargetPath));
2027
2028         /* don't check files of components that aren't installed */
2029         if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2030             comp->Installed == INSTALLSTATE_ABSENT)
2031         {
2032             file->state = msifs_missing;  /* assume files are missing */
2033             continue;
2034         }
2035
2036         if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2037         {
2038             file->state = msifs_missing;
2039             comp->Cost += file->FileSize;
2040             comp->Installed = INSTALLSTATE_INCOMPLETE;
2041             continue;
2042         }
2043
2044         if (file->Version &&
2045             (file_version = msi_get_disk_file_version( file->TargetPath )))
2046         {
2047             TRACE("new %s old %s\n", debugstr_w(file->Version),
2048                   debugstr_w(file_version));
2049             /* FIXME: seems like a bad way to compare version numbers */
2050             if (lstrcmpiW(file_version, file->Version)<0)
2051             {
2052                 file->state = msifs_overwrite;
2053                 comp->Cost += file->FileSize;
2054                 comp->Installed = INSTALLSTATE_INCOMPLETE;
2055             }
2056             else
2057                 file->state = msifs_present;
2058             msi_free( file_version );
2059         }
2060         else
2061             file->state = msifs_present;
2062     }
2063
2064     return ERROR_SUCCESS;
2065 }
2066
2067 /*
2068  * A lot is done in this function aside from just the costing.
2069  * The costing needs to be implemented at some point but for now I am going
2070  * to focus on the directory building
2071  *
2072  */
2073 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2074 {
2075     static const WCHAR ExecSeqQuery[] =
2076         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2077          '`','D','i','r','e','c','t','o','r','y','`',0};
2078     static const WCHAR ConditionQuery[] =
2079         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2080          '`','C','o','n','d','i','t','i','o','n','`',0};
2081     static const WCHAR szCosting[] =
2082         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2083     static const WCHAR szlevel[] =
2084         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2085     static const WCHAR szOne[] = { '1', 0 };
2086     MSICOMPONENT *comp;
2087     UINT rc;
2088     MSIQUERY * view;
2089     LPWSTR level;
2090
2091     if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2092         return ERROR_SUCCESS;
2093
2094     TRACE("Building Directory properties\n");
2095
2096     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2097     if (rc == ERROR_SUCCESS)
2098     {
2099         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2100                         package);
2101         msiobj_release(&view->hdr);
2102     }
2103
2104     /* read components states from the registry */
2105     ACTION_GetComponentInstallStates(package);
2106
2107     TRACE("File calculations\n");
2108     msi_check_file_install_states( package );
2109
2110     TRACE("Evaluating Condition Table\n");
2111
2112     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2113     if (rc == ERROR_SUCCESS)
2114     {
2115         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2116                     package);
2117         msiobj_release(&view->hdr);
2118     }
2119
2120     TRACE("Enabling or Disabling Components\n");
2121     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2122     {
2123         if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2124         {
2125             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2126             comp->Enabled = FALSE;
2127         }
2128     }
2129
2130     MSI_SetPropertyW(package,szCosting,szOne);
2131     /* set default run level if not set */
2132     level = msi_dup_property( package, szlevel );
2133     if (!level)
2134         MSI_SetPropertyW(package,szlevel, szOne);
2135     msi_free(level);
2136
2137     ACTION_UpdateFeatureInstallStates(package);
2138
2139     return MSI_SetFeatureStates(package);
2140 }
2141
2142 /* OK this value is "interpreted" and then formatted based on the 
2143    first few characters */
2144 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2145                          DWORD *size)
2146 {
2147     LPSTR data = NULL;
2148     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2149     {
2150         if (value[1]=='x')
2151         {
2152             LPWSTR ptr;
2153             CHAR byte[5];
2154             LPWSTR deformated = NULL;
2155             int count;
2156
2157             deformat_string(package, &value[2], &deformated);
2158
2159             /* binary value type */
2160             ptr = deformated;
2161             *type = REG_BINARY;
2162             if (strlenW(ptr)%2)
2163                 *size = (strlenW(ptr)/2)+1;
2164             else
2165                 *size = strlenW(ptr)/2;
2166
2167             data = msi_alloc(*size);
2168
2169             byte[0] = '0'; 
2170             byte[1] = 'x'; 
2171             byte[4] = 0; 
2172             count = 0;
2173             /* if uneven pad with a zero in front */
2174             if (strlenW(ptr)%2)
2175             {
2176                 byte[2]= '0';
2177                 byte[3]= *ptr;
2178                 ptr++;
2179                 data[count] = (BYTE)strtol(byte,NULL,0);
2180                 count ++;
2181                 TRACE("Uneven byte count\n");
2182             }
2183             while (*ptr)
2184             {
2185                 byte[2]= *ptr;
2186                 ptr++;
2187                 byte[3]= *ptr;
2188                 ptr++;
2189                 data[count] = (BYTE)strtol(byte,NULL,0);
2190                 count ++;
2191             }
2192             msi_free(deformated);
2193
2194             TRACE("Data %i bytes(%i)\n",*size,count);
2195         }
2196         else
2197         {
2198             LPWSTR deformated;
2199             LPWSTR p;
2200             DWORD d = 0;
2201             deformat_string(package, &value[1], &deformated);
2202
2203             *type=REG_DWORD; 
2204             *size = sizeof(DWORD);
2205             data = msi_alloc(*size);
2206             p = deformated;
2207             if (*p == '-')
2208                 p++;
2209             while (*p)
2210             {
2211                 if ( (*p < '0') || (*p > '9') )
2212                     break;
2213                 d *= 10;
2214                 d += (*p - '0');
2215                 p++;
2216             }
2217             if (deformated[0] == '-')
2218                 d = -d;
2219             *(LPDWORD)data = d;
2220             TRACE("DWORD %i\n",*(LPDWORD)data);
2221
2222             msi_free(deformated);
2223         }
2224     }
2225     else
2226     {
2227         static const WCHAR szMulti[] = {'[','~',']',0};
2228         LPCWSTR ptr;
2229         *type=REG_SZ;
2230
2231         if (value[0]=='#')
2232         {
2233             if (value[1]=='%')
2234             {
2235                 ptr = &value[2];
2236                 *type=REG_EXPAND_SZ;
2237             }
2238             else
2239                 ptr = &value[1];
2240          }
2241          else
2242             ptr=value;
2243
2244         if (strstrW(value,szMulti))
2245             *type = REG_MULTI_SZ;
2246
2247         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2248     }
2249     return data;
2250 }
2251
2252 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2253 {
2254     MSIPACKAGE *package = (MSIPACKAGE*)param;
2255     static const WCHAR szHCR[] = 
2256         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2257          'R','O','O','T','\\',0};
2258     static const WCHAR szHCU[] =
2259         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2260          'U','S','E','R','\\',0};
2261     static const WCHAR szHLM[] =
2262         {'H','K','E','Y','_','L','O','C','A','L','_',
2263          'M','A','C','H','I','N','E','\\',0};
2264     static const WCHAR szHU[] =
2265         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2266
2267     LPSTR value_data = NULL;
2268     HKEY  root_key, hkey;
2269     DWORD type,size;
2270     LPWSTR  deformated;
2271     LPCWSTR szRoot, component, name, key, value;
2272     MSICOMPONENT *comp;
2273     MSIRECORD * uirow;
2274     LPWSTR uikey;
2275     INT   root;
2276     BOOL check_first = FALSE;
2277     UINT rc;
2278
2279     ui_progress(package,2,0,0,0);
2280
2281     value = NULL;
2282     key = NULL;
2283     uikey = NULL;
2284     name = NULL;
2285
2286     component = MSI_RecordGetString(row, 6);
2287     comp = get_loaded_component(package,component);
2288     if (!comp)
2289         return ERROR_SUCCESS;
2290
2291     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2292     {
2293         TRACE("Skipping write due to disabled component %s\n",
2294                         debugstr_w(component));
2295
2296         comp->Action = comp->Installed;
2297
2298         return ERROR_SUCCESS;
2299     }
2300
2301     comp->Action = INSTALLSTATE_LOCAL;
2302
2303     name = MSI_RecordGetString(row, 4);
2304     if( MSI_RecordIsNull(row,5) && name )
2305     {
2306         /* null values can have special meanings */
2307         if (name[0]=='-' && name[1] == 0)
2308                 return ERROR_SUCCESS;
2309         else if ((name[0]=='+' && name[1] == 0) || 
2310                  (name[0] == '*' && name[1] == 0))
2311                 name = NULL;
2312         check_first = TRUE;
2313     }
2314
2315     root = MSI_RecordGetInteger(row,2);
2316     key = MSI_RecordGetString(row, 3);
2317
2318     /* get the root key */
2319     switch (root)
2320     {
2321         case -1: 
2322             {
2323                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2324                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2325                 if (all_users && all_users[0] == '1')
2326                 {
2327                     root_key = HKEY_LOCAL_MACHINE;
2328                     szRoot = szHLM;
2329                 }
2330                 else
2331                 {
2332                     root_key = HKEY_CURRENT_USER;
2333                     szRoot = szHCU;
2334                 }
2335                 msi_free(all_users);
2336             }
2337                  break;
2338         case 0:  root_key = HKEY_CLASSES_ROOT; 
2339                  szRoot = szHCR;
2340                  break;
2341         case 1:  root_key = HKEY_CURRENT_USER;
2342                  szRoot = szHCU;
2343                  break;
2344         case 2:  root_key = HKEY_LOCAL_MACHINE;
2345                  szRoot = szHLM;
2346                  break;
2347         case 3:  root_key = HKEY_USERS; 
2348                  szRoot = szHU;
2349                  break;
2350         default:
2351                  ERR("Unknown root %i\n",root);
2352                  root_key=NULL;
2353                  szRoot = NULL;
2354                  break;
2355     }
2356     if (!root_key)
2357         return ERROR_SUCCESS;
2358
2359     deformat_string(package, key , &deformated);
2360     size = strlenW(deformated) + strlenW(szRoot) + 1;
2361     uikey = msi_alloc(size*sizeof(WCHAR));
2362     strcpyW(uikey,szRoot);
2363     strcatW(uikey,deformated);
2364
2365     if (RegCreateKeyW( root_key, deformated, &hkey))
2366     {
2367         ERR("Could not create key %s\n",debugstr_w(deformated));
2368         msi_free(deformated);
2369         msi_free(uikey);
2370         return ERROR_SUCCESS;
2371     }
2372     msi_free(deformated);
2373
2374     value = MSI_RecordGetString(row,5);
2375     if (value)
2376         value_data = parse_value(package, value, &type, &size); 
2377     else
2378     {
2379         static const WCHAR szEmpty[] = {0};
2380         value_data = (LPSTR)strdupW(szEmpty);
2381         size = 0;
2382         type = REG_SZ;
2383     }
2384
2385     deformat_string(package, name, &deformated);
2386
2387     /* get the double nulls to terminate SZ_MULTI */
2388     if (type == REG_MULTI_SZ)
2389         size +=sizeof(WCHAR);
2390
2391     if (!check_first)
2392     {
2393         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2394                         debugstr_w(uikey));
2395         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2396     }
2397     else
2398     {
2399         DWORD sz = 0;
2400         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2401         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2402         {
2403             TRACE("value %s of %s checked already exists\n",
2404                             debugstr_w(deformated), debugstr_w(uikey));
2405         }
2406         else
2407         {
2408             TRACE("Checked and setting value %s of %s\n",
2409                             debugstr_w(deformated), debugstr_w(uikey));
2410             if (deformated || size)
2411                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2412         }
2413     }
2414     RegCloseKey(hkey);
2415
2416     uirow = MSI_CreateRecord(3);
2417     MSI_RecordSetStringW(uirow,2,deformated);
2418     MSI_RecordSetStringW(uirow,1,uikey);
2419
2420     if (type == REG_SZ)
2421         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2422     else
2423         MSI_RecordSetStringW(uirow,3,value);
2424
2425     ui_actiondata(package,szWriteRegistryValues,uirow);
2426     msiobj_release( &uirow->hdr );
2427
2428     msi_free(value_data);
2429     msi_free(deformated);
2430     msi_free(uikey);
2431
2432     return ERROR_SUCCESS;
2433 }
2434
2435 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2436 {
2437     UINT rc;
2438     MSIQUERY * view;
2439     static const WCHAR ExecSeqQuery[] =
2440         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2441          '`','R','e','g','i','s','t','r','y','`',0 };
2442
2443     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2444     if (rc != ERROR_SUCCESS)
2445         return ERROR_SUCCESS;
2446
2447     /* increment progress bar each time action data is sent */
2448     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2449
2450     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2451
2452     msiobj_release(&view->hdr);
2453     return rc;
2454 }
2455
2456 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2457 {
2458     package->script->CurrentlyScripting = TRUE;
2459
2460     return ERROR_SUCCESS;
2461 }
2462
2463
2464 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2465 {
2466     MSICOMPONENT *comp;
2467     DWORD progress = 0;
2468     DWORD total = 0;
2469     static const WCHAR q1[]=
2470         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2471          '`','R','e','g','i','s','t','r','y','`',0};
2472     UINT rc;
2473     MSIQUERY * view;
2474     MSIFEATURE *feature;
2475     MSIFILE *file;
2476
2477     TRACE("InstallValidate\n");
2478
2479     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2480     if (rc == ERROR_SUCCESS)
2481     {
2482         MSI_IterateRecords( view, &progress, NULL, package );
2483         msiobj_release( &view->hdr );
2484         total += progress * REG_PROGRESS_VALUE;
2485     }
2486
2487     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2488         total += COMPONENT_PROGRESS_VALUE;
2489
2490     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2491         total += file->FileSize;
2492
2493     ui_progress(package,0,total,0,0);
2494
2495     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2496     {
2497         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2498             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2499             feature->ActionRequest);
2500     }
2501     
2502     return ERROR_SUCCESS;
2503 }
2504
2505 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2506 {
2507     MSIPACKAGE* package = (MSIPACKAGE*)param;
2508     LPCWSTR cond = NULL; 
2509     LPCWSTR message = NULL;
2510     static const WCHAR title[]=
2511         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2512
2513     cond = MSI_RecordGetString(row,1);
2514
2515     if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
2516     {
2517         LPWSTR deformated;
2518         message = MSI_RecordGetString(row,2);
2519         deformat_string(package,message,&deformated); 
2520         MessageBoxW(NULL,deformated,title,MB_OK);
2521         msi_free(deformated);
2522         return ERROR_FUNCTION_FAILED;
2523     }
2524
2525     return ERROR_SUCCESS;
2526 }
2527
2528 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2529 {
2530     UINT rc;
2531     MSIQUERY * view = NULL;
2532     static const WCHAR ExecSeqQuery[] =
2533         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2534          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2535
2536     TRACE("Checking launch conditions\n");
2537
2538     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2539     if (rc != ERROR_SUCCESS)
2540         return ERROR_SUCCESS;
2541
2542     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2543     msiobj_release(&view->hdr);
2544
2545     return rc;
2546 }
2547
2548 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2549 {
2550
2551     if (!cmp->KeyPath)
2552         return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2553
2554     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2555     {
2556         MSIRECORD * row = 0;
2557         UINT root,len;
2558         LPWSTR deformated,buffer,deformated_name;
2559         LPCWSTR key,name;
2560         static const WCHAR ExecSeqQuery[] =
2561             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2562              '`','R','e','g','i','s','t','r','y','`',' ',
2563              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2564              ' ','=',' ' ,'\'','%','s','\'',0 };
2565         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2566         static const WCHAR fmt2[]=
2567             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2568
2569         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2570         if (!row)
2571             return NULL;
2572
2573         root = MSI_RecordGetInteger(row,2);
2574         key = MSI_RecordGetString(row, 3);
2575         name = MSI_RecordGetString(row, 4);
2576         deformat_string(package, key , &deformated);
2577         deformat_string(package, name, &deformated_name);
2578
2579         len = strlenW(deformated) + 6;
2580         if (deformated_name)
2581             len+=strlenW(deformated_name);
2582
2583         buffer = msi_alloc( len *sizeof(WCHAR));
2584
2585         if (deformated_name)
2586             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2587         else
2588             sprintfW(buffer,fmt,root,deformated);
2589
2590         msi_free(deformated);
2591         msi_free(deformated_name);
2592         msiobj_release(&row->hdr);
2593
2594         return buffer;
2595     }
2596     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2597     {
2598         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2599         return NULL;
2600     }
2601     else
2602     {
2603         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2604
2605         if (file)
2606             return strdupW( file->TargetPath );
2607     }
2608     return NULL;
2609 }
2610
2611 static HKEY openSharedDLLsKey(void)
2612 {
2613     HKEY hkey=0;
2614     static const WCHAR path[] =
2615         {'S','o','f','t','w','a','r','e','\\',
2616          'M','i','c','r','o','s','o','f','t','\\',
2617          'W','i','n','d','o','w','s','\\',
2618          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2619          'S','h','a','r','e','d','D','L','L','s',0};
2620
2621     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2622     return hkey;
2623 }
2624
2625 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2626 {
2627     HKEY hkey;
2628     DWORD count=0;
2629     DWORD type;
2630     DWORD sz = sizeof(count);
2631     DWORD rc;
2632     
2633     hkey = openSharedDLLsKey();
2634     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2635     if (rc != ERROR_SUCCESS)
2636         count = 0;
2637     RegCloseKey(hkey);
2638     return count;
2639 }
2640
2641 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2642 {
2643     HKEY hkey;
2644
2645     hkey = openSharedDLLsKey();
2646     if (count > 0)
2647         msi_reg_set_val_dword( hkey, path, count );
2648     else
2649         RegDeleteValueW(hkey,path);
2650     RegCloseKey(hkey);
2651     return count;
2652 }
2653
2654 /*
2655  * Return TRUE if the count should be written out and FALSE if not
2656  */
2657 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2658 {
2659     MSIFEATURE *feature;
2660     INT count = 0;
2661     BOOL write = FALSE;
2662
2663     /* only refcount DLLs */
2664     if (comp->KeyPath == NULL || 
2665         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2666         comp->Attributes & msidbComponentAttributesODBCDataSource)
2667         write = FALSE;
2668     else
2669     {
2670         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2671         write = (count > 0);
2672
2673         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2674             write = TRUE;
2675     }
2676
2677     /* increment counts */
2678     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2679     {
2680         ComponentList *cl;
2681
2682         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2683             continue;
2684
2685         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2686         {
2687             if ( cl->component == comp )
2688                 count++;
2689         }
2690     }
2691
2692     /* decrement counts */
2693     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2694     {
2695         ComponentList *cl;
2696
2697         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2698             continue;
2699
2700         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2701         {
2702             if ( cl->component == comp )
2703                 count--;
2704         }
2705     }
2706
2707     /* ref count all the files in the component */
2708     if (write)
2709     {
2710         MSIFILE *file;
2711
2712         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2713         {
2714             if (file->Component == comp)
2715                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2716         }
2717     }
2718     
2719     /* add a count for permenent */
2720     if (comp->Attributes & msidbComponentAttributesPermanent)
2721         count ++;
2722     
2723     comp->RefCount = count;
2724
2725     if (write)
2726         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2727 }
2728
2729 /*
2730  * Ok further analysis makes me think that this work is
2731  * actually done in the PublishComponents and PublishFeatures
2732  * step, and not here.  It appears like the keypath and all that is
2733  * resolved in this step, however actually written in the Publish steps.
2734  * But we will leave it here for now because it is unclear
2735  */
2736 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2737 {
2738     WCHAR squished_pc[GUID_SIZE];
2739     WCHAR squished_cc[GUID_SIZE];
2740     UINT rc;
2741     MSICOMPONENT *comp;
2742     HKEY hkey=0,hkey2=0;
2743
2744     /* writes the Component and Features values to the registry */
2745
2746     rc = MSIREG_OpenComponents(&hkey);
2747     if (rc != ERROR_SUCCESS)
2748         return rc;
2749
2750     squash_guid(package->ProductCode,squished_pc);
2751     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2752
2753     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2754     {
2755         MSIRECORD * uirow;
2756
2757         ui_progress(package,2,0,0,0);
2758         if (!comp->ComponentId)
2759             continue;
2760
2761         squash_guid(comp->ComponentId,squished_cc);
2762
2763         msi_free(comp->FullKeypath);
2764         comp->FullKeypath = resolve_keypath( package, comp );
2765
2766         /* do the refcounting */
2767         ACTION_RefCountComponent( package, comp );
2768
2769         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2770                             debugstr_w(comp->Component),
2771                             debugstr_w(squished_cc),
2772                             debugstr_w(comp->FullKeypath),
2773                             comp->RefCount);
2774         /*
2775          * Write the keypath out if the component is to be registered
2776          * and delete the key if the component is to be deregistered
2777          */
2778         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2779         {
2780             rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2781             if (rc != ERROR_SUCCESS)
2782                 continue;
2783
2784             if (!comp->FullKeypath)
2785                 continue;
2786
2787             msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2788
2789             if (comp->Attributes & msidbComponentAttributesPermanent)
2790             {
2791                 static const WCHAR szPermKey[] =
2792                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2793                       '0','0','0','0','0','0','0','0','0','0','0','0',
2794                       '0','0','0','0','0','0','0','0',0 };
2795
2796                 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2797             }
2798
2799             RegCloseKey(hkey2);
2800         }
2801         else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2802         {
2803             DWORD res;
2804
2805             rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2806             if (rc != ERROR_SUCCESS)
2807                 continue;
2808
2809             RegDeleteValueW(hkey2,squished_pc);
2810
2811             /* if the key is empty delete it */
2812             res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2813             RegCloseKey(hkey2);
2814             if (res == ERROR_NO_MORE_ITEMS)
2815                 RegDeleteKeyW(hkey,squished_cc);
2816
2817         }
2818
2819         /* UI stuff */
2820         uirow = MSI_CreateRecord(3);
2821         MSI_RecordSetStringW(uirow,1,package->ProductCode);
2822         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2823         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2824         ui_actiondata(package,szProcessComponents,uirow);
2825         msiobj_release( &uirow->hdr );
2826     }
2827     RegCloseKey(hkey);
2828     return rc;
2829 }
2830
2831 typedef struct {
2832     CLSID       clsid;
2833     LPWSTR      source;
2834
2835     LPWSTR      path;
2836     ITypeLib    *ptLib;
2837 } typelib_struct;
2838
2839 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
2840                                        LPWSTR lpszName, LONG_PTR lParam)
2841 {
2842     TLIBATTR *attr;
2843     typelib_struct *tl_struct = (typelib_struct*) lParam;
2844     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2845     int sz; 
2846     HRESULT res;
2847
2848     if (!IS_INTRESOURCE(lpszName))
2849     {
2850         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2851         return TRUE;
2852     }
2853
2854     sz = strlenW(tl_struct->source)+4;
2855     sz *= sizeof(WCHAR);
2856
2857     if ((INT_PTR)lpszName == 1)
2858         tl_struct->path = strdupW(tl_struct->source);
2859     else
2860     {
2861         tl_struct->path = msi_alloc(sz);
2862         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2863     }
2864
2865     TRACE("trying %s\n", debugstr_w(tl_struct->path));
2866     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2867     if (!SUCCEEDED(res))
2868     {
2869         msi_free(tl_struct->path);
2870         tl_struct->path = NULL;
2871
2872         return TRUE;
2873     }
2874
2875     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2876     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2877     {
2878         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2879         return FALSE;
2880     }
2881
2882     msi_free(tl_struct->path);
2883     tl_struct->path = NULL;
2884
2885     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2886     ITypeLib_Release(tl_struct->ptLib);
2887
2888     return TRUE;
2889 }
2890
2891 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2892 {
2893     MSIPACKAGE* package = (MSIPACKAGE*)param;
2894     LPCWSTR component;
2895     MSICOMPONENT *comp;
2896     MSIFILE *file;
2897     typelib_struct tl_struct;
2898     HMODULE module;
2899     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2900
2901     component = MSI_RecordGetString(row,3);
2902     comp = get_loaded_component(package,component);
2903     if (!comp)
2904         return ERROR_SUCCESS;
2905
2906     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2907     {
2908         TRACE("Skipping typelib reg due to disabled component\n");
2909
2910         comp->Action = comp->Installed;
2911
2912         return ERROR_SUCCESS;
2913     }
2914
2915     comp->Action = INSTALLSTATE_LOCAL;
2916
2917     file = get_loaded_file( package, comp->KeyPath ); 
2918     if (!file)
2919         return ERROR_SUCCESS;
2920
2921     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2922     if (module)
2923     {
2924         LPCWSTR guid;
2925         guid = MSI_RecordGetString(row,1);
2926         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2927         tl_struct.source = strdupW( file->TargetPath );
2928         tl_struct.path = NULL;
2929
2930         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2931                         (LONG_PTR)&tl_struct);
2932
2933         if (tl_struct.path)
2934         {
2935             LPWSTR help = NULL;
2936             LPCWSTR helpid;
2937             HRESULT res;
2938
2939             helpid = MSI_RecordGetString(row,6);
2940
2941             if (helpid)
2942                 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2943             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2944             msi_free(help);
2945
2946             if (!SUCCEEDED(res))
2947                 ERR("Failed to register type library %s\n",
2948                         debugstr_w(tl_struct.path));
2949             else
2950             {
2951                 ui_actiondata(package,szRegisterTypeLibraries,row);
2952
2953                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2954             }
2955
2956             ITypeLib_Release(tl_struct.ptLib);
2957             msi_free(tl_struct.path);
2958         }
2959         else
2960             ERR("Failed to load type library %s\n",
2961                     debugstr_w(tl_struct.source));
2962
2963         FreeLibrary(module);
2964         msi_free(tl_struct.source);
2965     }
2966     else
2967         ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2968
2969     return ERROR_SUCCESS;
2970 }
2971
2972 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2973 {
2974     /* 
2975      * OK this is a bit confusing.. I am given a _Component key and I believe
2976      * that the file that is being registered as a type library is the "key file
2977      * of that component" which I interpret to mean "The file in the KeyPath of
2978      * that component".
2979      */
2980     UINT rc;
2981     MSIQUERY * view;
2982     static const WCHAR Query[] =
2983         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2984          '`','T','y','p','e','L','i','b','`',0};
2985
2986     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2987     if (rc != ERROR_SUCCESS)
2988         return ERROR_SUCCESS;
2989
2990     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2991     msiobj_release(&view->hdr);
2992     return rc;
2993 }
2994
2995 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2996 {
2997     MSIPACKAGE *package = (MSIPACKAGE*)param;
2998     LPWSTR target_file, target_folder, filename;
2999     LPCWSTR buffer, extension;
3000     MSICOMPONENT *comp;
3001     static const WCHAR szlnk[]={'.','l','n','k',0};
3002     IShellLinkW *sl = NULL;
3003     IPersistFile *pf = NULL;
3004     HRESULT res;
3005
3006     buffer = MSI_RecordGetString(row,4);
3007     comp = get_loaded_component(package,buffer);
3008     if (!comp)
3009         return ERROR_SUCCESS;
3010
3011     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3012     {
3013         TRACE("Skipping shortcut creation due to disabled component\n");
3014
3015         comp->Action = comp->Installed;
3016
3017         return ERROR_SUCCESS;
3018     }
3019
3020     comp->Action = INSTALLSTATE_LOCAL;
3021
3022     ui_actiondata(package,szCreateShortcuts,row);
3023
3024     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3025                     &IID_IShellLinkW, (LPVOID *) &sl );
3026
3027     if (FAILED( res ))
3028     {
3029         ERR("CLSID_ShellLink not available\n");
3030         goto err;
3031     }
3032
3033     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3034     if (FAILED( res ))
3035     {
3036         ERR("QueryInterface(IID_IPersistFile) failed\n");
3037         goto err;
3038     }
3039
3040     buffer = MSI_RecordGetString(row,2);
3041     target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3042
3043     /* may be needed because of a bug somehwere else */
3044     create_full_pathW(target_folder);
3045
3046     filename = msi_dup_record_field( row, 3 );
3047     reduce_to_longfilename(filename);
3048
3049     extension = strchrW(filename,'.');
3050     if (!extension || strcmpiW(extension,szlnk))
3051     {
3052         int len = strlenW(filename);
3053         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3054         memcpy(filename + len, szlnk, sizeof(szlnk));
3055     }
3056     target_file = build_directory_name(2, target_folder, filename);
3057     msi_free(target_folder);
3058     msi_free(filename);
3059
3060     buffer = MSI_RecordGetString(row,5);
3061     if (strchrW(buffer,'['))
3062     {
3063         LPWSTR deformated;
3064         deformat_string(package,buffer,&deformated);
3065         IShellLinkW_SetPath(sl,deformated);
3066         msi_free(deformated);
3067     }
3068     else
3069     {
3070         FIXME("poorly handled shortcut format, advertised shortcut\n");
3071         IShellLinkW_SetPath(sl,comp->FullKeypath);
3072     }
3073
3074     if (!MSI_RecordIsNull(row,6))
3075     {
3076         LPWSTR deformated;
3077         buffer = MSI_RecordGetString(row,6);
3078         deformat_string(package,buffer,&deformated);
3079         IShellLinkW_SetArguments(sl,deformated);
3080         msi_free(deformated);
3081     }
3082
3083     if (!MSI_RecordIsNull(row,7))
3084     {
3085         buffer = MSI_RecordGetString(row,7);
3086         IShellLinkW_SetDescription(sl,buffer);
3087     }
3088
3089     if (!MSI_RecordIsNull(row,8))
3090         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3091
3092     if (!MSI_RecordIsNull(row,9))
3093     {
3094         LPWSTR Path;
3095         INT index; 
3096
3097         buffer = MSI_RecordGetString(row,9);
3098
3099         Path = build_icon_path(package,buffer);
3100         index = MSI_RecordGetInteger(row,10);
3101
3102         /* no value means 0 */
3103         if (index == MSI_NULL_INTEGER)
3104             index = 0;
3105
3106         IShellLinkW_SetIconLocation(sl,Path,index);
3107         msi_free(Path);
3108     }
3109
3110     if (!MSI_RecordIsNull(row,11))
3111         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3112
3113     if (!MSI_RecordIsNull(row,12))
3114     {
3115         LPWSTR Path;
3116         buffer = MSI_RecordGetString(row,12);
3117         Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3118         if (Path)
3119             IShellLinkW_SetWorkingDirectory(sl,Path);
3120         msi_free(Path);
3121     }
3122
3123     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3124     IPersistFile_Save(pf,target_file,FALSE);
3125
3126     msi_free(target_file);    
3127
3128 err:
3129     if (pf)
3130         IPersistFile_Release( pf );
3131     if (sl)
3132         IShellLinkW_Release( sl );
3133
3134     return ERROR_SUCCESS;
3135 }
3136
3137 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3138 {
3139     UINT rc;
3140     HRESULT res;
3141     MSIQUERY * view;
3142     static const WCHAR Query[] =
3143         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3144          '`','S','h','o','r','t','c','u','t','`',0};
3145
3146     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3147     if (rc != ERROR_SUCCESS)
3148         return ERROR_SUCCESS;
3149
3150     res = CoInitialize( NULL );
3151     if (FAILED (res))
3152     {
3153         ERR("CoInitialize failed\n");
3154         return ERROR_FUNCTION_FAILED;
3155     }
3156
3157     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3158     msiobj_release(&view->hdr);
3159
3160     CoUninitialize();
3161
3162     return rc;
3163 }
3164
3165 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3166 {
3167     MSIPACKAGE* package = (MSIPACKAGE*)param;
3168     HANDLE the_file;
3169     LPWSTR FilePath;
3170     LPCWSTR FileName;
3171     CHAR buffer[1024];
3172     DWORD sz;
3173     UINT rc;
3174     MSIRECORD *uirow;
3175
3176     FileName = MSI_RecordGetString(row,1);
3177     if (!FileName)
3178     {
3179         ERR("Unable to get FileName\n");
3180         return ERROR_SUCCESS;
3181     }
3182
3183     FilePath = build_icon_path(package,FileName);
3184
3185     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3186
3187     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3188                         FILE_ATTRIBUTE_NORMAL, NULL);
3189
3190     if (the_file == INVALID_HANDLE_VALUE)
3191     {
3192         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3193         msi_free(FilePath);
3194         return ERROR_SUCCESS;
3195     }
3196
3197     do 
3198     {
3199         DWORD write;
3200         sz = 1024;
3201         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3202         if (rc != ERROR_SUCCESS)
3203         {
3204             ERR("Failed to get stream\n");
3205             CloseHandle(the_file);  
3206             DeleteFileW(FilePath);
3207             break;
3208         }
3209         WriteFile(the_file,buffer,sz,&write,NULL);
3210     } while (sz == 1024);
3211
3212     msi_free(FilePath);
3213
3214     CloseHandle(the_file);
3215
3216     uirow = MSI_CreateRecord(1);
3217     MSI_RecordSetStringW(uirow,1,FileName);
3218     ui_actiondata(package,szPublishProduct,uirow);
3219     msiobj_release( &uirow->hdr );
3220
3221     return ERROR_SUCCESS;
3222 }
3223
3224 /*
3225  * 99% of the work done here is only done for 
3226  * advertised installs. However this is where the
3227  * Icon table is processed and written out
3228  * so that is what I am going to do here.
3229  */
3230 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3231 {
3232     UINT rc;
3233     MSIQUERY * view;
3234     static const WCHAR Query[]=
3235         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3236          '`','I','c','o','n','`',0};
3237     /* for registry stuff */
3238     HKEY hkey=0;
3239     HKEY hukey=0;
3240     static const WCHAR szProductLanguage[] =
3241         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3242     static const WCHAR szARPProductIcon[] =
3243         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3244     static const WCHAR szProductVersion[] =
3245         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3246     DWORD langid;
3247     LPWSTR buffer;
3248     DWORD size;
3249     MSIHANDLE hDb, hSumInfo;
3250
3251     /* write out icon files */
3252
3253     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3254     if (rc == ERROR_SUCCESS)
3255     {
3256         MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3257         msiobj_release(&view->hdr);
3258     }
3259
3260     /* ok there is a lot more done here but i need to figure out what */
3261
3262     rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3263     if (rc != ERROR_SUCCESS)
3264         goto end;
3265
3266     rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3267     if (rc != ERROR_SUCCESS)
3268         goto end;
3269
3270
3271     buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3272     msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3273     msi_free(buffer);
3274
3275     langid = msi_get_property_int( package, szProductLanguage, 0 );
3276     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3277
3278     buffer = msi_dup_property( package, szARPProductIcon );
3279     if (buffer)
3280     {
3281         LPWSTR path = build_icon_path(package,buffer);
3282         msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3283         msi_free( path );
3284     }
3285     msi_free(buffer);
3286
3287     buffer = msi_dup_property( package, szProductVersion );
3288     if (buffer)
3289     {
3290         DWORD verdword = msi_version_str_to_dword(buffer);
3291         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3292     }
3293     msi_free(buffer);
3294     
3295     /* FIXME: Need to write more keys to the user registry */
3296   
3297     hDb= alloc_msihandle( &package->db->hdr );
3298     if (!hDb) {
3299         rc = ERROR_NOT_ENOUGH_MEMORY;
3300         goto end;
3301     }
3302     rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo); 
3303     MsiCloseHandle(hDb);
3304     if (rc == ERROR_SUCCESS)
3305     {
3306         WCHAR guidbuffer[0x200];
3307         size = 0x200;
3308         rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3309                                         guidbuffer, &size);
3310         if (rc == ERROR_SUCCESS)
3311         {
3312             WCHAR squashed[GUID_SIZE];
3313             /* for now we only care about the first guid */
3314             LPWSTR ptr = strchrW(guidbuffer,';');
3315             if (ptr) *ptr = 0;
3316             squash_guid(guidbuffer,squashed);
3317             msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3318         }
3319         else
3320         {
3321             ERR("Unable to query Revision_Number...\n");
3322             rc = ERROR_SUCCESS;
3323         }
3324         MsiCloseHandle(hSumInfo);
3325     }
3326     else
3327     {
3328         ERR("Unable to open Summary Information\n");
3329         rc = ERROR_SUCCESS;
3330     }
3331
3332 end:
3333
3334     RegCloseKey(hkey);
3335     RegCloseKey(hukey);
3336
3337     return rc;
3338 }
3339
3340 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3341 {
3342     MSIPACKAGE *package = (MSIPACKAGE*)param;
3343     LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3344     LPWSTR deformated_section, deformated_key, deformated_value;
3345     LPWSTR folder, fullname = NULL;
3346     MSIRECORD * uirow;
3347     INT action;
3348     MSICOMPONENT *comp;
3349     static const WCHAR szWindowsFolder[] =
3350           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3351
3352     component = MSI_RecordGetString(row, 8);
3353     comp = get_loaded_component(package,component);
3354
3355     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3356     {
3357         TRACE("Skipping ini file due to disabled component %s\n",
3358                         debugstr_w(component));
3359
3360         comp->Action = comp->Installed;
3361
3362         return ERROR_SUCCESS;
3363     }
3364
3365     comp->Action = INSTALLSTATE_LOCAL;
3366
3367     identifier = MSI_RecordGetString(row,1); 
3368     filename = MSI_RecordGetString(row,2);
3369     dirproperty = MSI_RecordGetString(row,3);
3370     section = MSI_RecordGetString(row,4);
3371     key = MSI_RecordGetString(row,5);
3372     value = MSI_RecordGetString(row,6);
3373     action = MSI_RecordGetInteger(row,7);
3374
3375     deformat_string(package,section,&deformated_section);
3376     deformat_string(package,key,&deformated_key);
3377     deformat_string(package,value,&deformated_value);
3378
3379     if (dirproperty)
3380     {
3381         folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3382         if (!folder)
3383             folder = msi_dup_property( package, dirproperty );
3384     }
3385     else
3386         folder = msi_dup_property( package, szWindowsFolder );
3387
3388     if (!folder)
3389     {
3390         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3391         goto cleanup;
3392     }
3393
3394     fullname = build_directory_name(2, folder, filename);
3395
3396     if (action == 0)
3397     {
3398         TRACE("Adding value %s to section %s in %s\n",
3399                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3400                 debugstr_w(fullname));
3401         WritePrivateProfileStringW(deformated_section, deformated_key,
3402                                    deformated_value, fullname);
3403     }
3404     else if (action == 1)
3405     {
3406         WCHAR returned[10];
3407         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3408                                  returned, 10, fullname);
3409         if (returned[0] == 0)
3410         {
3411             TRACE("Adding value %s to section %s in %s\n",
3412                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3413                     debugstr_w(fullname));
3414
3415             WritePrivateProfileStringW(deformated_section, deformated_key,
3416                                        deformated_value, fullname);
3417         }
3418     }
3419     else if (action == 3)
3420         FIXME("Append to existing section not yet implemented\n");
3421
3422     uirow = MSI_CreateRecord(4);
3423     MSI_RecordSetStringW(uirow,1,identifier);
3424     MSI_RecordSetStringW(uirow,2,deformated_section);
3425     MSI_RecordSetStringW(uirow,3,deformated_key);
3426     MSI_RecordSetStringW(uirow,4,deformated_value);
3427     ui_actiondata(package,szWriteIniValues,uirow);
3428     msiobj_release( &uirow->hdr );
3429 cleanup:
3430     msi_free(fullname);
3431     msi_free(folder);
3432     msi_free(deformated_key);
3433     msi_free(deformated_value);
3434     msi_free(deformated_section);
3435     return ERROR_SUCCESS;
3436 }
3437
3438 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3439 {
3440     UINT rc;
3441     MSIQUERY * view;
3442     static const WCHAR ExecSeqQuery[] = 
3443         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3444          '`','I','n','i','F','i','l','e','`',0};
3445
3446     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3447     if (rc != ERROR_SUCCESS)
3448     {
3449         TRACE("no IniFile table\n");
3450         return ERROR_SUCCESS;
3451     }
3452
3453     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3454     msiobj_release(&view->hdr);
3455     return rc;
3456 }
3457
3458 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3459 {
3460     MSIPACKAGE *package = (MSIPACKAGE*)param;
3461     LPCWSTR filename;
3462     LPWSTR FullName;
3463     MSIFILE *file;
3464     DWORD len;
3465     static const WCHAR ExeStr[] =
3466         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3467     static const WCHAR close[] =  {'\"',0};
3468     STARTUPINFOW si;
3469     PROCESS_INFORMATION info;
3470     BOOL brc;
3471     MSIRECORD *uirow;
3472     LPWSTR uipath, p;
3473
3474     memset(&si,0,sizeof(STARTUPINFOW));
3475
3476     filename = MSI_RecordGetString(row,1);
3477     file = get_loaded_file( package, filename );
3478
3479     if (!file)
3480     {
3481         ERR("Unable to find file id %s\n",debugstr_w(filename));
3482         return ERROR_SUCCESS;
3483     }
3484
3485     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3486
3487     FullName = msi_alloc(len*sizeof(WCHAR));
3488     strcpyW(FullName,ExeStr);
3489     strcatW( FullName, file->TargetPath );
3490     strcatW(FullName,close);
3491
3492     TRACE("Registering %s\n",debugstr_w(FullName));
3493     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3494                     &si, &info);
3495
3496     if (brc)
3497         msi_dialog_check_messages(info.hProcess);
3498
3499     msi_free(FullName);
3500
3501     /* the UI chunk */
3502     uirow = MSI_CreateRecord( 2 );
3503     uipath = strdupW( file->TargetPath );
3504     p = strrchrW(uipath,'\\');
3505     if (p)
3506         p[0]=0;
3507     MSI_RecordSetStringW( uirow, 1, &p[1] );
3508     MSI_RecordSetStringW( uirow, 2, uipath);
3509     ui_actiondata( package, szSelfRegModules, uirow);
3510     msiobj_release( &uirow->hdr );
3511     msi_free( uipath );
3512     /* FIXME: call ui_progress? */
3513
3514     return ERROR_SUCCESS;
3515 }
3516
3517 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3518 {
3519     UINT rc;
3520     MSIQUERY * view;
3521     static const WCHAR ExecSeqQuery[] = 
3522         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3523          '`','S','e','l','f','R','e','g','`',0};
3524
3525     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3526     if (rc != ERROR_SUCCESS)
3527     {
3528         TRACE("no SelfReg table\n");
3529         return ERROR_SUCCESS;
3530     }
3531
3532     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3533     msiobj_release(&view->hdr);
3534
3535     return ERROR_SUCCESS;
3536 }
3537
3538 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3539 {
3540     MSIFEATURE *feature;
3541     UINT rc;
3542     HKEY hkey=0;
3543     HKEY hukey=0;
3544     
3545     rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3546     if (rc != ERROR_SUCCESS)
3547         goto end;
3548
3549     rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3550     if (rc != ERROR_SUCCESS)
3551         goto end;
3552
3553     /* here the guids are base 85 encoded */
3554     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3555     {
3556         ComponentList *cl;
3557         LPWSTR data = NULL;
3558         GUID clsid;
3559         INT size;
3560         BOOL absent = FALSE;
3561         MSIRECORD *uirow;
3562
3563         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3564             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3565             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3566             absent = TRUE;
3567
3568         size = 1;
3569         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3570         {
3571             size += 21;
3572         }
3573         if (feature->Feature_Parent)
3574             size += strlenW( feature->Feature_Parent )+2;
3575
3576         data = msi_alloc(size * sizeof(WCHAR));
3577
3578         data[0] = 0;
3579         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3580         {
3581             MSICOMPONENT* component = cl->component;
3582             WCHAR buf[21];
3583
3584             buf[0] = 0;
3585             if (component->ComponentId)
3586             {
3587                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3588                 CLSIDFromString(component->ComponentId, &clsid);
3589                 encode_base85_guid(&clsid,buf);
3590                 TRACE("to %s\n",debugstr_w(buf));
3591                 strcatW(data,buf);
3592             }
3593         }
3594         if (feature->Feature_Parent)
3595         {
3596             static const WCHAR sep[] = {'\2',0};
3597             strcatW(data,sep);
3598             strcatW(data,feature->Feature_Parent);
3599         }
3600
3601         msi_reg_set_val_str( hkey, feature->Feature, data );
3602         msi_free(data);
3603
3604         size = 0;
3605         if (feature->Feature_Parent)
3606             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3607         if (!absent)
3608         {
3609             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3610                        (LPBYTE)feature->Feature_Parent,size);
3611         }
3612         else
3613         {
3614             size += 2*sizeof(WCHAR);
3615             data = msi_alloc(size);
3616             data[0] = 0x6;
3617             data[1] = 0;
3618             if (feature->Feature_Parent)
3619                 strcpyW( &data[1], feature->Feature_Parent );
3620             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3621                        (LPBYTE)data,size);
3622             msi_free(data);
3623         }
3624
3625         /* the UI chunk */
3626         uirow = MSI_CreateRecord( 1 );
3627         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3628         ui_actiondata( package, szPublishFeatures, uirow);
3629         msiobj_release( &uirow->hdr );
3630         /* FIXME: call ui_progress? */
3631     }
3632
3633 end:
3634     RegCloseKey(hkey);
3635     RegCloseKey(hukey);
3636     return rc;
3637 }
3638
3639 static UINT msi_get_local_package_name( LPWSTR path )
3640 {
3641     static const WCHAR szInstaller[] = {
3642         '\\','I','n','s','t','a','l','l','e','r','\\',0};
3643     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3644     DWORD time, len, i;
3645     HANDLE handle;
3646
3647     time = GetTickCount();
3648     GetWindowsDirectoryW( path, MAX_PATH );
3649     lstrcatW( path, szInstaller );
3650     CreateDirectoryW( path, NULL );
3651
3652     len = lstrlenW(path);
3653     for (i=0; i<0x10000; i++)
3654     {
3655         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3656         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3657                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3658         if (handle != INVALID_HANDLE_VALUE)
3659         {
3660             CloseHandle(handle);
3661             break;
3662         }
3663         if (GetLastError() != ERROR_FILE_EXISTS &&
3664             GetLastError() != ERROR_SHARING_VIOLATION)
3665             return ERROR_FUNCTION_FAILED;
3666     }
3667
3668     return ERROR_SUCCESS;
3669 }
3670
3671 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3672 {
3673     static const WCHAR szOriginalDatabase[] =
3674         {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3675     WCHAR packagefile[MAX_PATH];
3676     LPWSTR msiFilePath;
3677     UINT r;
3678
3679     r = msi_get_local_package_name( packagefile );
3680     if (r != ERROR_SUCCESS)
3681         return r;
3682
3683     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3684
3685     msiFilePath = msi_dup_property( package, szOriginalDatabase );
3686     r = CopyFileW( msiFilePath, packagefile, FALSE);
3687
3688     if (!r)
3689     {
3690         ERR("Unable to copy package (%s -> %s) (error %d)\n",
3691             debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3692         msi_free( msiFilePath );
3693         return ERROR_FUNCTION_FAILED;
3694     }
3695     msi_free( msiFilePath );
3696
3697     /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3698     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3699     return ERROR_SUCCESS;
3700 }
3701
3702 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3703 {
3704     LPWSTR prop, val, key;
3705     static const LPCSTR propval[] = {
3706         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3707         "ARPCONTACT",             "Contact",
3708         "ARPCOMMENTS",            "Comments",
3709         "ProductName",            "DisplayName",
3710         "ProductVersion",         "DisplayVersion",
3711         "ARPHELPLINK",            "HelpLink",
3712         "ARPHELPTELEPHONE",       "HelpTelephone",
3713         "ARPINSTALLLOCATION",     "InstallLocation",
3714         "SourceDir",              "InstallSource",
3715         "Manufacturer",           "Publisher",
3716         "ARPREADME",              "Readme",
3717         "ARPSIZE",                "Size",
3718         "ARPURLINFOABOUT",        "URLInfoAbout",
3719         "ARPURLUPDATEINFO",       "URLUpdateInfo",
3720         NULL,
3721     };
3722     const LPCSTR *p = propval;
3723
3724     while( *p )
3725     {
3726         prop = strdupAtoW( *p++ );
3727         key = strdupAtoW( *p++ );
3728         val = msi_dup_property( package, prop );
3729         msi_reg_set_val_str( hkey, key, val );
3730         msi_free(val);
3731         msi_free(key);
3732         msi_free(prop);
3733     }
3734     return ERROR_SUCCESS;
3735 }
3736
3737 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3738 {
3739     HKEY hkey=0;
3740     LPWSTR buffer = NULL;
3741     UINT rc;
3742     DWORD size, langid;
3743     static const WCHAR szWindowsInstaller[] = 
3744         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3745     static const WCHAR szUpgradeCode[] = 
3746         {'U','p','g','r','a','d','e','C','o','d','e',0};
3747     static const WCHAR modpath_fmt[] = 
3748         {'M','s','i','E','x','e','c','.','e','x','e',' ',
3749          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3750     static const WCHAR szModifyPath[] = 
3751         {'M','o','d','i','f','y','P','a','t','h',0};
3752     static const WCHAR szUninstallString[] = 
3753         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3754     static const WCHAR szEstimatedSize[] = 
3755         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3756     static const WCHAR szProductLanguage[] =
3757         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3758     static const WCHAR szProductVersion[] =
3759         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3760
3761     SYSTEMTIME systime;
3762     static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3763     LPWSTR upgrade_code;
3764     WCHAR szDate[9]; 
3765
3766     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3767     if (rc != ERROR_SUCCESS)
3768         return rc;
3769
3770     /* dump all the info i can grab */
3771     /* FIXME: Flesh out more information */
3772
3773     msi_write_uninstall_property_vals( package, hkey );
3774
3775     msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3776     
3777     msi_make_package_local( package, hkey );
3778
3779     /* do ModifyPath and UninstallString */
3780     size = deformat_string(package,modpath_fmt,&buffer);
3781     RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3782     RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3783     msi_free(buffer);
3784
3785     /* FIXME: Write real Estimated Size when we have it */
3786     msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3787    
3788     GetLocalTime(&systime);
3789     sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3790     msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3791    
3792     langid = msi_get_property_int( package, szProductLanguage, 0 );
3793     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3794
3795     buffer = msi_dup_property( package, szProductVersion );
3796     if (buffer)
3797     {
3798         DWORD verdword = msi_version_str_to_dword(buffer);
3799
3800         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3801         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3802         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3803     }
3804     msi_free(buffer);
3805     
3806     /* Handle Upgrade Codes */
3807     upgrade_code = msi_dup_property( package, szUpgradeCode );
3808     if (upgrade_code)
3809     {
3810         HKEY hkey2;
3811         WCHAR squashed[33];
3812         MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3813         squash_guid(package->ProductCode,squashed);
3814         msi_reg_set_val_str( hkey2, squashed, NULL );
3815         RegCloseKey(hkey2);
3816         MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3817         squash_guid(package->ProductCode,squashed);
3818         msi_reg_set_val_str( hkey2, squashed, NULL );
3819         RegCloseKey(hkey2);
3820
3821         msi_free(upgrade_code);
3822     }
3823     
3824     RegCloseKey(hkey);
3825
3826     /* FIXME: call ui_actiondata */
3827
3828     return ERROR_SUCCESS;
3829 }
3830
3831 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3832 {
3833     return execute_script(package,INSTALL_SCRIPT);
3834 }
3835
3836 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
3837 {
3838     UINT rc;
3839
3840     /* turn off scheduling */
3841     package->script->CurrentlyScripting= FALSE;
3842
3843     /* first do the same as an InstallExecute */
3844     rc = ACTION_InstallExecute(package);
3845     if (rc != ERROR_SUCCESS)
3846         return rc;
3847
3848     /* then handle Commit Actions */
3849     rc = execute_script(package,COMMIT_SCRIPT);
3850
3851     return rc;
3852 }
3853
3854 UINT ACTION_ForceReboot(MSIPACKAGE *package)
3855 {
3856     static const WCHAR RunOnce[] = {
3857     'S','o','f','t','w','a','r','e','\\',
3858     'M','i','c','r','o','s','o','f','t','\\',
3859     'W','i','n','d','o','w','s','\\',
3860     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3861     'R','u','n','O','n','c','e',0};
3862     static const WCHAR InstallRunOnce[] = {
3863     'S','o','f','t','w','a','r','e','\\',
3864     'M','i','c','r','o','s','o','f','t','\\',
3865     'W','i','n','d','o','w','s','\\',
3866     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3867     'I','n','s','t','a','l','l','e','r','\\',
3868     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3869
3870     static const WCHAR msiexec_fmt[] = {
3871     '%','s',
3872     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3873     '\"','%','s','\"',0};
3874     static const WCHAR install_fmt[] = {
3875     '/','I',' ','\"','%','s','\"',' ',
3876     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3877     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3878     WCHAR buffer[256], sysdir[MAX_PATH];
3879     HKEY hkey;
3880     WCHAR squished_pc[100];
3881
3882     squash_guid(package->ProductCode,squished_pc);
3883
3884     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
3885     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
3886     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
3887      squished_pc);
3888
3889     msi_reg_set_val_str( hkey, squished_pc, buffer );
3890     RegCloseKey(hkey);
3891
3892     TRACE("Reboot command %s\n",debugstr_w(buffer));
3893
3894     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
3895     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
3896
3897     msi_reg_set_val_str( hkey, squished_pc, buffer );
3898     RegCloseKey(hkey);
3899
3900     return ERROR_INSTALL_SUSPEND;
3901 }
3902
3903 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
3904 {
3905     LPWSTR p, source;
3906     DWORD len;
3907
3908     p = strrchrW( package->PackagePath, '\\' );
3909     if (!p)
3910         return ERROR_SUCCESS;
3911
3912     len = p - package->PackagePath + 2;
3913     source = msi_alloc( len * sizeof(WCHAR) );
3914     lstrcpynW( source, package->PackagePath, len );
3915
3916     MSI_SetPropertyW( package, cszSourceDir, source );
3917     MSI_SetPropertyW( package, cszSOURCEDIR, source );
3918
3919     msi_free( source );
3920
3921     return ERROR_SUCCESS;
3922 }
3923
3924 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
3925 {
3926     DWORD attrib;
3927     UINT rc;
3928
3929     /*
3930      * We are currently doing what should be done here in the top level Install
3931      * however for Administrative and uninstalls this step will be needed
3932      */
3933     if (!package->PackagePath)
3934         return ERROR_SUCCESS;
3935
3936     msi_set_sourcedir_props(package);
3937
3938     attrib = GetFileAttributesW(package->PackagePath);
3939     if (attrib == INVALID_FILE_ATTRIBUTES)
3940     {
3941         LPWSTR prompt;
3942         LPWSTR msg;
3943         DWORD size = 0;
3944
3945         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
3946                 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3947                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
3948         if (rc == ERROR_MORE_DATA)
3949         {
3950             prompt = msi_alloc(size * sizeof(WCHAR));
3951             MsiSourceListGetInfoW(package->ProductCode, NULL, 
3952                     MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3953                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
3954         }
3955         else
3956             prompt = strdupW(package->PackagePath);
3957
3958         msg = generate_error_string(package,1302,1,prompt);
3959         while(attrib == INVALID_FILE_ATTRIBUTES)
3960         {
3961             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
3962             if (rc == IDCANCEL)
3963             {
3964                 rc = ERROR_INSTALL_USEREXIT;
3965                 break;
3966             }
3967             attrib = GetFileAttributesW(package->PackagePath);
3968         }
3969         msi_free(prompt);
3970         rc = ERROR_SUCCESS;
3971     }
3972     else
3973         return ERROR_SUCCESS;
3974
3975     return rc;
3976 }
3977
3978 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
3979 {
3980     HKEY hkey=0;
3981     LPWSTR buffer;
3982     LPWSTR productid;
3983     UINT rc,i;
3984
3985     static const WCHAR szPropKeys[][80] = 
3986     {
3987         {'P','r','o','d','u','c','t','I','D',0},
3988         {'U','S','E','R','N','A','M','E',0},
3989         {'C','O','M','P','A','N','Y','N','A','M','E',0},
3990         {0},
3991     };
3992
3993     static const WCHAR szRegKeys[][80] = 
3994     {
3995         {'P','r','o','d','u','c','t','I','D',0},
3996         {'R','e','g','O','w','n','e','r',0},
3997         {'R','e','g','C','o','m','p','a','n','y',0},
3998         {0},
3999     };
4000
4001     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4002     if (!productid)
4003         return ERROR_SUCCESS;
4004
4005     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4006     if (rc != ERROR_SUCCESS)
4007         goto end;
4008
4009     for( i = 0; szPropKeys[i][0]; i++ )
4010     {
4011         buffer = msi_dup_property( package, szPropKeys[i] );
4012         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4013         msi_free( buffer );
4014     }
4015
4016 end:
4017     msi_free(productid);
4018     RegCloseKey(hkey);
4019
4020     /* FIXME: call ui_actiondata */
4021
4022     return ERROR_SUCCESS;
4023 }
4024
4025
4026 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4027 {
4028     UINT rc;
4029
4030     package->script->InWhatSequence |= SEQUENCE_EXEC;
4031     rc = ACTION_ProcessExecSequence(package,FALSE);
4032     return rc;
4033 }
4034
4035
4036 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4037 {
4038     MSIPACKAGE *package = (MSIPACKAGE*)param;
4039     LPCWSTR compgroupid=NULL;
4040     LPCWSTR feature=NULL;
4041     LPCWSTR text = NULL;
4042     LPCWSTR qualifier = NULL;
4043     LPCWSTR component = NULL;
4044     LPWSTR advertise = NULL;
4045     LPWSTR output = NULL;
4046     HKEY hkey;
4047     UINT rc = ERROR_SUCCESS;
4048     MSICOMPONENT *comp;
4049     DWORD sz = 0;
4050     MSIRECORD *uirow;
4051
4052     component = MSI_RecordGetString(rec,3);
4053     comp = get_loaded_component(package,component);
4054
4055     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4056        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4057        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4058     {
4059         TRACE("Skipping: Component %s not scheduled for install\n",
4060                         debugstr_w(component));
4061
4062         return ERROR_SUCCESS;
4063     }
4064
4065     compgroupid = MSI_RecordGetString(rec,1);
4066     qualifier = MSI_RecordGetString(rec,2);
4067
4068     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4069     if (rc != ERROR_SUCCESS)
4070         goto end;
4071     
4072     text = MSI_RecordGetString(rec,4);
4073     feature = MSI_RecordGetString(rec,5);
4074   
4075     advertise = create_component_advertise_string(package, comp, feature);
4076
4077     sz = strlenW(advertise);
4078
4079     if (text)
4080         sz += lstrlenW(text);
4081
4082     sz+=3;
4083     sz *= sizeof(WCHAR);
4084            
4085     output = msi_alloc_zero(sz);
4086     strcpyW(output,advertise);
4087     msi_free(advertise);
4088
4089     if (text)
4090         strcatW(output,text);
4091
4092     msi_reg_set_val_multi_str( hkey, qualifier, output );
4093     
4094 end:
4095     RegCloseKey(hkey);
4096     msi_free(output);
4097
4098     /* the UI chunk */
4099     uirow = MSI_CreateRecord( 2 );
4100     MSI_RecordSetStringW( uirow, 1, compgroupid );
4101     MSI_RecordSetStringW( uirow, 2, qualifier);
4102     ui_actiondata( package, szPublishComponents, uirow);
4103     msiobj_release( &uirow->hdr );
4104     /* FIXME: call ui_progress? */
4105
4106     return rc;
4107 }
4108
4109 /*
4110  * At present I am ignorning the advertised components part of this and only
4111  * focusing on the qualified component sets
4112  */
4113 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4114 {
4115     UINT rc;
4116     MSIQUERY * view;
4117     static const WCHAR ExecSeqQuery[] =
4118         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4119          '`','P','u','b','l','i','s','h',
4120          'C','o','m','p','o','n','e','n','t','`',0};
4121     
4122     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4123     if (rc != ERROR_SUCCESS)
4124         return ERROR_SUCCESS;
4125
4126     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4127     msiobj_release(&view->hdr);
4128
4129     return rc;
4130 }
4131
4132 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4133 {
4134     MSIPACKAGE *package = (MSIPACKAGE*)param;
4135     MSIRECORD *row;
4136     MSIFILE *file;
4137     SC_HANDLE hscm, service = NULL;
4138     LPCWSTR name, disp, comp, depends, pass;
4139     LPCWSTR load_order, serv_name, key;
4140     DWORD serv_type, start_type;
4141     DWORD err_control;
4142
4143     static const WCHAR query[] =
4144         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4145          '`','C','o','m','p','o','n','e','n','t','`',' ',
4146          'W','H','E','R','E',' ',
4147          '`','C','o','m','p','o','n','e','n','t','`',' ',
4148          '=','\'','%','s','\'',0};
4149
4150     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4151     if (!hscm)
4152     {
4153         ERR("Failed to open the SC Manager!\n");
4154         goto done;
4155     }
4156
4157     start_type = MSI_RecordGetInteger(rec, 5);
4158     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4159         goto done;
4160
4161     depends = MSI_RecordGetString(rec, 8);
4162     if (depends && *depends)
4163         FIXME("Dependency list unhandled!\n");
4164
4165     name = MSI_RecordGetString(rec, 2);
4166     disp = MSI_RecordGetString(rec, 3);
4167     serv_type = MSI_RecordGetInteger(rec, 4);
4168     err_control = MSI_RecordGetInteger(rec, 6);
4169     load_order = MSI_RecordGetString(rec, 7);
4170     serv_name = MSI_RecordGetString(rec, 9);
4171     pass = MSI_RecordGetString(rec, 10);
4172     comp = MSI_RecordGetString(rec, 12);
4173
4174     /* fetch the service path */
4175     row = MSI_QueryGetRecord(package->db, query, comp);
4176     if (!row)
4177     {
4178         ERR("Control query failed!\n");
4179         goto done;
4180     }
4181
4182     key = MSI_RecordGetString(row, 6);
4183     msiobj_release(&row->hdr);
4184
4185     file = get_loaded_file(package, key);
4186     if (!file)
4187     {
4188         ERR("Failed to load the service file\n");
4189         goto done;
4190     }
4191
4192     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4193                              start_type, err_control, file->TargetPath,
4194                              load_order, NULL, NULL, serv_name, pass);
4195     if (!service)
4196     {
4197         if (GetLastError() != ERROR_SERVICE_EXISTS)
4198             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4199     }
4200
4201 done:
4202     CloseServiceHandle(service);
4203     CloseServiceHandle(hscm);
4204
4205     return ERROR_SUCCESS;
4206 }
4207
4208 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4209 {
4210     UINT rc;
4211     MSIQUERY * view;
4212     static const WCHAR ExecSeqQuery[] =
4213         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4214          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4215     
4216     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4217     if (rc != ERROR_SUCCESS)
4218         return ERROR_SUCCESS;
4219
4220     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4221     msiobj_release(&view->hdr);
4222
4223     return rc;
4224 }
4225
4226 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4227 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4228 {
4229     LPCWSTR *vector;
4230     LPWSTR p, q;
4231     DWORD sep_len;
4232
4233     static const WCHAR separator[] = {'[','~',']',0};
4234
4235     *numargs = 0;
4236     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4237
4238     if (!args)
4239         return NULL;
4240
4241     vector = msi_alloc(sizeof(LPWSTR));
4242     if (!vector)
4243         return NULL;
4244
4245     p = args;
4246     do
4247     {
4248         (*numargs)++;
4249         vector[*numargs - 1] = p;
4250
4251         if ((q = strstrW(p, separator)))
4252         {
4253             *q = '\0';
4254
4255             vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4256             if (!vector)
4257                 return NULL;
4258
4259             p = q + sep_len;
4260         }
4261     } while (q);
4262
4263     return vector;
4264 }
4265
4266 static MSICOMPONENT *msi_find_component( MSIPACKAGE *package, LPCWSTR component )
4267 {
4268     MSICOMPONENT *comp;
4269
4270     LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
4271     {
4272         if (!lstrcmpW(comp->Component, component))
4273             return comp;
4274     }
4275
4276     return NULL;
4277 }
4278
4279 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4280 {
4281     MSIPACKAGE *package = (MSIPACKAGE *)param;
4282     MSICOMPONENT *comp;
4283     SC_HANDLE scm, service = NULL;
4284     LPCWSTR name, *vector = NULL;
4285     LPWSTR args;
4286     DWORD event, numargs;
4287     UINT r = ERROR_FUNCTION_FAILED;
4288
4289     comp = msi_find_component(package, MSI_RecordGetString(rec, 6));
4290     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4291         return ERROR_SUCCESS;
4292
4293     name = MSI_RecordGetString(rec, 2);
4294     event = MSI_RecordGetInteger(rec, 3);
4295     args = strdupW(MSI_RecordGetString(rec, 4));
4296
4297     if (!(event & msidbServiceControlEventStart))
4298         return ERROR_SUCCESS;
4299
4300     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4301     if (!scm)
4302     {
4303         ERR("Failed to open the service control manager\n");
4304         goto done;
4305     }
4306
4307     service = OpenServiceW(scm, name, SERVICE_START);
4308     if (!service)
4309     {
4310         ERR("Failed to open service %s\n", debugstr_w(name));
4311         goto done;
4312     }
4313
4314     vector = msi_service_args_to_vector(name, args, &numargs);
4315
4316     if (!StartServiceW(service, numargs, vector))
4317     {
4318         ERR("Failed to start service %s\n", debugstr_w(name));
4319         goto done;
4320     }
4321
4322     r = ERROR_SUCCESS;
4323
4324 done:
4325     CloseServiceHandle(service);
4326     CloseServiceHandle(scm);
4327
4328     msi_free(args);
4329     msi_free(vector);
4330     return r;
4331 }
4332
4333 static UINT ACTION_StartServices( MSIPACKAGE *package )
4334 {
4335     UINT rc;
4336     MSIQUERY *view;
4337
4338     static const WCHAR query[] = {
4339         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4340         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4341
4342     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4343     if (rc != ERROR_SUCCESS)
4344         return ERROR_SUCCESS;
4345
4346     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4347     msiobj_release(&view->hdr);
4348
4349     return rc;
4350 }
4351
4352 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4353 {
4354     MSIFILE *file;
4355
4356     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4357     {
4358         if (!lstrcmpW(file->File, filename))
4359             return file;
4360     }
4361
4362     return NULL;
4363 }
4364
4365 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4366 {
4367     MSIPACKAGE *package = (MSIPACKAGE*)param;
4368     LPWSTR driver, driver_path, ptr;
4369     WCHAR outpath[MAX_PATH];
4370     MSIFILE *driver_file, *setup_file;
4371     LPCWSTR desc;
4372     DWORD len, usage;
4373     UINT r = ERROR_SUCCESS;
4374
4375     static const WCHAR driver_fmt[] = {
4376         'D','r','i','v','e','r','=','%','s',0};
4377     static const WCHAR setup_fmt[] = {
4378         'S','e','t','u','p','=','%','s',0};
4379     static const WCHAR usage_fmt[] = {
4380         'F','i','l','e','U','s','a','g','e','=','1',0};
4381
4382     desc = MSI_RecordGetString(rec, 3);
4383
4384     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4385     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4386
4387     if (!driver_file || !setup_file)
4388     {
4389         ERR("ODBC Driver entry not found!\n");
4390         return ERROR_FUNCTION_FAILED;
4391     }
4392
4393     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4394           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4395           lstrlenW(usage_fmt) + 1;
4396     driver = msi_alloc(len * sizeof(WCHAR));
4397     if (!driver)
4398         return ERROR_OUTOFMEMORY;
4399
4400     ptr = driver;
4401     lstrcpyW(ptr, desc);
4402     ptr += lstrlenW(ptr) + 1;
4403
4404     sprintfW(ptr, driver_fmt, driver_file->FileName);
4405     ptr += lstrlenW(ptr) + 1;
4406
4407     sprintfW(ptr, setup_fmt, setup_file->FileName);
4408     ptr += lstrlenW(ptr) + 1;
4409
4410     lstrcpyW(ptr, usage_fmt);
4411     ptr += lstrlenW(ptr) + 1;
4412     *ptr = '\0';
4413
4414     driver_path = strdupW(driver_file->TargetPath);
4415     ptr = strrchrW(driver_path, '\\');
4416     if (ptr) *ptr = '\0';
4417
4418     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4419                              NULL, ODBC_INSTALL_COMPLETE, &usage))
4420     {
4421         ERR("Failed to install SQL driver!\n");
4422         r = ERROR_FUNCTION_FAILED;
4423     }
4424
4425     msi_free(driver);
4426     msi_free(driver_path);
4427
4428     return r;
4429 }
4430
4431 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4432 {
4433     MSIPACKAGE *package = (MSIPACKAGE*)param;
4434     LPWSTR translator, translator_path, ptr;
4435     WCHAR outpath[MAX_PATH];
4436     MSIFILE *translator_file, *setup_file;
4437     LPCWSTR desc;
4438     DWORD len, usage;
4439     UINT r = ERROR_SUCCESS;
4440
4441     static const WCHAR translator_fmt[] = {
4442         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4443     static const WCHAR setup_fmt[] = {
4444         'S','e','t','u','p','=','%','s',0};
4445
4446     desc = MSI_RecordGetString(rec, 3);
4447
4448     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4449     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4450
4451     if (!translator_file || !setup_file)
4452     {
4453         ERR("ODBC Translator entry not found!\n");
4454         return ERROR_FUNCTION_FAILED;
4455     }
4456
4457     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4458           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4459     translator = msi_alloc(len * sizeof(WCHAR));
4460     if (!translator)
4461         return ERROR_OUTOFMEMORY;
4462
4463     ptr = translator;
4464     lstrcpyW(ptr, desc);
4465     ptr += lstrlenW(ptr) + 1;
4466
4467     sprintfW(ptr, translator_fmt, translator_file->FileName);
4468     ptr += lstrlenW(ptr) + 1;
4469
4470     sprintfW(ptr, setup_fmt, setup_file->FileName);
4471     ptr += lstrlenW(ptr) + 1;
4472     *ptr = '\0';
4473
4474     translator_path = strdupW(translator_file->TargetPath);
4475     ptr = strrchrW(translator_path, '\\');
4476     if (ptr) *ptr = '\0';
4477
4478     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4479                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
4480     {
4481         ERR("Failed to install SQL translator!\n");
4482         r = ERROR_FUNCTION_FAILED;
4483     }
4484
4485     msi_free(translator);
4486     msi_free(translator_path);
4487
4488     return r;
4489 }
4490
4491 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4492 {
4493     LPWSTR attrs;
4494     LPCWSTR desc, driver;
4495     WORD request = ODBC_ADD_SYS_DSN;
4496     INT registration;
4497     DWORD len;
4498     UINT r = ERROR_SUCCESS;
4499
4500     static const WCHAR attrs_fmt[] = {
4501         'D','S','N','=','%','s',0 };
4502
4503     desc = MSI_RecordGetString(rec, 3);
4504     driver = MSI_RecordGetString(rec, 4);
4505     registration = MSI_RecordGetInteger(rec, 5);
4506
4507     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4508     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4509
4510     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4511     attrs = msi_alloc(len * sizeof(WCHAR));
4512     if (!attrs)
4513         return ERROR_OUTOFMEMORY;
4514
4515     sprintfW(attrs, attrs_fmt, desc);
4516     attrs[len - 1] = '\0';
4517
4518     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4519     {
4520         ERR("Failed to install SQL data source!\n");
4521         r = ERROR_FUNCTION_FAILED;
4522     }
4523
4524     msi_free(attrs);
4525
4526     return r;
4527 }
4528
4529 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4530 {
4531     UINT rc;
4532     MSIQUERY *view;
4533
4534     static const WCHAR driver_query[] = {
4535         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4536         'O','D','B','C','D','r','i','v','e','r',0 };
4537
4538     static const WCHAR translator_query[] = {
4539         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4540         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4541
4542     static const WCHAR source_query[] = {
4543         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4544         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4545
4546     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4547     if (rc != ERROR_SUCCESS)
4548         return ERROR_SUCCESS;
4549
4550     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4551     msiobj_release(&view->hdr);
4552
4553     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4554     if (rc != ERROR_SUCCESS)
4555         return ERROR_SUCCESS;
4556
4557     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4558     msiobj_release(&view->hdr);
4559
4560     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4561     if (rc != ERROR_SUCCESS)
4562         return ERROR_SUCCESS;
4563
4564     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4565     msiobj_release(&view->hdr);
4566
4567     return rc;
4568 }
4569
4570 #define ENV_ACT_SETALWAYS   0x1
4571 #define ENV_ACT_SETABSENT   0x2
4572 #define ENV_ACT_REMOVE      0x4
4573 #define ENV_ACT_REMOVEMATCH 0x8
4574
4575 #define ENV_MOD_MACHINE     0x20000000
4576 #define ENV_MOD_APPEND      0x40000000
4577 #define ENV_MOD_PREFIX      0x80000000
4578 #define ENV_MOD_MASK        0xC0000000
4579
4580 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4581
4582 static LONG env_set_flags( LPCWSTR *name, LPWSTR *value, DWORD *flags )
4583 {
4584     LPCWSTR cptr = *name;
4585     LPWSTR ptr = *value;
4586
4587     static const WCHAR prefix[] = {'[','~',']',0};
4588
4589     *flags = 0;
4590     while (*cptr && (*cptr == '=' || *cptr == '+' ||
4591            *cptr == '-' || *cptr == '!' || *cptr == '*'))
4592     {
4593         switch (*cptr)
4594         {
4595         case '=':
4596             *flags |= ENV_ACT_SETALWAYS;
4597             break;
4598         case '+':
4599             *flags |= ENV_ACT_SETABSENT;
4600             break;
4601         case '-':
4602             *flags |= ENV_ACT_REMOVE;
4603             break;
4604         case '!':
4605             *flags |= ENV_ACT_REMOVEMATCH;
4606             break;
4607         case '*':
4608             *flags |= ENV_MOD_MACHINE;
4609             break;
4610         default:
4611             ERR("Unknown Environment flag: %c\n", *cptr);
4612             return ERROR_FUNCTION_FAILED;
4613         }
4614
4615         cptr++;
4616         (*name)++;
4617     }
4618
4619     if (!*cptr)
4620     {
4621         ERR("Missing environment variable\n");
4622         return ERROR_FUNCTION_FAILED;
4623     }
4624
4625     if (!strncmpW(ptr, prefix, lstrlenW(prefix)))
4626     {
4627         *flags |= ENV_MOD_PREFIX;
4628         *value += lstrlenW(prefix);
4629     }
4630     else
4631     {
4632         ptr += lstrlenW(ptr) - lstrlenW(prefix) - 1;
4633         if (!lstrcmpW(ptr, prefix))
4634         {
4635             *flags |= ENV_MOD_APPEND;
4636             *ptr = '\0';
4637         }
4638     }
4639
4640     if (!*flags ||
4641         check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4642         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4643         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4644         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4645     {
4646         ERR("Invalid flags: %08x\n", *flags);
4647         return ERROR_FUNCTION_FAILED;
4648     }
4649
4650     return ERROR_SUCCESS;
4651 }
4652
4653 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4654 {
4655     MSIPACKAGE *package = param;
4656     LPCWSTR name, value, comp;
4657     LPWSTR data = NULL, newval = NULL;
4658     LPWSTR deformatted, ptr;
4659     DWORD flags, type, size;
4660     LONG res;
4661     HKEY env, root = HKEY_CURRENT_USER;
4662
4663     static const WCHAR environment[] =
4664         {'S','y','s','t','e','m','\\',
4665          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4666          'C','o','n','t','r','o','l','\\',
4667          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4668          'E','n','v','i','r','o','n','m','e','n','t',0};
4669     static const WCHAR semicolon[] = {';',0};
4670
4671     name = MSI_RecordGetString(rec, 2);
4672     value = MSI_RecordGetString(rec, 3);
4673     comp = MSI_RecordGetString(rec, 4);
4674
4675     deformat_string(package, value, &deformatted);
4676     if (!deformatted)
4677         return ERROR_OUTOFMEMORY;
4678
4679     res = env_set_flags(&name, &deformatted, &flags);
4680     if (res != ERROR_SUCCESS)
4681        goto done;
4682
4683     value = deformatted;
4684
4685     if (flags & ENV_MOD_MACHINE)
4686         root = HKEY_LOCAL_MACHINE;
4687
4688     res = RegOpenKeyExW(root, environment, 0, KEY_ALL_ACCESS, &env);
4689     if (res != ERROR_SUCCESS)
4690         goto done;
4691
4692     if (flags & ENV_ACT_REMOVE)
4693         FIXME("Not removing environment variable on uninstall!\n");
4694
4695     size = 0;
4696     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4697     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4698         (res == ERROR_SUCCESS && type != REG_SZ))
4699         goto done;
4700
4701     if (res != ERROR_FILE_NOT_FOUND)
4702     {
4703         if (flags & ENV_ACT_SETABSENT)
4704         {
4705             res = ERROR_SUCCESS;
4706             goto done;
4707         }
4708
4709         data = msi_alloc(size);
4710         if (!data)
4711         {
4712             RegCloseKey(env);
4713             return ERROR_OUTOFMEMORY;
4714         }
4715
4716         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4717         if (res != ERROR_SUCCESS)
4718             goto done;
4719
4720         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4721         {
4722             res = RegDeleteKeyW(env, name);
4723             goto done;
4724         }
4725
4726         size =  (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4727         newval =  msi_alloc(size);
4728         ptr = newval;
4729         if (!newval)
4730         {
4731             res = ERROR_OUTOFMEMORY;
4732             goto done;
4733         }
4734
4735         if (!(flags & ENV_MOD_MASK))
4736             lstrcpyW(newval, value);
4737         else
4738         {
4739             if (flags & ENV_MOD_PREFIX)
4740             {
4741                 lstrcpyW(newval, value);
4742                 lstrcatW(newval, semicolon);
4743                 ptr = newval + lstrlenW(value) + 1;
4744             }
4745
4746             lstrcpyW(ptr, data);
4747
4748             if (flags & ENV_MOD_APPEND)
4749             {
4750                 lstrcatW(newval, semicolon);
4751                 lstrcatW(newval, value);
4752             }
4753         }
4754     }
4755     else
4756     {
4757         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
4758         newval = msi_alloc(size);
4759         if (!newval)
4760         {
4761             res = ERROR_OUTOFMEMORY;
4762             goto done;
4763         }
4764
4765         lstrcpyW(newval, value);
4766     }
4767
4768     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
4769     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
4770
4771 done:
4772     RegCloseKey(env);
4773     msi_free(deformatted);
4774     msi_free(data);
4775     msi_free(newval);
4776     return res;
4777 }
4778
4779 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4780 {
4781     UINT rc;
4782     MSIQUERY * view;
4783     static const WCHAR ExecSeqQuery[] =
4784         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4785          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4786     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4787     if (rc != ERROR_SUCCESS)
4788         return ERROR_SUCCESS;
4789
4790     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
4791     msiobj_release(&view->hdr);
4792
4793     return rc;
4794 }
4795
4796 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4797                                            LPCSTR action, LPCWSTR table )
4798 {
4799     static const WCHAR query[] = {
4800         'S','E','L','E','C','T',' ','*',' ',
4801         'F','R','O','M',' ','`','%','s','`',0 };
4802     MSIQUERY *view = NULL;
4803     DWORD count = 0;
4804     UINT r;
4805     
4806     r = MSI_OpenQuery( package->db, &view, query, table );
4807     if (r == ERROR_SUCCESS)
4808     {
4809         r = MSI_IterateRecords(view, &count, NULL, package);
4810         msiobj_release(&view->hdr);
4811     }
4812
4813     if (count)
4814         FIXME("%s -> %u ignored %s table values\n",
4815               action, count, debugstr_w(table));
4816
4817     return ERROR_SUCCESS;
4818 }
4819
4820 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4821 {
4822     TRACE("%p\n", package);
4823     return ERROR_SUCCESS;
4824 }
4825
4826 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4827 {
4828     static const WCHAR table[] =
4829          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4830     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4831 }
4832
4833 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4834 {
4835     static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4836     return msi_unimplemented_action_stub( package, "MoveFiles", table );
4837 }
4838
4839 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4840 {
4841     static const WCHAR table[] = { 'P','a','t','c','h',0 };
4842     return msi_unimplemented_action_stub( package, "PatchFiles", table );
4843 }
4844
4845 static UINT ACTION_BindImage( MSIPACKAGE *package )
4846 {
4847     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4848     return msi_unimplemented_action_stub( package, "BindImage", table );
4849 }
4850
4851 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4852 {
4853     static const WCHAR table[] = {
4854         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4855     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4856 }
4857
4858 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4859 {
4860     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4861     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4862 }
4863
4864 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4865 {
4866     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4867     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4868 }
4869
4870 static UINT ACTION_StopServices( MSIPACKAGE *package )
4871 {
4872     static const WCHAR table[] = {
4873         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4874     return msi_unimplemented_action_stub( package, "StopServices", table );
4875 }
4876
4877 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4878 {
4879     static const WCHAR table[] = {
4880         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4881     return msi_unimplemented_action_stub( package, "DeleteServices", table );
4882 }
4883 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
4884 {
4885         static const WCHAR table[] = {
4886                 'P','r','o','d','u','c','t','I','D',0 };
4887         return msi_unimplemented_action_stub( package, "ValidateProductID", table );
4888 }
4889
4890 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
4891 {
4892     static const WCHAR table[] = {
4893         'E','n','v','i','r','o','n','m','e','n','t',0 };
4894     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
4895 }
4896
4897 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
4898 {
4899     static const WCHAR table[] = {
4900         'M','s','i','A','s','s','e','m','b','l','y',0 };
4901     return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
4902 }
4903
4904 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
4905 {
4906     static const WCHAR table[] = {
4907         'M','s','i','A','s','s','e','m','b','l','y',0 };
4908     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
4909 }
4910
4911 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
4912 {
4913     static const WCHAR table[] = { 'F','o','n','t',0 };
4914     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
4915 }
4916
4917 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
4918 {
4919     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4920     return msi_unimplemented_action_stub( package, "CCPSearch", table );
4921 }
4922
4923 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
4924 {
4925     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4926     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
4927 }
4928
4929 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
4930 {
4931     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4932     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
4933 }
4934
4935 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
4936 {
4937     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4938     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
4939 }
4940
4941 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
4942 {
4943     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
4944     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
4945 }
4946
4947 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
4948 {
4949     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
4950     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
4951 }
4952
4953 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
4954 {
4955     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4956     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
4957 }
4958
4959 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
4960 {
4961     static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
4962     return msi_unimplemented_action_stub( package, "RemoveFolders", table );
4963 }
4964
4965 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
4966 {
4967     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
4968     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
4969 }
4970
4971 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
4972 {
4973     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
4974     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
4975 }
4976
4977 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4978 {
4979     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
4980     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
4981 }
4982
4983 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
4984 {
4985     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
4986     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
4987 }
4988
4989 static UINT ACTION_UnpublishFeatures( MSIPACKAGE *package )
4990 {
4991     static const WCHAR table[] = { 'F','e','a','t','u','r','e','C','o','m','p','o','n','e','n','t','s',0 };
4992     return msi_unimplemented_action_stub( package, "UnpublishFeatures", table );
4993 }
4994
4995 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
4996 {
4997     static const WCHAR table[] = { 'A','p','p','I','d',0 };
4998     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
4999 }
5000
5001 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5002 {
5003     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5004     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5005 }
5006
5007 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5008 {
5009     static const WCHAR table[] = { 'M','I','M','E',0 };
5010     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5011 }
5012
5013 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5014 {
5015     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5016     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5017 }
5018
5019 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5020 {
5021     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5022     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5023 }
5024
5025 static const struct _actions StandardActions[] = {
5026     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5027     { szAppSearch, ACTION_AppSearch },
5028     { szBindImage, ACTION_BindImage },
5029     { szCCPSearch, ACTION_CCPSearch },
5030     { szCostFinalize, ACTION_CostFinalize },
5031     { szCostInitialize, ACTION_CostInitialize },
5032     { szCreateFolders, ACTION_CreateFolders },
5033     { szCreateShortcuts, ACTION_CreateShortcuts },
5034     { szDeleteServices, ACTION_DeleteServices },
5035     { szDisableRollback, NULL },
5036     { szDuplicateFiles, ACTION_DuplicateFiles },
5037     { szExecuteAction, ACTION_ExecuteAction },
5038     { szFileCost, ACTION_FileCost },
5039     { szFindRelatedProducts, ACTION_FindRelatedProducts },
5040     { szForceReboot, ACTION_ForceReboot },
5041     { szInstallAdminPackage, NULL },
5042     { szInstallExecute, ACTION_InstallExecute },
5043     { szInstallExecuteAgain, ACTION_InstallExecute },
5044     { szInstallFiles, ACTION_InstallFiles},
5045     { szInstallFinalize, ACTION_InstallFinalize },
5046     { szInstallInitialize, ACTION_InstallInitialize },
5047     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5048     { szInstallValidate, ACTION_InstallValidate },
5049     { szIsolateComponents, ACTION_IsolateComponents },
5050     { szLaunchConditions, ACTION_LaunchConditions },
5051     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5052     { szMoveFiles, ACTION_MoveFiles },
5053     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5054     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5055     { szInstallODBC, ACTION_InstallODBC },
5056     { szInstallServices, ACTION_InstallServices },
5057     { szPatchFiles, ACTION_PatchFiles },
5058     { szProcessComponents, ACTION_ProcessComponents },
5059     { szPublishComponents, ACTION_PublishComponents },
5060     { szPublishFeatures, ACTION_PublishFeatures },
5061     { szPublishProduct, ACTION_PublishProduct },
5062     { szRegisterClassInfo, ACTION_RegisterClassInfo },
5063     { szRegisterComPlus, ACTION_RegisterComPlus},
5064     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5065     { szRegisterFonts, ACTION_RegisterFonts },
5066     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5067     { szRegisterProduct, ACTION_RegisterProduct },
5068     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5069     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5070     { szRegisterUser, ACTION_RegisterUser },
5071     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5072     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5073     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5074     { szRemoveFiles, ACTION_RemoveFiles },
5075     { szRemoveFolders, ACTION_RemoveFolders },
5076     { szRemoveIniValues, ACTION_RemoveIniValues },
5077     { szRemoveODBC, ACTION_RemoveODBC },
5078     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5079     { szRemoveShortcuts, ACTION_RemoveShortcuts },
5080     { szResolveSource, ACTION_ResolveSource },
5081     { szRMCCPSearch, ACTION_RMCCPSearch },
5082     { szScheduleReboot, NULL },
5083     { szSelfRegModules, ACTION_SelfRegModules },
5084     { szSelfUnregModules, ACTION_SelfUnregModules },
5085     { szSetODBCFolders, NULL },
5086     { szStartServices, ACTION_StartServices },
5087     { szStopServices, ACTION_StopServices },
5088     { szUnpublishComponents, ACTION_UnpublishComponents },
5089     { szUnpublishFeatures, ACTION_UnpublishFeatures },
5090     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5091     { szUnregisterComPlus, ACTION_UnregisterComPlus },
5092     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5093     { szUnregisterFonts, ACTION_UnregisterFonts },
5094     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5095     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5096     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5097     { szValidateProductID, ACTION_ValidateProductID },
5098     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5099     { szWriteIniValues, ACTION_WriteIniValues },
5100     { szWriteRegistryValues, ACTION_WriteRegistryValues },
5101     { NULL, NULL },
5102 };