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