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