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