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