gdi32: Use atan2 to compute angles in WidenPath without triggering floating point...
[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      *
1784      * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1785      * REMOVE are the big ones, since we don't handle administrative installs
1786      * yet anyway.
1787      */
1788     override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1789     override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1790     override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1791     override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1792
1793     if (!override)
1794     {
1795         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1796         {
1797             BOOL feature_state = ((feature->Level > 0) &&
1798                              (feature->Level <= install_level));
1799
1800             if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1801             {
1802                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1803                     msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1804                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1805                     msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1806                 else
1807                     msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1808             }
1809         }
1810
1811         /* disable child features of unselected parent features */
1812         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1813         {
1814             FeatureList *fl;
1815
1816             if (feature->Level > 0 && feature->Level <= install_level)
1817                 continue;
1818
1819             LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1820                 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1821         }
1822     }
1823     else
1824     {
1825         /* set the Preselected Property */
1826         static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1827         static const WCHAR szOne[] = { '1', 0 };
1828
1829         MSI_SetPropertyW(package,szPreselected,szOne);
1830     }
1831
1832     /*
1833      * now we want to enable or disable components base on feature
1834      */
1835
1836     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1837     {
1838         ComponentList *cl;
1839
1840         TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1841               debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1842
1843         if (!feature->Level)
1844             continue;
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("Resetting 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 szOutOfDiskSpace[] =
2129         {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2130     static const WCHAR szOne[] = { '1', 0 };
2131     static const WCHAR szZero[] = { '0', 0 };
2132     MSICOMPONENT *comp;
2133     UINT rc;
2134     MSIQUERY * view;
2135     LPWSTR level;
2136
2137     if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2138         return ERROR_SUCCESS;
2139
2140     TRACE("Building Directory properties\n");
2141
2142     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2143     if (rc == ERROR_SUCCESS)
2144     {
2145         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2146                         package);
2147         msiobj_release(&view->hdr);
2148     }
2149
2150     /* read components states from the registry */
2151     ACTION_GetComponentInstallStates(package);
2152
2153     TRACE("File calculations\n");
2154     msi_check_file_install_states( package );
2155
2156     TRACE("Evaluating Condition Table\n");
2157
2158     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2159     if (rc == ERROR_SUCCESS)
2160     {
2161         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2162                     package);
2163         msiobj_release(&view->hdr);
2164     }
2165
2166     TRACE("Enabling or Disabling Components\n");
2167     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2168     {
2169         if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2170         {
2171             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2172             comp->Enabled = FALSE;
2173         }
2174     }
2175
2176     MSI_SetPropertyW(package,szCosting,szOne);
2177     /* set default run level if not set */
2178     level = msi_dup_property( package, szlevel );
2179     if (!level)
2180         MSI_SetPropertyW(package,szlevel, szOne);
2181     msi_free(level);
2182
2183     /* FIXME: check volume disk space */
2184     MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2185
2186     ACTION_UpdateFeatureInstallStates(package);
2187
2188     return MSI_SetFeatureStates(package);
2189 }
2190
2191 /* OK this value is "interpreted" and then formatted based on the 
2192    first few characters */
2193 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2194                          DWORD *size)
2195 {
2196     LPSTR data = NULL;
2197
2198     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2199     {
2200         if (value[1]=='x')
2201         {
2202             LPWSTR ptr;
2203             CHAR byte[5];
2204             LPWSTR deformated = NULL;
2205             int count;
2206
2207             deformat_string(package, &value[2], &deformated);
2208
2209             /* binary value type */
2210             ptr = deformated;
2211             *type = REG_BINARY;
2212             if (strlenW(ptr)%2)
2213                 *size = (strlenW(ptr)/2)+1;
2214             else
2215                 *size = strlenW(ptr)/2;
2216
2217             data = msi_alloc(*size);
2218
2219             byte[0] = '0'; 
2220             byte[1] = 'x'; 
2221             byte[4] = 0; 
2222             count = 0;
2223             /* if uneven pad with a zero in front */
2224             if (strlenW(ptr)%2)
2225             {
2226                 byte[2]= '0';
2227                 byte[3]= *ptr;
2228                 ptr++;
2229                 data[count] = (BYTE)strtol(byte,NULL,0);
2230                 count ++;
2231                 TRACE("Uneven byte count\n");
2232             }
2233             while (*ptr)
2234             {
2235                 byte[2]= *ptr;
2236                 ptr++;
2237                 byte[3]= *ptr;
2238                 ptr++;
2239                 data[count] = (BYTE)strtol(byte,NULL,0);
2240                 count ++;
2241             }
2242             msi_free(deformated);
2243
2244             TRACE("Data %i bytes(%i)\n",*size,count);
2245         }
2246         else
2247         {
2248             LPWSTR deformated;
2249             LPWSTR p;
2250             DWORD d = 0;
2251             deformat_string(package, &value[1], &deformated);
2252
2253             *type=REG_DWORD; 
2254             *size = sizeof(DWORD);
2255             data = msi_alloc(*size);
2256             p = deformated;
2257             if (*p == '-')
2258                 p++;
2259             while (*p)
2260             {
2261                 if ( (*p < '0') || (*p > '9') )
2262                     break;
2263                 d *= 10;
2264                 d += (*p - '0');
2265                 p++;
2266             }
2267             if (deformated[0] == '-')
2268                 d = -d;
2269             *(LPDWORD)data = d;
2270             TRACE("DWORD %i\n",*(LPDWORD)data);
2271
2272             msi_free(deformated);
2273         }
2274     }
2275     else
2276     {
2277         static const WCHAR szMulti[] = {'[','~',']',0};
2278         LPCWSTR ptr;
2279         *type=REG_SZ;
2280
2281         if (value[0]=='#')
2282         {
2283             if (value[1]=='%')
2284             {
2285                 ptr = &value[2];
2286                 *type=REG_EXPAND_SZ;
2287             }
2288             else
2289                 ptr = &value[1];
2290          }
2291          else
2292             ptr=value;
2293
2294         if (strstrW(value,szMulti))
2295             *type = REG_MULTI_SZ;
2296
2297         /* remove initial delimiter */
2298         if (!strncmpW(value, szMulti, 3))
2299             ptr = value + 3;
2300
2301         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2302
2303         /* add double NULL terminator */
2304         if (*type == REG_MULTI_SZ)
2305         {
2306             *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2307             data = msi_realloc_zero(data, *size);
2308         }
2309     }
2310     return data;
2311 }
2312
2313 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2314 {
2315     MSIPACKAGE *package = (MSIPACKAGE*)param;
2316     static const WCHAR szHCR[] = 
2317         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2318          'R','O','O','T','\\',0};
2319     static const WCHAR szHCU[] =
2320         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2321          'U','S','E','R','\\',0};
2322     static const WCHAR szHLM[] =
2323         {'H','K','E','Y','_','L','O','C','A','L','_',
2324          'M','A','C','H','I','N','E','\\',0};
2325     static const WCHAR szHU[] =
2326         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2327
2328     LPSTR value_data = NULL;
2329     HKEY  root_key, hkey;
2330     DWORD type,size;
2331     LPWSTR  deformated;
2332     LPCWSTR szRoot, component, name, key, value;
2333     MSICOMPONENT *comp;
2334     MSIRECORD * uirow;
2335     LPWSTR uikey;
2336     INT   root;
2337     BOOL check_first = FALSE;
2338     UINT rc;
2339
2340     ui_progress(package,2,0,0,0);
2341
2342     value = NULL;
2343     key = NULL;
2344     uikey = NULL;
2345     name = NULL;
2346
2347     component = MSI_RecordGetString(row, 6);
2348     comp = get_loaded_component(package,component);
2349     if (!comp)
2350         return ERROR_SUCCESS;
2351
2352     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2353     {
2354         TRACE("Skipping write due to disabled component %s\n",
2355                         debugstr_w(component));
2356
2357         comp->Action = comp->Installed;
2358
2359         return ERROR_SUCCESS;
2360     }
2361
2362     comp->Action = INSTALLSTATE_LOCAL;
2363
2364     name = MSI_RecordGetString(row, 4);
2365     if( MSI_RecordIsNull(row,5) && name )
2366     {
2367         /* null values can have special meanings */
2368         if (name[0]=='-' && name[1] == 0)
2369                 return ERROR_SUCCESS;
2370         else if ((name[0]=='+' && name[1] == 0) || 
2371                  (name[0] == '*' && name[1] == 0))
2372                 name = NULL;
2373         check_first = TRUE;
2374     }
2375
2376     root = MSI_RecordGetInteger(row,2);
2377     key = MSI_RecordGetString(row, 3);
2378
2379     /* get the root key */
2380     switch (root)
2381     {
2382         case -1: 
2383             {
2384                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2385                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2386                 if (all_users && all_users[0] == '1')
2387                 {
2388                     root_key = HKEY_LOCAL_MACHINE;
2389                     szRoot = szHLM;
2390                 }
2391                 else
2392                 {
2393                     root_key = HKEY_CURRENT_USER;
2394                     szRoot = szHCU;
2395                 }
2396                 msi_free(all_users);
2397             }
2398                  break;
2399         case 0:  root_key = HKEY_CLASSES_ROOT; 
2400                  szRoot = szHCR;
2401                  break;
2402         case 1:  root_key = HKEY_CURRENT_USER;
2403                  szRoot = szHCU;
2404                  break;
2405         case 2:  root_key = HKEY_LOCAL_MACHINE;
2406                  szRoot = szHLM;
2407                  break;
2408         case 3:  root_key = HKEY_USERS; 
2409                  szRoot = szHU;
2410                  break;
2411         default:
2412                  ERR("Unknown root %i\n",root);
2413                  root_key=NULL;
2414                  szRoot = NULL;
2415                  break;
2416     }
2417     if (!root_key)
2418         return ERROR_SUCCESS;
2419
2420     deformat_string(package, key , &deformated);
2421     size = strlenW(deformated) + strlenW(szRoot) + 1;
2422     uikey = msi_alloc(size*sizeof(WCHAR));
2423     strcpyW(uikey,szRoot);
2424     strcatW(uikey,deformated);
2425
2426     if (RegCreateKeyW( root_key, deformated, &hkey))
2427     {
2428         ERR("Could not create key %s\n",debugstr_w(deformated));
2429         msi_free(deformated);
2430         msi_free(uikey);
2431         return ERROR_SUCCESS;
2432     }
2433     msi_free(deformated);
2434
2435     value = MSI_RecordGetString(row,5);
2436     if (value)
2437         value_data = parse_value(package, value, &type, &size); 
2438     else
2439     {
2440         static const WCHAR szEmpty[] = {0};
2441         value_data = (LPSTR)strdupW(szEmpty);
2442         size = sizeof(szEmpty);
2443         type = REG_SZ;
2444     }
2445
2446     deformat_string(package, name, &deformated);
2447
2448     if (!check_first)
2449     {
2450         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2451                         debugstr_w(uikey));
2452         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2453     }
2454     else
2455     {
2456         DWORD sz = 0;
2457         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2458         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2459         {
2460             TRACE("value %s of %s checked already exists\n",
2461                             debugstr_w(deformated), debugstr_w(uikey));
2462         }
2463         else
2464         {
2465             TRACE("Checked and setting value %s of %s\n",
2466                             debugstr_w(deformated), debugstr_w(uikey));
2467             if (deformated || size)
2468                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2469         }
2470     }
2471     RegCloseKey(hkey);
2472
2473     uirow = MSI_CreateRecord(3);
2474     MSI_RecordSetStringW(uirow,2,deformated);
2475     MSI_RecordSetStringW(uirow,1,uikey);
2476
2477     if (type == REG_SZ)
2478         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2479     else
2480         MSI_RecordSetStringW(uirow,3,value);
2481
2482     ui_actiondata(package,szWriteRegistryValues,uirow);
2483     msiobj_release( &uirow->hdr );
2484
2485     msi_free(value_data);
2486     msi_free(deformated);
2487     msi_free(uikey);
2488
2489     return ERROR_SUCCESS;
2490 }
2491
2492 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2493 {
2494     UINT rc;
2495     MSIQUERY * view;
2496     static const WCHAR ExecSeqQuery[] =
2497         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2498          '`','R','e','g','i','s','t','r','y','`',0 };
2499
2500     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2501     if (rc != ERROR_SUCCESS)
2502         return ERROR_SUCCESS;
2503
2504     /* increment progress bar each time action data is sent */
2505     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2506
2507     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2508
2509     msiobj_release(&view->hdr);
2510     return rc;
2511 }
2512
2513 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2514 {
2515     package->script->CurrentlyScripting = TRUE;
2516
2517     return ERROR_SUCCESS;
2518 }
2519
2520
2521 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2522 {
2523     MSICOMPONENT *comp;
2524     DWORD progress = 0;
2525     DWORD total = 0;
2526     static const WCHAR q1[]=
2527         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2528          '`','R','e','g','i','s','t','r','y','`',0};
2529     UINT rc;
2530     MSIQUERY * view;
2531     MSIFEATURE *feature;
2532     MSIFILE *file;
2533
2534     TRACE("InstallValidate\n");
2535
2536     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2537     if (rc == ERROR_SUCCESS)
2538     {
2539         MSI_IterateRecords( view, &progress, NULL, package );
2540         msiobj_release( &view->hdr );
2541         total += progress * REG_PROGRESS_VALUE;
2542     }
2543
2544     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2545         total += COMPONENT_PROGRESS_VALUE;
2546
2547     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2548         total += file->FileSize;
2549
2550     ui_progress(package,0,total,0,0);
2551
2552     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2553     {
2554         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2555             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2556             feature->ActionRequest);
2557     }
2558     
2559     return ERROR_SUCCESS;
2560 }
2561
2562 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2563 {
2564     MSIPACKAGE* package = (MSIPACKAGE*)param;
2565     LPCWSTR cond = NULL; 
2566     LPCWSTR message = NULL;
2567     UINT r;
2568
2569     static const WCHAR title[]=
2570         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2571
2572     cond = MSI_RecordGetString(row,1);
2573
2574     r = MSI_EvaluateConditionW(package,cond);
2575     if (r == MSICONDITION_FALSE)
2576     {
2577         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2578         {
2579             LPWSTR deformated;
2580             message = MSI_RecordGetString(row,2);
2581             deformat_string(package,message,&deformated);
2582             MessageBoxW(NULL,deformated,title,MB_OK);
2583             msi_free(deformated);
2584         }
2585
2586         return ERROR_INSTALL_FAILURE;
2587     }
2588
2589     return ERROR_SUCCESS;
2590 }
2591
2592 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2593 {
2594     UINT rc;
2595     MSIQUERY * view = NULL;
2596     static const WCHAR ExecSeqQuery[] =
2597         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2598          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2599
2600     TRACE("Checking launch conditions\n");
2601
2602     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2603     if (rc != ERROR_SUCCESS)
2604         return ERROR_SUCCESS;
2605
2606     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2607     msiobj_release(&view->hdr);
2608
2609     return rc;
2610 }
2611
2612 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2613 {
2614
2615     if (!cmp->KeyPath)
2616         return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2617
2618     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2619     {
2620         MSIRECORD * row = 0;
2621         UINT root,len;
2622         LPWSTR deformated,buffer,deformated_name;
2623         LPCWSTR key,name;
2624         static const WCHAR ExecSeqQuery[] =
2625             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2626              '`','R','e','g','i','s','t','r','y','`',' ',
2627              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2628              ' ','=',' ' ,'\'','%','s','\'',0 };
2629         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2630         static const WCHAR fmt2[]=
2631             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2632
2633         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2634         if (!row)
2635             return NULL;
2636
2637         root = MSI_RecordGetInteger(row,2);
2638         key = MSI_RecordGetString(row, 3);
2639         name = MSI_RecordGetString(row, 4);
2640         deformat_string(package, key , &deformated);
2641         deformat_string(package, name, &deformated_name);
2642
2643         len = strlenW(deformated) + 6;
2644         if (deformated_name)
2645             len+=strlenW(deformated_name);
2646
2647         buffer = msi_alloc( len *sizeof(WCHAR));
2648
2649         if (deformated_name)
2650             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2651         else
2652             sprintfW(buffer,fmt,root,deformated);
2653
2654         msi_free(deformated);
2655         msi_free(deformated_name);
2656         msiobj_release(&row->hdr);
2657
2658         return buffer;
2659     }
2660     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2661     {
2662         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2663         return NULL;
2664     }
2665     else
2666     {
2667         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2668
2669         if (file)
2670             return strdupW( file->TargetPath );
2671     }
2672     return NULL;
2673 }
2674
2675 static HKEY openSharedDLLsKey(void)
2676 {
2677     HKEY hkey=0;
2678     static const WCHAR path[] =
2679         {'S','o','f','t','w','a','r','e','\\',
2680          'M','i','c','r','o','s','o','f','t','\\',
2681          'W','i','n','d','o','w','s','\\',
2682          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2683          'S','h','a','r','e','d','D','L','L','s',0};
2684
2685     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2686     return hkey;
2687 }
2688
2689 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2690 {
2691     HKEY hkey;
2692     DWORD count=0;
2693     DWORD type;
2694     DWORD sz = sizeof(count);
2695     DWORD rc;
2696     
2697     hkey = openSharedDLLsKey();
2698     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2699     if (rc != ERROR_SUCCESS)
2700         count = 0;
2701     RegCloseKey(hkey);
2702     return count;
2703 }
2704
2705 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2706 {
2707     HKEY hkey;
2708
2709     hkey = openSharedDLLsKey();
2710     if (count > 0)
2711         msi_reg_set_val_dword( hkey, path, count );
2712     else
2713         RegDeleteValueW(hkey,path);
2714     RegCloseKey(hkey);
2715     return count;
2716 }
2717
2718 /*
2719  * Return TRUE if the count should be written out and FALSE if not
2720  */
2721 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2722 {
2723     MSIFEATURE *feature;
2724     INT count = 0;
2725     BOOL write = FALSE;
2726
2727     /* only refcount DLLs */
2728     if (comp->KeyPath == NULL || 
2729         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2730         comp->Attributes & msidbComponentAttributesODBCDataSource)
2731         write = FALSE;
2732     else
2733     {
2734         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2735         write = (count > 0);
2736
2737         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2738             write = TRUE;
2739     }
2740
2741     /* increment counts */
2742     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2743     {
2744         ComponentList *cl;
2745
2746         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2747             continue;
2748
2749         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2750         {
2751             if ( cl->component == comp )
2752                 count++;
2753         }
2754     }
2755
2756     /* decrement counts */
2757     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2758     {
2759         ComponentList *cl;
2760
2761         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2762             continue;
2763
2764         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2765         {
2766             if ( cl->component == comp )
2767                 count--;
2768         }
2769     }
2770
2771     /* ref count all the files in the component */
2772     if (write)
2773     {
2774         MSIFILE *file;
2775
2776         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2777         {
2778             if (file->Component == comp)
2779                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2780         }
2781     }
2782     
2783     /* add a count for permanent */
2784     if (comp->Attributes & msidbComponentAttributesPermanent)
2785         count ++;
2786     
2787     comp->RefCount = count;
2788
2789     if (write)
2790         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2791 }
2792
2793 /*
2794  * Ok further analysis makes me think that this work is
2795  * actually done in the PublishComponents and PublishFeatures
2796  * step, and not here.  It appears like the keypath and all that is
2797  * resolved in this step, however actually written in the Publish steps.
2798  * But we will leave it here for now because it is unclear
2799  */
2800 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2801 {
2802     WCHAR squished_pc[GUID_SIZE];
2803     WCHAR squished_cc[GUID_SIZE];
2804     UINT rc;
2805     MSICOMPONENT *comp;
2806     HKEY hkey=0,hkey2=0;
2807
2808     TRACE("\n");
2809
2810     /* writes the Component and Features values to the registry */
2811
2812     rc = MSIREG_OpenComponents(&hkey);
2813     if (rc != ERROR_SUCCESS)
2814         return rc;
2815
2816     squash_guid(package->ProductCode,squished_pc);
2817     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2818
2819     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2820     {
2821         MSIRECORD * uirow;
2822
2823         ui_progress(package,2,0,0,0);
2824         if (!comp->ComponentId)
2825             continue;
2826
2827         squash_guid(comp->ComponentId,squished_cc);
2828
2829         msi_free(comp->FullKeypath);
2830         comp->FullKeypath = resolve_keypath( package, comp );
2831
2832         /* do the refcounting */
2833         ACTION_RefCountComponent( package, comp );
2834
2835         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2836                             debugstr_w(comp->Component),
2837                             debugstr_w(squished_cc),
2838                             debugstr_w(comp->FullKeypath),
2839                             comp->RefCount);
2840         /*
2841          * Write the keypath out if the component is to be registered
2842          * and delete the key if the component is to be unregistered
2843          */
2844         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2845         {
2846             rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2847             if (rc != ERROR_SUCCESS)
2848                 continue;
2849
2850             if (!comp->FullKeypath)
2851                 continue;
2852
2853             msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2854
2855             if (comp->Attributes & msidbComponentAttributesPermanent)
2856             {
2857                 static const WCHAR szPermKey[] =
2858                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2859                       '0','0','0','0','0','0','0','0','0','0','0','0',
2860                       '0','0','0','0','0','0','0','0',0 };
2861
2862                 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2863             }
2864
2865             RegCloseKey(hkey2);
2866
2867             rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2868             if (rc != ERROR_SUCCESS)
2869                 continue;
2870
2871             msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2872             RegCloseKey(hkey2);
2873         }
2874         else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2875         {
2876             DWORD res;
2877
2878             rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2879             if (rc != ERROR_SUCCESS)
2880                 continue;
2881
2882             RegDeleteValueW(hkey2,squished_pc);
2883
2884             /* if the key is empty delete it */
2885             res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2886             RegCloseKey(hkey2);
2887             if (res == ERROR_NO_MORE_ITEMS)
2888                 RegDeleteKeyW(hkey,squished_cc);
2889
2890             MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2891         }
2892
2893         /* UI stuff */
2894         uirow = MSI_CreateRecord(3);
2895         MSI_RecordSetStringW(uirow,1,package->ProductCode);
2896         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2897         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2898         ui_actiondata(package,szProcessComponents,uirow);
2899         msiobj_release( &uirow->hdr );
2900     }
2901     RegCloseKey(hkey);
2902     return rc;
2903 }
2904
2905 typedef struct {
2906     CLSID       clsid;
2907     LPWSTR      source;
2908
2909     LPWSTR      path;
2910     ITypeLib    *ptLib;
2911 } typelib_struct;
2912
2913 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
2914                                        LPWSTR lpszName, LONG_PTR lParam)
2915 {
2916     TLIBATTR *attr;
2917     typelib_struct *tl_struct = (typelib_struct*) lParam;
2918     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2919     int sz; 
2920     HRESULT res;
2921
2922     if (!IS_INTRESOURCE(lpszName))
2923     {
2924         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2925         return TRUE;
2926     }
2927
2928     sz = strlenW(tl_struct->source)+4;
2929     sz *= sizeof(WCHAR);
2930
2931     if ((INT_PTR)lpszName == 1)
2932         tl_struct->path = strdupW(tl_struct->source);
2933     else
2934     {
2935         tl_struct->path = msi_alloc(sz);
2936         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2937     }
2938
2939     TRACE("trying %s\n", debugstr_w(tl_struct->path));
2940     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2941     if (!SUCCEEDED(res))
2942     {
2943         msi_free(tl_struct->path);
2944         tl_struct->path = NULL;
2945
2946         return TRUE;
2947     }
2948
2949     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2950     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2951     {
2952         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2953         return FALSE;
2954     }
2955
2956     msi_free(tl_struct->path);
2957     tl_struct->path = NULL;
2958
2959     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2960     ITypeLib_Release(tl_struct->ptLib);
2961
2962     return TRUE;
2963 }
2964
2965 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2966 {
2967     MSIPACKAGE* package = (MSIPACKAGE*)param;
2968     LPCWSTR component;
2969     MSICOMPONENT *comp;
2970     MSIFILE *file;
2971     typelib_struct tl_struct;
2972     HMODULE module;
2973     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2974
2975     component = MSI_RecordGetString(row,3);
2976     comp = get_loaded_component(package,component);
2977     if (!comp)
2978         return ERROR_SUCCESS;
2979
2980     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2981     {
2982         TRACE("Skipping typelib reg due to disabled component\n");
2983
2984         comp->Action = comp->Installed;
2985
2986         return ERROR_SUCCESS;
2987     }
2988
2989     comp->Action = INSTALLSTATE_LOCAL;
2990
2991     file = get_loaded_file( package, comp->KeyPath ); 
2992     if (!file)
2993         return ERROR_SUCCESS;
2994
2995     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2996     if (module)
2997     {
2998         LPCWSTR guid;
2999         guid = MSI_RecordGetString(row,1);
3000         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3001         tl_struct.source = strdupW( file->TargetPath );
3002         tl_struct.path = NULL;
3003
3004         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3005                         (LONG_PTR)&tl_struct);
3006
3007         if (tl_struct.path)
3008         {
3009             LPWSTR help = NULL;
3010             LPCWSTR helpid;
3011             HRESULT res;
3012
3013             helpid = MSI_RecordGetString(row,6);
3014
3015             if (helpid)
3016                 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3017             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3018             msi_free(help);
3019
3020             if (!SUCCEEDED(res))
3021                 ERR("Failed to register type library %s\n",
3022                         debugstr_w(tl_struct.path));
3023             else
3024             {
3025                 ui_actiondata(package,szRegisterTypeLibraries,row);
3026
3027                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3028             }
3029
3030             ITypeLib_Release(tl_struct.ptLib);
3031             msi_free(tl_struct.path);
3032         }
3033         else
3034             ERR("Failed to load type library %s\n",
3035                     debugstr_w(tl_struct.source));
3036
3037         FreeLibrary(module);
3038         msi_free(tl_struct.source);
3039     }
3040     else
3041         ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3042
3043     return ERROR_SUCCESS;
3044 }
3045
3046 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3047 {
3048     /* 
3049      * OK this is a bit confusing.. I am given a _Component key and I believe
3050      * that the file that is being registered as a type library is the "key file
3051      * of that component" which I interpret to mean "The file in the KeyPath of
3052      * that component".
3053      */
3054     UINT rc;
3055     MSIQUERY * view;
3056     static const WCHAR Query[] =
3057         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3058          '`','T','y','p','e','L','i','b','`',0};
3059
3060     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3061     if (rc != ERROR_SUCCESS)
3062         return ERROR_SUCCESS;
3063
3064     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3065     msiobj_release(&view->hdr);
3066     return rc;
3067 }
3068
3069 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3070 {
3071     MSIPACKAGE *package = (MSIPACKAGE*)param;
3072     LPWSTR target_file, target_folder, filename;
3073     LPCWSTR buffer, extension;
3074     MSICOMPONENT *comp;
3075     static const WCHAR szlnk[]={'.','l','n','k',0};
3076     IShellLinkW *sl = NULL;
3077     IPersistFile *pf = NULL;
3078     HRESULT res;
3079
3080     buffer = MSI_RecordGetString(row,4);
3081     comp = get_loaded_component(package,buffer);
3082     if (!comp)
3083         return ERROR_SUCCESS;
3084
3085     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3086     {
3087         TRACE("Skipping shortcut creation due to disabled component\n");
3088
3089         comp->Action = comp->Installed;
3090
3091         return ERROR_SUCCESS;
3092     }
3093
3094     comp->Action = INSTALLSTATE_LOCAL;
3095
3096     ui_actiondata(package,szCreateShortcuts,row);
3097
3098     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3099                     &IID_IShellLinkW, (LPVOID *) &sl );
3100
3101     if (FAILED( res ))
3102     {
3103         ERR("CLSID_ShellLink not available\n");
3104         goto err;
3105     }
3106
3107     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3108     if (FAILED( res ))
3109     {
3110         ERR("QueryInterface(IID_IPersistFile) failed\n");
3111         goto err;
3112     }
3113
3114     buffer = MSI_RecordGetString(row,2);
3115     target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3116
3117     /* may be needed because of a bug somewhere else */
3118     create_full_pathW(target_folder);
3119
3120     filename = msi_dup_record_field( row, 3 );
3121     reduce_to_longfilename(filename);
3122
3123     extension = strchrW(filename,'.');
3124     if (!extension || strcmpiW(extension,szlnk))
3125     {
3126         int len = strlenW(filename);
3127         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3128         memcpy(filename + len, szlnk, sizeof(szlnk));
3129     }
3130     target_file = build_directory_name(2, target_folder, filename);
3131     msi_free(target_folder);
3132     msi_free(filename);
3133
3134     buffer = MSI_RecordGetString(row,5);
3135     if (strchrW(buffer,'['))
3136     {
3137         LPWSTR deformated;
3138         deformat_string(package,buffer,&deformated);
3139         IShellLinkW_SetPath(sl,deformated);
3140         msi_free(deformated);
3141     }
3142     else
3143     {
3144         FIXME("poorly handled shortcut format, advertised shortcut\n");
3145         IShellLinkW_SetPath(sl,comp->FullKeypath);
3146     }
3147
3148     if (!MSI_RecordIsNull(row,6))
3149     {
3150         LPWSTR deformated;
3151         buffer = MSI_RecordGetString(row,6);
3152         deformat_string(package,buffer,&deformated);
3153         IShellLinkW_SetArguments(sl,deformated);
3154         msi_free(deformated);
3155     }
3156
3157     if (!MSI_RecordIsNull(row,7))
3158     {
3159         buffer = MSI_RecordGetString(row,7);
3160         IShellLinkW_SetDescription(sl,buffer);
3161     }
3162
3163     if (!MSI_RecordIsNull(row,8))
3164         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3165
3166     if (!MSI_RecordIsNull(row,9))
3167     {
3168         LPWSTR Path;
3169         INT index; 
3170
3171         buffer = MSI_RecordGetString(row,9);
3172
3173         Path = build_icon_path(package,buffer);
3174         index = MSI_RecordGetInteger(row,10);
3175
3176         /* no value means 0 */
3177         if (index == MSI_NULL_INTEGER)
3178             index = 0;
3179
3180         IShellLinkW_SetIconLocation(sl,Path,index);
3181         msi_free(Path);
3182     }
3183
3184     if (!MSI_RecordIsNull(row,11))
3185         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3186
3187     if (!MSI_RecordIsNull(row,12))
3188     {
3189         LPWSTR Path;
3190         buffer = MSI_RecordGetString(row,12);
3191         Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3192         if (Path)
3193             IShellLinkW_SetWorkingDirectory(sl,Path);
3194         msi_free(Path);
3195     }
3196
3197     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3198     IPersistFile_Save(pf,target_file,FALSE);
3199
3200     msi_free(target_file);    
3201
3202 err:
3203     if (pf)
3204         IPersistFile_Release( pf );
3205     if (sl)
3206         IShellLinkW_Release( sl );
3207
3208     return ERROR_SUCCESS;
3209 }
3210
3211 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3212 {
3213     UINT rc;
3214     HRESULT res;
3215     MSIQUERY * view;
3216     static const WCHAR Query[] =
3217         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3218          '`','S','h','o','r','t','c','u','t','`',0};
3219
3220     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3221     if (rc != ERROR_SUCCESS)
3222         return ERROR_SUCCESS;
3223
3224     res = CoInitialize( NULL );
3225     if (FAILED (res))
3226     {
3227         ERR("CoInitialize failed\n");
3228         return ERROR_FUNCTION_FAILED;
3229     }
3230
3231     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3232     msiobj_release(&view->hdr);
3233
3234     CoUninitialize();
3235
3236     return rc;
3237 }
3238
3239 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3240 {
3241     MSIPACKAGE* package = (MSIPACKAGE*)param;
3242     HANDLE the_file;
3243     LPWSTR FilePath;
3244     LPCWSTR FileName;
3245     CHAR buffer[1024];
3246     DWORD sz;
3247     UINT rc;
3248     MSIRECORD *uirow;
3249
3250     FileName = MSI_RecordGetString(row,1);
3251     if (!FileName)
3252     {
3253         ERR("Unable to get FileName\n");
3254         return ERROR_SUCCESS;
3255     }
3256
3257     FilePath = build_icon_path(package,FileName);
3258
3259     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3260
3261     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3262                         FILE_ATTRIBUTE_NORMAL, NULL);
3263
3264     if (the_file == INVALID_HANDLE_VALUE)
3265     {
3266         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3267         msi_free(FilePath);
3268         return ERROR_SUCCESS;
3269     }
3270
3271     do 
3272     {
3273         DWORD write;
3274         sz = 1024;
3275         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3276         if (rc != ERROR_SUCCESS)
3277         {
3278             ERR("Failed to get stream\n");
3279             CloseHandle(the_file);  
3280             DeleteFileW(FilePath);
3281             break;
3282         }
3283         WriteFile(the_file,buffer,sz,&write,NULL);
3284     } while (sz == 1024);
3285
3286     msi_free(FilePath);
3287
3288     CloseHandle(the_file);
3289
3290     uirow = MSI_CreateRecord(1);
3291     MSI_RecordSetStringW(uirow,1,FileName);
3292     ui_actiondata(package,szPublishProduct,uirow);
3293     msiobj_release( &uirow->hdr );
3294
3295     return ERROR_SUCCESS;
3296 }
3297
3298 static BOOL msi_check_publish(MSIPACKAGE *package)
3299 {
3300     MSIFEATURE *feature;
3301
3302     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3303     {
3304         if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3305             return TRUE;
3306     }
3307
3308     return FALSE;
3309 }
3310
3311 /*
3312  * 99% of the work done here is only done for 
3313  * advertised installs. However this is where the
3314  * Icon table is processed and written out
3315  * so that is what I am going to do here.
3316  */
3317 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3318 {
3319     UINT rc;
3320     LPWSTR packname;
3321     MSIQUERY * view;
3322     MSISOURCELISTINFO *info;
3323     MSIMEDIADISK *disk;
3324     static const WCHAR Query[]=
3325         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3326          '`','I','c','o','n','`',0};
3327     /* for registry stuff */
3328     HKEY hkey=0;
3329     HKEY hukey=0;
3330     HKEY hudkey=0, props=0;
3331     HKEY source;
3332     static const WCHAR szProductLanguage[] =
3333         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3334     static const WCHAR szARPProductIcon[] =
3335         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3336     static const WCHAR szProductVersion[] =
3337         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3338     static const WCHAR szSourceList[] =
3339         {'S','o','u','r','c','e','L','i','s','t',0};
3340     static const WCHAR szEmpty[] = {0};
3341     DWORD langid;
3342     LPWSTR buffer;
3343     DWORD size;
3344     MSIHANDLE hDb, hSumInfo;
3345
3346     /* FIXME: also need to publish if the product is in advertise mode */
3347     if (!msi_check_publish(package))
3348         return ERROR_SUCCESS;
3349
3350     /* write out icon files */
3351
3352     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3353     if (rc == ERROR_SUCCESS)
3354     {
3355         MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3356         msiobj_release(&view->hdr);
3357     }
3358
3359     /* ok there is a lot more done here but i need to figure out what */
3360
3361     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3362     {
3363         rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3364         if (rc != ERROR_SUCCESS)
3365             goto end;
3366
3367         rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
3368         if (rc != ERROR_SUCCESS)
3369             goto end;
3370     }
3371     else
3372     {
3373         rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3374         if (rc != ERROR_SUCCESS)
3375             goto end;
3376
3377         rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3378         if (rc != ERROR_SUCCESS)
3379             goto end;
3380
3381         rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
3382         if (rc != ERROR_SUCCESS)
3383             goto end;
3384     }
3385
3386     rc = RegCreateKeyW(hukey, szSourceList, &source);
3387     if (rc != ERROR_SUCCESS)
3388         goto end;
3389
3390     RegCloseKey(source);
3391
3392     rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3393     if (rc != ERROR_SUCCESS)
3394         goto end;
3395
3396     buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3397     msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3398     msi_free(buffer);
3399
3400     langid = msi_get_property_int( package, szProductLanguage, 0 );
3401     msi_reg_set_val_dword( hukey, INSTALLPROPERTY_LANGUAGEW, langid );
3402
3403     packname = strrchrW( package->PackagePath, '\\' ) + 1;
3404     msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGENAMEW, packname );
3405
3406     /* FIXME */
3407     msi_reg_set_val_dword( hukey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0 );
3408     msi_reg_set_val_dword( props, INSTALLPROPERTY_INSTANCETYPEW, 0 );
3409
3410     buffer = msi_dup_property( package, szARPProductIcon );
3411     if (buffer)
3412     {
3413         LPWSTR path = build_icon_path(package,buffer);
3414         msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3415         msi_free( path );
3416     }
3417     msi_free(buffer);
3418
3419     buffer = msi_dup_property( package, szProductVersion );
3420     if (buffer)
3421     {
3422         DWORD verdword = msi_version_str_to_dword(buffer);
3423         msi_reg_set_val_dword( hukey, INSTALLPROPERTY_VERSIONW, verdword );
3424     }
3425     msi_free(buffer);
3426
3427     buffer = strrchrW( package->PackagePath, '\\') + 1;
3428     rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3429                                 package->Context, MSICODE_PRODUCT,
3430                                 INSTALLPROPERTY_PACKAGENAMEW, buffer );
3431     if (rc != ERROR_SUCCESS)
3432         goto end;
3433
3434     rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3435                                 package->Context, MSICODE_PRODUCT,
3436                                 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty );
3437     if (rc != ERROR_SUCCESS)
3438         goto end;
3439
3440     rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3441                                 package->Context, MSICODE_PRODUCT,
3442                                 INSTALLPROPERTY_DISKPROMPTW, szEmpty );
3443     if (rc != ERROR_SUCCESS)
3444         goto end;
3445
3446     /* FIXME: Need to write more keys to the user registry */
3447   
3448     hDb= alloc_msihandle( &package->db->hdr );
3449     if (!hDb) {
3450         rc = ERROR_NOT_ENOUGH_MEMORY;
3451         goto end;
3452     }
3453     rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo); 
3454     MsiCloseHandle(hDb);
3455     if (rc == ERROR_SUCCESS)
3456     {
3457         WCHAR guidbuffer[0x200];
3458         size = 0x200;
3459         rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3460                                         guidbuffer, &size);
3461         if (rc == ERROR_SUCCESS)
3462         {
3463             /* for now we only care about the first guid */
3464             LPWSTR ptr = strchrW(guidbuffer,';');
3465             if (ptr) *ptr = 0;
3466             msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, guidbuffer );
3467         }
3468         else
3469         {
3470             ERR("Unable to query Revision_Number...\n");
3471             rc = ERROR_SUCCESS;
3472         }
3473         MsiCloseHandle(hSumInfo);
3474     }
3475     else
3476     {
3477         ERR("Unable to open Summary Information\n");
3478         rc = ERROR_SUCCESS;
3479     }
3480
3481     /* publish the SourceList info */
3482     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3483     {
3484         if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3485             msi_set_last_used_source(package->ProductCode, NULL, info->context,
3486                                      info->options, info->value);
3487         else
3488             MsiSourceListSetInfoW(package->ProductCode, NULL,
3489                                   info->context, info->options,
3490                                   info->property, info->value);
3491     }
3492
3493     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3494     {
3495         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3496                                    disk->context, disk->options,
3497                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3498     }
3499
3500 end:
3501     RegCloseKey(hkey);
3502     RegCloseKey(hukey);
3503     RegCloseKey(hudkey);
3504     RegCloseKey(props);
3505
3506     return rc;
3507 }
3508
3509 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3510 {
3511     MSIPACKAGE *package = (MSIPACKAGE*)param;
3512     LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3513     LPWSTR deformated_section, deformated_key, deformated_value;
3514     LPWSTR folder, fullname = NULL;
3515     MSIRECORD * uirow;
3516     INT action;
3517     MSICOMPONENT *comp;
3518     static const WCHAR szWindowsFolder[] =
3519           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3520
3521     component = MSI_RecordGetString(row, 8);
3522     comp = get_loaded_component(package,component);
3523
3524     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3525     {
3526         TRACE("Skipping ini file due to disabled component %s\n",
3527                         debugstr_w(component));
3528
3529         comp->Action = comp->Installed;
3530
3531         return ERROR_SUCCESS;
3532     }
3533
3534     comp->Action = INSTALLSTATE_LOCAL;
3535
3536     identifier = MSI_RecordGetString(row,1); 
3537     filename = MSI_RecordGetString(row,2);
3538     dirproperty = MSI_RecordGetString(row,3);
3539     section = MSI_RecordGetString(row,4);
3540     key = MSI_RecordGetString(row,5);
3541     value = MSI_RecordGetString(row,6);
3542     action = MSI_RecordGetInteger(row,7);
3543
3544     deformat_string(package,section,&deformated_section);
3545     deformat_string(package,key,&deformated_key);
3546     deformat_string(package,value,&deformated_value);
3547
3548     if (dirproperty)
3549     {
3550         folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3551         if (!folder)
3552             folder = msi_dup_property( package, dirproperty );
3553     }
3554     else
3555         folder = msi_dup_property( package, szWindowsFolder );
3556
3557     if (!folder)
3558     {
3559         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3560         goto cleanup;
3561     }
3562
3563     fullname = build_directory_name(2, folder, filename);
3564
3565     if (action == 0)
3566     {
3567         TRACE("Adding value %s to section %s in %s\n",
3568                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3569                 debugstr_w(fullname));
3570         WritePrivateProfileStringW(deformated_section, deformated_key,
3571                                    deformated_value, fullname);
3572     }
3573     else if (action == 1)
3574     {
3575         WCHAR returned[10];
3576         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3577                                  returned, 10, fullname);
3578         if (returned[0] == 0)
3579         {
3580             TRACE("Adding value %s to section %s in %s\n",
3581                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3582                     debugstr_w(fullname));
3583
3584             WritePrivateProfileStringW(deformated_section, deformated_key,
3585                                        deformated_value, fullname);
3586         }
3587     }
3588     else if (action == 3)
3589         FIXME("Append to existing section not yet implemented\n");
3590
3591     uirow = MSI_CreateRecord(4);
3592     MSI_RecordSetStringW(uirow,1,identifier);
3593     MSI_RecordSetStringW(uirow,2,deformated_section);
3594     MSI_RecordSetStringW(uirow,3,deformated_key);
3595     MSI_RecordSetStringW(uirow,4,deformated_value);
3596     ui_actiondata(package,szWriteIniValues,uirow);
3597     msiobj_release( &uirow->hdr );
3598 cleanup:
3599     msi_free(fullname);
3600     msi_free(folder);
3601     msi_free(deformated_key);
3602     msi_free(deformated_value);
3603     msi_free(deformated_section);
3604     return ERROR_SUCCESS;
3605 }
3606
3607 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3608 {
3609     UINT rc;
3610     MSIQUERY * view;
3611     static const WCHAR ExecSeqQuery[] = 
3612         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3613          '`','I','n','i','F','i','l','e','`',0};
3614
3615     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3616     if (rc != ERROR_SUCCESS)
3617     {
3618         TRACE("no IniFile table\n");
3619         return ERROR_SUCCESS;
3620     }
3621
3622     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3623     msiobj_release(&view->hdr);
3624     return rc;
3625 }
3626
3627 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3628 {
3629     MSIPACKAGE *package = (MSIPACKAGE*)param;
3630     LPCWSTR filename;
3631     LPWSTR FullName;
3632     MSIFILE *file;
3633     DWORD len;
3634     static const WCHAR ExeStr[] =
3635         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3636     static const WCHAR close[] =  {'\"',0};
3637     STARTUPINFOW si;
3638     PROCESS_INFORMATION info;
3639     BOOL brc;
3640     MSIRECORD *uirow;
3641     LPWSTR uipath, p;
3642
3643     memset(&si,0,sizeof(STARTUPINFOW));
3644
3645     filename = MSI_RecordGetString(row,1);
3646     file = get_loaded_file( package, filename );
3647
3648     if (!file)
3649     {
3650         ERR("Unable to find file id %s\n",debugstr_w(filename));
3651         return ERROR_SUCCESS;
3652     }
3653
3654     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3655
3656     FullName = msi_alloc(len*sizeof(WCHAR));
3657     strcpyW(FullName,ExeStr);
3658     strcatW( FullName, file->TargetPath );
3659     strcatW(FullName,close);
3660
3661     TRACE("Registering %s\n",debugstr_w(FullName));
3662     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3663                     &si, &info);
3664
3665     if (brc)
3666         msi_dialog_check_messages(info.hProcess);
3667
3668     msi_free(FullName);
3669
3670     /* the UI chunk */
3671     uirow = MSI_CreateRecord( 2 );
3672     uipath = strdupW( file->TargetPath );
3673     p = strrchrW(uipath,'\\');
3674     if (p)
3675         p[0]=0;
3676     MSI_RecordSetStringW( uirow, 1, &p[1] );
3677     MSI_RecordSetStringW( uirow, 2, uipath);
3678     ui_actiondata( package, szSelfRegModules, uirow);
3679     msiobj_release( &uirow->hdr );
3680     msi_free( uipath );
3681     /* FIXME: call ui_progress? */
3682
3683     return ERROR_SUCCESS;
3684 }
3685
3686 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3687 {
3688     UINT rc;
3689     MSIQUERY * view;
3690     static const WCHAR ExecSeqQuery[] = 
3691         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3692          '`','S','e','l','f','R','e','g','`',0};
3693
3694     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3695     if (rc != ERROR_SUCCESS)
3696     {
3697         TRACE("no SelfReg table\n");
3698         return ERROR_SUCCESS;
3699     }
3700
3701     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3702     msiobj_release(&view->hdr);
3703
3704     return ERROR_SUCCESS;
3705 }
3706
3707 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3708 {
3709     MSIFEATURE *feature;
3710     UINT rc;
3711     HKEY hkey=0;
3712     HKEY hukey=0;
3713     HKEY userdata=0;
3714
3715     if (!msi_check_publish(package))
3716         return ERROR_SUCCESS;
3717
3718     rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3719     if (rc != ERROR_SUCCESS)
3720         goto end;
3721
3722     rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3723     if (rc != ERROR_SUCCESS)
3724         goto end;
3725
3726     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3727     if (rc != ERROR_SUCCESS)
3728         goto end;
3729
3730     /* here the guids are base 85 encoded */
3731     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3732     {
3733         ComponentList *cl;
3734         LPWSTR data = NULL;
3735         GUID clsid;
3736         INT size;
3737         BOOL absent = FALSE;
3738         MSIRECORD *uirow;
3739
3740         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3741             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3742             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3743             absent = TRUE;
3744
3745         size = 1;
3746         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3747         {
3748             size += 21;
3749         }
3750         if (feature->Feature_Parent)
3751             size += strlenW( feature->Feature_Parent )+2;
3752
3753         data = msi_alloc(size * sizeof(WCHAR));
3754
3755         data[0] = 0;
3756         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3757         {
3758             MSICOMPONENT* component = cl->component;
3759             WCHAR buf[21];
3760
3761             buf[0] = 0;
3762             if (component->ComponentId)
3763             {
3764                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3765                 CLSIDFromString(component->ComponentId, &clsid);
3766                 encode_base85_guid(&clsid,buf);
3767                 TRACE("to %s\n",debugstr_w(buf));
3768                 strcatW(data,buf);
3769             }
3770         }
3771
3772         if (feature->Feature_Parent)
3773         {
3774             static const WCHAR sep[] = {'\2',0};
3775             strcatW(data,sep);
3776             strcatW(data,feature->Feature_Parent);
3777         }
3778
3779         msi_reg_set_val_str( hkey, feature->Feature, data );
3780         msi_reg_set_val_str( userdata, feature->Feature, data );
3781         msi_free(data);
3782
3783         size = 0;
3784         if (feature->Feature_Parent)
3785             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3786         if (!absent)
3787         {
3788             static const WCHAR emptyW[] = {0};
3789             size += sizeof(WCHAR);
3790             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3791                            (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3792         }
3793         else
3794         {
3795             size += 2*sizeof(WCHAR);
3796             data = msi_alloc(size);
3797             data[0] = 0x6;
3798             data[1] = 0;
3799             if (feature->Feature_Parent)
3800                 strcpyW( &data[1], feature->Feature_Parent );
3801             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3802                        (LPBYTE)data,size);
3803             msi_free(data);
3804         }
3805
3806         /* the UI chunk */
3807         uirow = MSI_CreateRecord( 1 );
3808         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3809         ui_actiondata( package, szPublishFeatures, uirow);
3810         msiobj_release( &uirow->hdr );
3811         /* FIXME: call ui_progress? */
3812     }
3813
3814 end:
3815     RegCloseKey(hkey);
3816     RegCloseKey(hukey);
3817     return rc;
3818 }
3819
3820 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3821 {
3822     UINT r;
3823     HKEY hkey;
3824
3825     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3826
3827     r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3828     if (r == ERROR_SUCCESS)
3829     {
3830         RegDeleteValueW(hkey, feature->Feature);
3831         RegCloseKey(hkey);
3832     }
3833
3834     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3835     if (r == ERROR_SUCCESS)
3836     {
3837         RegDeleteValueW(hkey, feature->Feature);
3838         RegCloseKey(hkey);
3839     }
3840
3841     return ERROR_SUCCESS;
3842 }
3843
3844 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3845 {
3846     MSIFEATURE *feature;
3847
3848     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3849     {
3850         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3851             return FALSE;
3852     }
3853
3854     return TRUE;
3855 }
3856
3857 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3858 {
3859     MSIFEATURE *feature;
3860
3861     if (!msi_check_unpublish(package))
3862         return ERROR_SUCCESS;
3863
3864     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3865     {
3866         msi_unpublish_feature(package, feature);
3867     }
3868
3869     return ERROR_SUCCESS;
3870 }
3871
3872 static UINT msi_get_local_package_name( LPWSTR path )
3873 {
3874     static const WCHAR szInstaller[] = {
3875         '\\','I','n','s','t','a','l','l','e','r','\\',0};
3876     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3877     DWORD time, len, i;
3878     HANDLE handle;
3879
3880     time = GetTickCount();
3881     GetWindowsDirectoryW( path, MAX_PATH );
3882     lstrcatW( path, szInstaller );
3883     CreateDirectoryW( path, NULL );
3884
3885     len = lstrlenW(path);
3886     for (i=0; i<0x10000; i++)
3887     {
3888         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3889         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3890                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3891         if (handle != INVALID_HANDLE_VALUE)
3892         {
3893             CloseHandle(handle);
3894             break;
3895         }
3896         if (GetLastError() != ERROR_FILE_EXISTS &&
3897             GetLastError() != ERROR_SHARING_VIOLATION)
3898             return ERROR_FUNCTION_FAILED;
3899     }
3900
3901     return ERROR_SUCCESS;
3902 }
3903
3904 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3905 {
3906     WCHAR packagefile[MAX_PATH];
3907     HKEY props;
3908     UINT r;
3909
3910     r = msi_get_local_package_name( packagefile );
3911     if (r != ERROR_SUCCESS)
3912         return r;
3913
3914     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3915
3916     r = CopyFileW( package->db->path, packagefile, FALSE);
3917
3918     if (!r)
3919     {
3920         ERR("Unable to copy package (%s -> %s) (error %d)\n",
3921             debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3922         return ERROR_FUNCTION_FAILED;
3923     }
3924
3925     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3926
3927     r = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
3928     if (r != ERROR_SUCCESS)
3929         return r;
3930
3931     msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3932     RegCloseKey(props);
3933     return ERROR_SUCCESS;
3934 }
3935
3936 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3937 {
3938     LPWSTR prop, val, key;
3939     static const LPCSTR propval[] = {
3940         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3941         "ARPCONTACT",             "Contact",
3942         "ARPCOMMENTS",            "Comments",
3943         "ProductName",            "DisplayName",
3944         "ProductVersion",         "DisplayVersion",
3945         "ARPHELPLINK",            "HelpLink",
3946         "ARPHELPTELEPHONE",       "HelpTelephone",
3947         "ARPINSTALLLOCATION",     "InstallLocation",
3948         "SourceDir",              "InstallSource",
3949         "Manufacturer",           "Publisher",
3950         "ARPREADME",              "Readme",
3951         "ARPSIZE",                "Size",
3952         "ARPURLINFOABOUT",        "URLInfoAbout",
3953         "ARPURLUPDATEINFO",       "URLUpdateInfo",
3954         NULL,
3955     };
3956     const LPCSTR *p = propval;
3957
3958     while( *p )
3959     {
3960         prop = strdupAtoW( *p++ );
3961         key = strdupAtoW( *p++ );
3962         val = msi_dup_property( package, prop );
3963         msi_reg_set_val_str( hkey, key, val );
3964         msi_free(val);
3965         msi_free(key);
3966         msi_free(prop);
3967     }
3968     return ERROR_SUCCESS;
3969 }
3970
3971 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3972 {
3973     HKEY hkey=0;
3974     HKEY hudkey=0, props=0;
3975     LPWSTR buffer = NULL;
3976     UINT rc;
3977     DWORD size, langid;
3978     static const WCHAR szWindowsInstaller[] = 
3979         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3980     static const WCHAR szUpgradeCode[] = 
3981         {'U','p','g','r','a','d','e','C','o','d','e',0};
3982     static const WCHAR modpath_fmt[] = 
3983         {'M','s','i','E','x','e','c','.','e','x','e',' ',
3984          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3985     static const WCHAR szModifyPath[] = 
3986         {'M','o','d','i','f','y','P','a','t','h',0};
3987     static const WCHAR szUninstallString[] = 
3988         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3989     static const WCHAR szEstimatedSize[] = 
3990         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3991     static const WCHAR szProductLanguage[] =
3992         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3993     static const WCHAR szProductVersion[] =
3994         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3995     static const WCHAR szProductName[] =
3996         {'P','r','o','d','u','c','t','N','a','m','e',0};
3997     static const WCHAR szDisplayName[] =
3998         {'D','i','s','p','l','a','y','N','a','m','e',0};
3999     static const WCHAR szDisplayVersion[] =
4000         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4001     static const WCHAR szManufacturer[] =
4002         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4003
4004     SYSTEMTIME systime;
4005     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4006     LPWSTR upgrade_code;
4007     WCHAR szDate[9];
4008
4009     /* FIXME: also need to publish if the product is in advertise mode */
4010     if (!msi_check_publish(package))
4011         return ERROR_SUCCESS;
4012
4013     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4014     if (rc != ERROR_SUCCESS)
4015         return rc;
4016
4017     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4018     {
4019         rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4020         if (rc != ERROR_SUCCESS)
4021             return rc;
4022     }
4023     else
4024     {
4025         rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4026         if (rc != ERROR_SUCCESS)
4027             return rc;
4028     }
4029
4030     /* dump all the info i can grab */
4031     /* FIXME: Flesh out more information */
4032
4033     msi_write_uninstall_property_vals( package, hkey );
4034
4035     msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4036     
4037     msi_make_package_local( package, hkey );
4038
4039     /* do ModifyPath and UninstallString */
4040     size = deformat_string(package,modpath_fmt,&buffer);
4041     RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4042     RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4043     msi_free(buffer);
4044
4045     /* FIXME: Write real Estimated Size when we have it */
4046     msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4047
4048     buffer = msi_dup_property( package, szProductName );
4049     msi_reg_set_val_str( props, szDisplayName, buffer );
4050     msi_free(buffer);
4051
4052     buffer = msi_dup_property( package, cszSourceDir );
4053     msi_reg_set_val_str( props, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4054     msi_free(buffer);
4055
4056     buffer = msi_dup_property( package, szManufacturer );
4057     msi_reg_set_val_str( props, INSTALLPROPERTY_PUBLISHERW, buffer);
4058     msi_free(buffer);
4059    
4060     GetLocalTime(&systime);
4061     sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4062     msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4063     msi_reg_set_val_str( props, INSTALLPROPERTY_INSTALLDATEW, szDate );
4064    
4065     langid = msi_get_property_int( package, szProductLanguage, 0 );
4066     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4067
4068     buffer = msi_dup_property( package, szProductVersion );
4069     msi_reg_set_val_str( props, szDisplayVersion, buffer );
4070     if (buffer)
4071     {
4072         DWORD verdword = msi_version_str_to_dword(buffer);
4073
4074         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4075         msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONW, verdword );
4076         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4077         msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4078         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4079         msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4080     }
4081     msi_free(buffer);
4082     
4083     /* Handle Upgrade Codes */
4084     upgrade_code = msi_dup_property( package, szUpgradeCode );
4085     if (upgrade_code)
4086     {
4087         HKEY hkey2;
4088         WCHAR squashed[33];
4089         MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4090         squash_guid(package->ProductCode,squashed);
4091         msi_reg_set_val_str( hkey2, squashed, NULL );
4092         RegCloseKey(hkey2);
4093         MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4094         squash_guid(package->ProductCode,squashed);
4095         msi_reg_set_val_str( hkey2, squashed, NULL );
4096         RegCloseKey(hkey2);
4097
4098         msi_free(upgrade_code);
4099     }
4100
4101     RegCloseKey(hkey);
4102
4103     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4104     if (rc != ERROR_SUCCESS)
4105         return rc;
4106
4107     RegCloseKey(hudkey);
4108
4109     msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4110     RegCloseKey(props);
4111
4112     return ERROR_SUCCESS;
4113 }
4114
4115 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4116 {
4117     return execute_script(package,INSTALL_SCRIPT);
4118 }
4119
4120 static UINT msi_unpublish_product(MSIPACKAGE *package)
4121 {
4122     LPWSTR remove = NULL;
4123     LPWSTR *features = NULL;
4124     BOOL full_uninstall = TRUE;
4125     MSIFEATURE *feature;
4126
4127     static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4128     static const WCHAR szAll[] = {'A','L','L',0};
4129
4130     remove = msi_dup_property(package, szRemove);
4131     if (!remove)
4132         return ERROR_SUCCESS;
4133
4134     features = msi_split_string(remove, ',');
4135     if (!features)
4136     {
4137         msi_free(remove);
4138         ERR("REMOVE feature list is empty!\n");
4139         return ERROR_FUNCTION_FAILED;
4140     }
4141
4142     if (!lstrcmpW(features[0], szAll))
4143         full_uninstall = TRUE;
4144     else
4145     {
4146         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4147         {
4148             if (feature->Action != INSTALLSTATE_ABSENT)
4149                 full_uninstall = FALSE;
4150         }
4151     }
4152
4153     if (!full_uninstall)
4154         goto done;
4155
4156     MSIREG_DeleteProductKey(package->ProductCode);
4157     MSIREG_DeleteUserProductKey(package->ProductCode);
4158     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4159     MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4160     MSIREG_DeleteUninstallKey(package->ProductCode);
4161
4162 done:
4163     msi_free(remove);
4164     msi_free(features);
4165     return ERROR_SUCCESS;
4166 }
4167
4168 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4169 {
4170     UINT rc;
4171
4172     rc = msi_unpublish_product(package);
4173     if (rc != ERROR_SUCCESS)
4174         return rc;
4175
4176     /* turn off scheduling */
4177     package->script->CurrentlyScripting= FALSE;
4178
4179     /* first do the same as an InstallExecute */
4180     rc = ACTION_InstallExecute(package);
4181     if (rc != ERROR_SUCCESS)
4182         return rc;
4183
4184     /* then handle Commit Actions */
4185     rc = execute_script(package,COMMIT_SCRIPT);
4186
4187     return rc;
4188 }
4189
4190 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4191 {
4192     static const WCHAR RunOnce[] = {
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     'R','u','n','O','n','c','e',0};
4198     static const WCHAR InstallRunOnce[] = {
4199     'S','o','f','t','w','a','r','e','\\',
4200     'M','i','c','r','o','s','o','f','t','\\',
4201     'W','i','n','d','o','w','s','\\',
4202     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4203     'I','n','s','t','a','l','l','e','r','\\',
4204     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4205
4206     static const WCHAR msiexec_fmt[] = {
4207     '%','s',
4208     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4209     '\"','%','s','\"',0};
4210     static const WCHAR install_fmt[] = {
4211     '/','I',' ','\"','%','s','\"',' ',
4212     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4213     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4214     WCHAR buffer[256], sysdir[MAX_PATH];
4215     HKEY hkey;
4216     WCHAR squished_pc[100];
4217
4218     squash_guid(package->ProductCode,squished_pc);
4219
4220     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4221     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4222     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4223      squished_pc);
4224
4225     msi_reg_set_val_str( hkey, squished_pc, buffer );
4226     RegCloseKey(hkey);
4227
4228     TRACE("Reboot command %s\n",debugstr_w(buffer));
4229
4230     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4231     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4232
4233     msi_reg_set_val_str( hkey, squished_pc, buffer );
4234     RegCloseKey(hkey);
4235
4236     return ERROR_INSTALL_SUSPEND;
4237 }
4238
4239 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4240 {
4241     DWORD attrib;
4242     UINT rc;
4243
4244     /*
4245      * We are currently doing what should be done here in the top level Install
4246      * however for Administrative and uninstalls this step will be needed
4247      */
4248     if (!package->PackagePath)
4249         return ERROR_SUCCESS;
4250
4251     msi_set_sourcedir_props(package, TRUE);
4252
4253     attrib = GetFileAttributesW(package->db->path);
4254     if (attrib == INVALID_FILE_ATTRIBUTES)
4255     {
4256         LPWSTR prompt;
4257         LPWSTR msg;
4258         DWORD size = 0;
4259
4260         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4261                 package->Context, MSICODE_PRODUCT,
4262                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4263         if (rc == ERROR_MORE_DATA)
4264         {
4265             prompt = msi_alloc(size * sizeof(WCHAR));
4266             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4267                     package->Context, MSICODE_PRODUCT,
4268                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4269         }
4270         else
4271             prompt = strdupW(package->db->path);
4272
4273         msg = generate_error_string(package,1302,1,prompt);
4274         while(attrib == INVALID_FILE_ATTRIBUTES)
4275         {
4276             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4277             if (rc == IDCANCEL)
4278             {
4279                 rc = ERROR_INSTALL_USEREXIT;
4280                 break;
4281             }
4282             attrib = GetFileAttributesW(package->db->path);
4283         }
4284         msi_free(prompt);
4285         rc = ERROR_SUCCESS;
4286     }
4287     else
4288         return ERROR_SUCCESS;
4289
4290     return rc;
4291 }
4292
4293 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4294 {
4295     HKEY hkey=0;
4296     LPWSTR buffer;
4297     LPWSTR productid;
4298     UINT rc,i;
4299
4300     static const WCHAR szPropKeys[][80] = 
4301     {
4302         {'P','r','o','d','u','c','t','I','D',0},
4303         {'U','S','E','R','N','A','M','E',0},
4304         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4305         {0},
4306     };
4307
4308     static const WCHAR szRegKeys[][80] = 
4309     {
4310         {'P','r','o','d','u','c','t','I','D',0},
4311         {'R','e','g','O','w','n','e','r',0},
4312         {'R','e','g','C','o','m','p','a','n','y',0},
4313         {0},
4314     };
4315
4316     if (msi_check_unpublish(package))
4317     {
4318         MSIREG_DeleteUserDataProductKey(package->ProductCode);
4319         return ERROR_SUCCESS;
4320     }
4321
4322     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4323     if (!productid)
4324         return ERROR_SUCCESS;
4325
4326     rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4327     if (rc != ERROR_SUCCESS)
4328         goto end;
4329
4330     for( i = 0; szPropKeys[i][0]; i++ )
4331     {
4332         buffer = msi_dup_property( package, szPropKeys[i] );
4333         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4334         msi_free( buffer );
4335     }
4336
4337 end:
4338     msi_free(productid);
4339     RegCloseKey(hkey);
4340
4341     /* FIXME: call ui_actiondata */
4342
4343     return rc;
4344 }
4345
4346
4347 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4348 {
4349     UINT rc;
4350
4351     package->script->InWhatSequence |= SEQUENCE_EXEC;
4352     rc = ACTION_ProcessExecSequence(package,FALSE);
4353     return rc;
4354 }
4355
4356
4357 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4358 {
4359     MSIPACKAGE *package = (MSIPACKAGE*)param;
4360     LPCWSTR compgroupid=NULL;
4361     LPCWSTR feature=NULL;
4362     LPCWSTR text = NULL;
4363     LPCWSTR qualifier = NULL;
4364     LPCWSTR component = NULL;
4365     LPWSTR advertise = NULL;
4366     LPWSTR output = NULL;
4367     HKEY hkey;
4368     UINT rc = ERROR_SUCCESS;
4369     MSICOMPONENT *comp;
4370     DWORD sz = 0;
4371     MSIRECORD *uirow;
4372
4373     component = MSI_RecordGetString(rec,3);
4374     comp = get_loaded_component(package,component);
4375
4376     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4377        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4378        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4379     {
4380         TRACE("Skipping: Component %s not scheduled for install\n",
4381                         debugstr_w(component));
4382
4383         return ERROR_SUCCESS;
4384     }
4385
4386     compgroupid = MSI_RecordGetString(rec,1);
4387     qualifier = MSI_RecordGetString(rec,2);
4388
4389     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4390     if (rc != ERROR_SUCCESS)
4391         goto end;
4392     
4393     text = MSI_RecordGetString(rec,4);
4394     feature = MSI_RecordGetString(rec,5);
4395   
4396     advertise = create_component_advertise_string(package, comp, feature);
4397
4398     sz = strlenW(advertise);
4399
4400     if (text)
4401         sz += lstrlenW(text);
4402
4403     sz+=3;
4404     sz *= sizeof(WCHAR);
4405            
4406     output = msi_alloc_zero(sz);
4407     strcpyW(output,advertise);
4408     msi_free(advertise);
4409
4410     if (text)
4411         strcatW(output,text);
4412
4413     msi_reg_set_val_multi_str( hkey, qualifier, output );
4414     
4415 end:
4416     RegCloseKey(hkey);
4417     msi_free(output);
4418
4419     /* the UI chunk */
4420     uirow = MSI_CreateRecord( 2 );
4421     MSI_RecordSetStringW( uirow, 1, compgroupid );
4422     MSI_RecordSetStringW( uirow, 2, qualifier);
4423     ui_actiondata( package, szPublishComponents, uirow);
4424     msiobj_release( &uirow->hdr );
4425     /* FIXME: call ui_progress? */
4426
4427     return rc;
4428 }
4429
4430 /*
4431  * At present I am ignorning the advertised components part of this and only
4432  * focusing on the qualified component sets
4433  */
4434 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4435 {
4436     UINT rc;
4437     MSIQUERY * view;
4438     static const WCHAR ExecSeqQuery[] =
4439         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4440          '`','P','u','b','l','i','s','h',
4441          'C','o','m','p','o','n','e','n','t','`',0};
4442     
4443     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4444     if (rc != ERROR_SUCCESS)
4445         return ERROR_SUCCESS;
4446
4447     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4448     msiobj_release(&view->hdr);
4449
4450     return rc;
4451 }
4452
4453 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4454 {
4455     MSIPACKAGE *package = (MSIPACKAGE*)param;
4456     MSIRECORD *row;
4457     MSIFILE *file;
4458     SC_HANDLE hscm, service = NULL;
4459     LPCWSTR comp, depends, pass;
4460     LPWSTR name = NULL, disp = NULL;
4461     LPCWSTR load_order, serv_name, key;
4462     DWORD serv_type, start_type;
4463     DWORD err_control;
4464
4465     static const WCHAR query[] =
4466         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4467          '`','C','o','m','p','o','n','e','n','t','`',' ',
4468          'W','H','E','R','E',' ',
4469          '`','C','o','m','p','o','n','e','n','t','`',' ',
4470          '=','\'','%','s','\'',0};
4471
4472     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4473     if (!hscm)
4474     {
4475         ERR("Failed to open the SC Manager!\n");
4476         goto done;
4477     }
4478
4479     start_type = MSI_RecordGetInteger(rec, 5);
4480     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4481         goto done;
4482
4483     depends = MSI_RecordGetString(rec, 8);
4484     if (depends && *depends)
4485         FIXME("Dependency list unhandled!\n");
4486
4487     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4488     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4489     serv_type = MSI_RecordGetInteger(rec, 4);
4490     err_control = MSI_RecordGetInteger(rec, 6);
4491     load_order = MSI_RecordGetString(rec, 7);
4492     serv_name = MSI_RecordGetString(rec, 9);
4493     pass = MSI_RecordGetString(rec, 10);
4494     comp = MSI_RecordGetString(rec, 12);
4495
4496     /* fetch the service path */
4497     row = MSI_QueryGetRecord(package->db, query, comp);
4498     if (!row)
4499     {
4500         ERR("Control query failed!\n");
4501         goto done;
4502     }
4503
4504     key = MSI_RecordGetString(row, 6);
4505
4506     file = get_loaded_file(package, key);
4507     msiobj_release(&row->hdr);
4508     if (!file)
4509     {
4510         ERR("Failed to load the service file\n");
4511         goto done;
4512     }
4513
4514     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4515                              start_type, err_control, file->TargetPath,
4516                              load_order, NULL, NULL, serv_name, pass);
4517     if (!service)
4518     {
4519         if (GetLastError() != ERROR_SERVICE_EXISTS)
4520             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4521     }
4522
4523 done:
4524     CloseServiceHandle(service);
4525     CloseServiceHandle(hscm);
4526     msi_free(name);
4527     msi_free(disp);
4528
4529     return ERROR_SUCCESS;
4530 }
4531
4532 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4533 {
4534     UINT rc;
4535     MSIQUERY * view;
4536     static const WCHAR ExecSeqQuery[] =
4537         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4538          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4539     
4540     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4541     if (rc != ERROR_SUCCESS)
4542         return ERROR_SUCCESS;
4543
4544     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4545     msiobj_release(&view->hdr);
4546
4547     return rc;
4548 }
4549
4550 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4551 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4552 {
4553     LPCWSTR *vector, *temp_vector;
4554     LPWSTR p, q;
4555     DWORD sep_len;
4556
4557     static const WCHAR separator[] = {'[','~',']',0};
4558
4559     *numargs = 0;
4560     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4561
4562     if (!args)
4563         return NULL;
4564
4565     vector = msi_alloc(sizeof(LPWSTR));
4566     if (!vector)
4567         return NULL;
4568
4569     p = args;
4570     do
4571     {
4572         (*numargs)++;
4573         vector[*numargs - 1] = p;
4574
4575         if ((q = strstrW(p, separator)))
4576         {
4577             *q = '\0';
4578
4579             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4580             if (!temp_vector)
4581             {
4582                 msi_free(vector);
4583                 return NULL;
4584             }
4585             vector = temp_vector;
4586
4587             p = q + sep_len;
4588         }
4589     } while (q);
4590
4591     return vector;
4592 }
4593
4594 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4595 {
4596     MSIPACKAGE *package = (MSIPACKAGE *)param;
4597     MSICOMPONENT *comp;
4598     SC_HANDLE scm, service = NULL;
4599     LPCWSTR name, *vector = NULL;
4600     LPWSTR args;
4601     DWORD event, numargs;
4602     UINT r = ERROR_FUNCTION_FAILED;
4603
4604     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4605     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4606         return ERROR_SUCCESS;
4607
4608     name = MSI_RecordGetString(rec, 2);
4609     event = MSI_RecordGetInteger(rec, 3);
4610     args = strdupW(MSI_RecordGetString(rec, 4));
4611
4612     if (!(event & msidbServiceControlEventStart))
4613         return ERROR_SUCCESS;
4614
4615     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4616     if (!scm)
4617     {
4618         ERR("Failed to open the service control manager\n");
4619         goto done;
4620     }
4621
4622     service = OpenServiceW(scm, name, SERVICE_START);
4623     if (!service)
4624     {
4625         ERR("Failed to open service %s\n", debugstr_w(name));
4626         goto done;
4627     }
4628
4629     vector = msi_service_args_to_vector(args, &numargs);
4630
4631     if (!StartServiceW(service, numargs, vector))
4632     {
4633         ERR("Failed to start service %s\n", debugstr_w(name));
4634         goto done;
4635     }
4636
4637     r = ERROR_SUCCESS;
4638
4639 done:
4640     CloseServiceHandle(service);
4641     CloseServiceHandle(scm);
4642
4643     msi_free(args);
4644     msi_free(vector);
4645     return r;
4646 }
4647
4648 static UINT ACTION_StartServices( MSIPACKAGE *package )
4649 {
4650     UINT rc;
4651     MSIQUERY *view;
4652
4653     static const WCHAR query[] = {
4654         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4655         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4656
4657     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4658     if (rc != ERROR_SUCCESS)
4659         return ERROR_SUCCESS;
4660
4661     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4662     msiobj_release(&view->hdr);
4663
4664     return rc;
4665 }
4666
4667 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4668 {
4669     DWORD i, needed, count;
4670     ENUM_SERVICE_STATUSW *dependencies;
4671     SERVICE_STATUS ss;
4672     SC_HANDLE depserv;
4673
4674     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4675                                0, &needed, &count))
4676         return TRUE;
4677
4678     if (GetLastError() != ERROR_MORE_DATA)
4679         return FALSE;
4680
4681     dependencies = msi_alloc(needed);
4682     if (!dependencies)
4683         return FALSE;
4684
4685     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4686                                 needed, &needed, &count))
4687         goto error;
4688
4689     for (i = 0; i < count; i++)
4690     {
4691         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4692                                SERVICE_STOP | SERVICE_QUERY_STATUS);
4693         if (!depserv)
4694             goto error;
4695
4696         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4697             goto error;
4698     }
4699
4700     return TRUE;
4701
4702 error:
4703     msi_free(dependencies);
4704     return FALSE;
4705 }
4706
4707 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4708 {
4709     MSIPACKAGE *package = (MSIPACKAGE *)param;
4710     MSICOMPONENT *comp;
4711     SERVICE_STATUS status;
4712     SERVICE_STATUS_PROCESS ssp;
4713     SC_HANDLE scm = NULL, service = NULL;
4714     LPWSTR name, args;
4715     DWORD event, needed;
4716
4717     event = MSI_RecordGetInteger(rec, 3);
4718     if (!(event & msidbServiceControlEventStop))
4719         return ERROR_SUCCESS;
4720
4721     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4722     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4723         return ERROR_SUCCESS;
4724
4725     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4726     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4727     args = strdupW(MSI_RecordGetString(rec, 4));
4728
4729     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4730     if (!scm)
4731     {
4732         WARN("Failed to open the SCM: %d\n", GetLastError());
4733         goto done;
4734     }
4735
4736     service = OpenServiceW(scm, name,
4737                            SERVICE_STOP |
4738                            SERVICE_QUERY_STATUS |
4739                            SERVICE_ENUMERATE_DEPENDENTS);
4740     if (!service)
4741     {
4742         WARN("Failed to open service (%s): %d\n",
4743               debugstr_w(name), GetLastError());
4744         goto done;
4745     }
4746
4747     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4748                               sizeof(SERVICE_STATUS_PROCESS), &needed))
4749     {
4750         WARN("Failed to query service status (%s): %d\n",
4751              debugstr_w(name), GetLastError());
4752         goto done;
4753     }
4754
4755     if (ssp.dwCurrentState == SERVICE_STOPPED)
4756         goto done;
4757
4758     stop_service_dependents(scm, service);
4759
4760     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4761         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4762
4763 done:
4764     CloseServiceHandle(service);
4765     CloseServiceHandle(scm);
4766     msi_free(name);
4767     msi_free(args);
4768
4769     return ERROR_SUCCESS;
4770 }
4771
4772 static UINT ACTION_StopServices( MSIPACKAGE *package )
4773 {
4774     UINT rc;
4775     MSIQUERY *view;
4776
4777     static const WCHAR query[] = {
4778         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4779         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4780
4781     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4782     if (rc != ERROR_SUCCESS)
4783         return ERROR_SUCCESS;
4784
4785     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4786     msiobj_release(&view->hdr);
4787
4788     return rc;
4789 }
4790
4791 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4792 {
4793     MSIFILE *file;
4794
4795     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4796     {
4797         if (!lstrcmpW(file->File, filename))
4798             return file;
4799     }
4800
4801     return NULL;
4802 }
4803
4804 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4805 {
4806     MSIPACKAGE *package = (MSIPACKAGE*)param;
4807     LPWSTR driver, driver_path, ptr;
4808     WCHAR outpath[MAX_PATH];
4809     MSIFILE *driver_file, *setup_file;
4810     LPCWSTR desc;
4811     DWORD len, usage;
4812     UINT r = ERROR_SUCCESS;
4813
4814     static const WCHAR driver_fmt[] = {
4815         'D','r','i','v','e','r','=','%','s',0};
4816     static const WCHAR setup_fmt[] = {
4817         'S','e','t','u','p','=','%','s',0};
4818     static const WCHAR usage_fmt[] = {
4819         'F','i','l','e','U','s','a','g','e','=','1',0};
4820
4821     desc = MSI_RecordGetString(rec, 3);
4822
4823     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4824     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4825
4826     if (!driver_file || !setup_file)
4827     {
4828         ERR("ODBC Driver entry not found!\n");
4829         return ERROR_FUNCTION_FAILED;
4830     }
4831
4832     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4833           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4834           lstrlenW(usage_fmt) + 1;
4835     driver = msi_alloc(len * sizeof(WCHAR));
4836     if (!driver)
4837         return ERROR_OUTOFMEMORY;
4838
4839     ptr = driver;
4840     lstrcpyW(ptr, desc);
4841     ptr += lstrlenW(ptr) + 1;
4842
4843     sprintfW(ptr, driver_fmt, driver_file->FileName);
4844     ptr += lstrlenW(ptr) + 1;
4845
4846     sprintfW(ptr, setup_fmt, setup_file->FileName);
4847     ptr += lstrlenW(ptr) + 1;
4848
4849     lstrcpyW(ptr, usage_fmt);
4850     ptr += lstrlenW(ptr) + 1;
4851     *ptr = '\0';
4852
4853     driver_path = strdupW(driver_file->TargetPath);
4854     ptr = strrchrW(driver_path, '\\');
4855     if (ptr) *ptr = '\0';
4856
4857     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4858                              NULL, ODBC_INSTALL_COMPLETE, &usage))
4859     {
4860         ERR("Failed to install SQL driver!\n");
4861         r = ERROR_FUNCTION_FAILED;
4862     }
4863
4864     msi_free(driver);
4865     msi_free(driver_path);
4866
4867     return r;
4868 }
4869
4870 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4871 {
4872     MSIPACKAGE *package = (MSIPACKAGE*)param;
4873     LPWSTR translator, translator_path, ptr;
4874     WCHAR outpath[MAX_PATH];
4875     MSIFILE *translator_file, *setup_file;
4876     LPCWSTR desc;
4877     DWORD len, usage;
4878     UINT r = ERROR_SUCCESS;
4879
4880     static const WCHAR translator_fmt[] = {
4881         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4882     static const WCHAR setup_fmt[] = {
4883         'S','e','t','u','p','=','%','s',0};
4884
4885     desc = MSI_RecordGetString(rec, 3);
4886
4887     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4888     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4889
4890     if (!translator_file || !setup_file)
4891     {
4892         ERR("ODBC Translator entry not found!\n");
4893         return ERROR_FUNCTION_FAILED;
4894     }
4895
4896     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4897           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4898     translator = msi_alloc(len * sizeof(WCHAR));
4899     if (!translator)
4900         return ERROR_OUTOFMEMORY;
4901
4902     ptr = translator;
4903     lstrcpyW(ptr, desc);
4904     ptr += lstrlenW(ptr) + 1;
4905
4906     sprintfW(ptr, translator_fmt, translator_file->FileName);
4907     ptr += lstrlenW(ptr) + 1;
4908
4909     sprintfW(ptr, setup_fmt, setup_file->FileName);
4910     ptr += lstrlenW(ptr) + 1;
4911     *ptr = '\0';
4912
4913     translator_path = strdupW(translator_file->TargetPath);
4914     ptr = strrchrW(translator_path, '\\');
4915     if (ptr) *ptr = '\0';
4916
4917     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4918                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
4919     {
4920         ERR("Failed to install SQL translator!\n");
4921         r = ERROR_FUNCTION_FAILED;
4922     }
4923
4924     msi_free(translator);
4925     msi_free(translator_path);
4926
4927     return r;
4928 }
4929
4930 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4931 {
4932     LPWSTR attrs;
4933     LPCWSTR desc, driver;
4934     WORD request = ODBC_ADD_SYS_DSN;
4935     INT registration;
4936     DWORD len;
4937     UINT r = ERROR_SUCCESS;
4938
4939     static const WCHAR attrs_fmt[] = {
4940         'D','S','N','=','%','s',0 };
4941
4942     desc = MSI_RecordGetString(rec, 3);
4943     driver = MSI_RecordGetString(rec, 4);
4944     registration = MSI_RecordGetInteger(rec, 5);
4945
4946     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4947     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4948
4949     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4950     attrs = msi_alloc(len * sizeof(WCHAR));
4951     if (!attrs)
4952         return ERROR_OUTOFMEMORY;
4953
4954     sprintfW(attrs, attrs_fmt, desc);
4955     attrs[len - 1] = '\0';
4956
4957     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4958     {
4959         ERR("Failed to install SQL data source!\n");
4960         r = ERROR_FUNCTION_FAILED;
4961     }
4962
4963     msi_free(attrs);
4964
4965     return r;
4966 }
4967
4968 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4969 {
4970     UINT rc;
4971     MSIQUERY *view;
4972
4973     static const WCHAR driver_query[] = {
4974         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4975         'O','D','B','C','D','r','i','v','e','r',0 };
4976
4977     static const WCHAR translator_query[] = {
4978         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4979         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4980
4981     static const WCHAR source_query[] = {
4982         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4983         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4984
4985     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4986     if (rc != ERROR_SUCCESS)
4987         return ERROR_SUCCESS;
4988
4989     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4990     msiobj_release(&view->hdr);
4991
4992     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4993     if (rc != ERROR_SUCCESS)
4994         return ERROR_SUCCESS;
4995
4996     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4997     msiobj_release(&view->hdr);
4998
4999     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5000     if (rc != ERROR_SUCCESS)
5001         return ERROR_SUCCESS;
5002
5003     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5004     msiobj_release(&view->hdr);
5005
5006     return rc;
5007 }
5008
5009 #define ENV_ACT_SETALWAYS   0x1
5010 #define ENV_ACT_SETABSENT   0x2
5011 #define ENV_ACT_REMOVE      0x4
5012 #define ENV_ACT_REMOVEMATCH 0x8
5013
5014 #define ENV_MOD_MACHINE     0x20000000
5015 #define ENV_MOD_APPEND      0x40000000
5016 #define ENV_MOD_PREFIX      0x80000000
5017 #define ENV_MOD_MASK        0xC0000000
5018
5019 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5020
5021 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5022 {
5023     LPCWSTR cptr = *name;
5024     LPCWSTR ptr = *value;
5025
5026     static const WCHAR prefix[] = {'[','~',']',0};
5027     static const int prefix_len = 3;
5028
5029     *flags = 0;
5030     while (*cptr)
5031     {
5032         if (*cptr == '=')
5033             *flags |= ENV_ACT_SETALWAYS;
5034         else if (*cptr == '+')
5035             *flags |= ENV_ACT_SETABSENT;
5036         else if (*cptr == '-')
5037             *flags |= ENV_ACT_REMOVE;
5038         else if (*cptr == '!')
5039             *flags |= ENV_ACT_REMOVEMATCH;
5040         else if (*cptr == '*')
5041             *flags |= ENV_MOD_MACHINE;
5042         else
5043             break;
5044
5045         cptr++;
5046         (*name)++;
5047     }
5048
5049     if (!*cptr)
5050     {
5051         ERR("Missing environment variable\n");
5052         return ERROR_FUNCTION_FAILED;
5053     }
5054
5055     if (!strncmpW(ptr, prefix, prefix_len))
5056     {
5057         *flags |= ENV_MOD_APPEND;
5058         *value += lstrlenW(prefix);
5059     }
5060     else if (lstrlenW(*value) >= prefix_len)
5061     {
5062         ptr += lstrlenW(ptr) - prefix_len;
5063         if (!lstrcmpW(ptr, prefix))
5064         {
5065             *flags |= ENV_MOD_PREFIX;
5066             /* the "[~]" will be removed by deformat_string */;
5067         }
5068     }
5069
5070     if (!*flags ||
5071         check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5072         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5073         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5074         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5075     {
5076         ERR("Invalid flags: %08x\n", *flags);
5077         return ERROR_FUNCTION_FAILED;
5078     }
5079
5080     return ERROR_SUCCESS;
5081 }
5082
5083 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5084 {
5085     MSIPACKAGE *package = param;
5086     LPCWSTR name, value;
5087     LPWSTR data = NULL, newval = NULL;
5088     LPWSTR deformatted = NULL, ptr;
5089     DWORD flags, type, size;
5090     LONG res;
5091     HKEY env = NULL, root;
5092     LPCWSTR environment;
5093
5094     static const WCHAR user_env[] =
5095         {'E','n','v','i','r','o','n','m','e','n','t',0};
5096     static const WCHAR machine_env[] =
5097         {'S','y','s','t','e','m','\\',
5098          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5099          'C','o','n','t','r','o','l','\\',
5100          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5101          'E','n','v','i','r','o','n','m','e','n','t',0};
5102     static const WCHAR semicolon[] = {';',0};
5103
5104     name = MSI_RecordGetString(rec, 2);
5105     value = MSI_RecordGetString(rec, 3);
5106
5107     res = env_set_flags(&name, &value, &flags);
5108     if (res != ERROR_SUCCESS)
5109        goto done;
5110
5111     deformat_string(package, value, &deformatted);
5112     if (!deformatted)
5113     {
5114         res = ERROR_OUTOFMEMORY;
5115         goto done;
5116     }
5117
5118     value = deformatted;
5119
5120     if (flags & ENV_MOD_MACHINE)
5121     {
5122         environment = machine_env;
5123         root = HKEY_LOCAL_MACHINE;
5124     }
5125     else
5126     {
5127         environment = user_env;
5128         root = HKEY_CURRENT_USER;
5129     }
5130
5131     res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5132                           KEY_ALL_ACCESS, NULL, &env, NULL);
5133     if (res != ERROR_SUCCESS)
5134         goto done;
5135
5136     if (flags & ENV_ACT_REMOVE)
5137         FIXME("Not removing environment variable on uninstall!\n");
5138
5139     size = 0;
5140     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5141     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5142         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5143         goto done;
5144
5145     if (res != ERROR_FILE_NOT_FOUND)
5146     {
5147         if (flags & ENV_ACT_SETABSENT)
5148         {
5149             res = ERROR_SUCCESS;
5150             goto done;
5151         }
5152
5153         data = msi_alloc(size);
5154         if (!data)
5155         {
5156             RegCloseKey(env);
5157             return ERROR_OUTOFMEMORY;
5158         }
5159
5160         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5161         if (res != ERROR_SUCCESS)
5162             goto done;
5163
5164         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5165         {
5166             res = RegDeleteKeyW(env, name);
5167             goto done;
5168         }
5169
5170         size =  (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5171         newval =  msi_alloc(size);
5172         ptr = newval;
5173         if (!newval)
5174         {
5175             res = ERROR_OUTOFMEMORY;
5176             goto done;
5177         }
5178
5179         if (!(flags & ENV_MOD_MASK))
5180             lstrcpyW(newval, value);
5181         else
5182         {
5183             if (flags & ENV_MOD_PREFIX)
5184             {
5185                 lstrcpyW(newval, value);
5186                 lstrcatW(newval, semicolon);
5187                 ptr = newval + lstrlenW(value) + 1;
5188             }
5189
5190             lstrcpyW(ptr, data);
5191
5192             if (flags & ENV_MOD_APPEND)
5193             {
5194                 lstrcatW(newval, semicolon);
5195                 lstrcatW(newval, value);
5196             }
5197         }
5198     }
5199     else
5200     {
5201         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5202         newval = msi_alloc(size);
5203         if (!newval)
5204         {
5205             res = ERROR_OUTOFMEMORY;
5206             goto done;
5207         }
5208
5209         lstrcpyW(newval, value);
5210     }
5211
5212     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5213     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5214
5215 done:
5216     if (env) RegCloseKey(env);
5217     msi_free(deformatted);
5218     msi_free(data);
5219     msi_free(newval);
5220     return res;
5221 }
5222
5223 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5224 {
5225     UINT rc;
5226     MSIQUERY * view;
5227     static const WCHAR ExecSeqQuery[] =
5228         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5229          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5230     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5231     if (rc != ERROR_SUCCESS)
5232         return ERROR_SUCCESS;
5233
5234     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5235     msiobj_release(&view->hdr);
5236
5237     return rc;
5238 }
5239
5240 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5241
5242 typedef struct
5243 {
5244     struct list entry;
5245     LPWSTR sourcename;
5246     LPWSTR destname;
5247     LPWSTR source;
5248     LPWSTR dest;
5249 } FILE_LIST;
5250
5251 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5252 {
5253     BOOL ret;
5254
5255     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5256         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5257     {
5258         WARN("Source or dest is directory, not moving\n");
5259         return FALSE;
5260     }
5261
5262     if (options == msidbMoveFileOptionsMove)
5263     {
5264         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5265         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5266         if (!ret)
5267         {
5268             WARN("MoveFile failed: %d\n", GetLastError());
5269             return FALSE;
5270         }
5271     }
5272     else
5273     {
5274         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5275         ret = CopyFileW(source, dest, FALSE);
5276         if (!ret)
5277         {
5278             WARN("CopyFile failed: %d\n", GetLastError());
5279             return FALSE;
5280         }
5281     }
5282
5283     return TRUE;
5284 }
5285
5286 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5287 {
5288     LPWSTR path, ptr;
5289     DWORD dirlen, pathlen;
5290
5291     ptr = strrchrW(wildcard, '\\');
5292     dirlen = ptr - wildcard + 1;
5293
5294     pathlen = dirlen + lstrlenW(filename) + 1;
5295     path = msi_alloc(pathlen * sizeof(WCHAR));
5296
5297     lstrcpynW(path, wildcard, dirlen + 1);
5298     lstrcatW(path, filename);
5299
5300     return path;
5301 }
5302
5303 static void free_file_entry(FILE_LIST *file)
5304 {
5305     msi_free(file->source);
5306     msi_free(file->dest);
5307     msi_free(file);
5308 }
5309
5310 static void free_list(FILE_LIST *list)
5311 {
5312     while (!list_empty(&list->entry))
5313     {
5314         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5315
5316         list_remove(&file->entry);
5317         free_file_entry(file);
5318     }
5319 }
5320
5321 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5322 {
5323     FILE_LIST *new, *file;
5324     LPWSTR ptr, filename;
5325     DWORD size;
5326
5327     new = msi_alloc_zero(sizeof(FILE_LIST));
5328     if (!new)
5329         return FALSE;
5330
5331     new->source = strdupW(source);
5332     ptr = strrchrW(dest, '\\') + 1;
5333     filename = strrchrW(new->source, '\\') + 1;
5334
5335     new->sourcename = filename;
5336
5337     if (*ptr)
5338         new->destname = ptr;
5339     else
5340         new->destname = new->sourcename;
5341
5342     size = (ptr - dest) + lstrlenW(filename) + 1;
5343     new->dest = msi_alloc(size * sizeof(WCHAR));
5344     if (!new->dest)
5345     {
5346         free_file_entry(new);
5347         return FALSE;
5348     }
5349
5350     lstrcpynW(new->dest, dest, ptr - dest + 1);
5351     lstrcatW(new->dest, filename);
5352
5353     if (list_empty(&files->entry))
5354     {
5355         list_add_head(&files->entry, &new->entry);
5356         return TRUE;
5357     }
5358
5359     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5360     {
5361         if (lstrcmpW(source, file->source) < 0)
5362         {
5363             list_add_before(&file->entry, &new->entry);
5364             return TRUE;
5365         }
5366     }
5367
5368     list_add_after(&file->entry, &new->entry);
5369     return TRUE;
5370 }
5371
5372 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5373 {
5374     WIN32_FIND_DATAW wfd;
5375     HANDLE hfile;
5376     LPWSTR path;
5377     BOOL res;
5378     FILE_LIST files, *file;
5379     DWORD size;
5380
5381     hfile = FindFirstFileW(source, &wfd);
5382     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5383
5384     list_init(&files.entry);
5385
5386     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5387     {
5388         if (is_dot_dir(wfd.cFileName)) continue;
5389
5390         path = wildcard_to_file(source, wfd.cFileName);
5391         if (!path)
5392         {
5393             res = FALSE;
5394             goto done;
5395         }
5396
5397         add_wildcard(&files, path, dest);
5398         msi_free(path);
5399     }
5400
5401     /* no files match the wildcard */
5402     if (list_empty(&files.entry))
5403         goto done;
5404
5405     /* only the first wildcard match gets renamed to dest */
5406     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5407     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5408     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5409     if (!file->dest)
5410     {
5411         res = FALSE;
5412         goto done;
5413     }
5414
5415     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5416
5417     while (!list_empty(&files.entry))
5418     {
5419         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5420
5421         msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5422
5423         list_remove(&file->entry);
5424         free_file_entry(file);
5425     }
5426
5427     res = TRUE;
5428
5429 done:
5430     free_list(&files);
5431     FindClose(hfile);
5432     return res;
5433 }
5434
5435 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5436 {
5437     MSIPACKAGE *package = param;
5438     MSICOMPONENT *comp;
5439     LPCWSTR sourcename, destname;
5440     LPWSTR sourcedir = NULL, destdir = NULL;
5441     LPWSTR source = NULL, dest = NULL;
5442     int options;
5443     DWORD size;
5444     BOOL ret, wildcards;
5445
5446     static const WCHAR backslash[] = {'\\',0};
5447
5448     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5449     if (!comp || !comp->Enabled ||
5450         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5451     {
5452         TRACE("Component not set for install, not moving file\n");
5453         return ERROR_SUCCESS;
5454     }
5455
5456     sourcename = MSI_RecordGetString(rec, 3);
5457     destname = MSI_RecordGetString(rec, 4);
5458     options = MSI_RecordGetInteger(rec, 7);
5459
5460     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5461     if (!sourcedir)
5462         goto done;
5463
5464     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5465     if (!destdir)
5466         goto done;
5467
5468     if (!sourcename)
5469     {
5470         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5471             goto done;
5472
5473         source = strdupW(sourcedir);
5474         if (!source)
5475             goto done;
5476     }
5477     else
5478     {
5479         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5480         source = msi_alloc(size * sizeof(WCHAR));
5481         if (!source)
5482             goto done;
5483
5484         lstrcpyW(source, sourcedir);
5485         if (source[lstrlenW(source) - 1] != '\\')
5486             lstrcatW(source, backslash);
5487         lstrcatW(source, sourcename);
5488     }
5489
5490     wildcards = strchrW(source, '*') || strchrW(source, '?');
5491
5492     if (!destname && !wildcards)
5493     {
5494         destname = strdupW(sourcename);
5495         if (!destname)
5496             goto done;
5497     }
5498
5499     size = 0;
5500     if (destname)
5501         size = lstrlenW(destname);
5502
5503     size += lstrlenW(destdir) + 2;
5504     dest = msi_alloc(size * sizeof(WCHAR));
5505     if (!dest)
5506         goto done;
5507
5508     lstrcpyW(dest, destdir);
5509     if (dest[lstrlenW(dest) - 1] != '\\')
5510         lstrcatW(dest, backslash);
5511
5512     if (destname)
5513         lstrcatW(dest, destname);
5514
5515     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5516     {
5517         ret = CreateDirectoryW(destdir, NULL);
5518         if (!ret)
5519         {
5520             WARN("CreateDirectory failed: %d\n", GetLastError());
5521             return ERROR_SUCCESS;
5522         }
5523     }
5524
5525     if (!wildcards)
5526         msi_move_file(source, dest, options);
5527     else
5528         move_files_wildcard(source, dest, options);
5529
5530 done:
5531     msi_free(sourcedir);
5532     msi_free(destdir);
5533     msi_free(source);
5534     msi_free(dest);
5535
5536     return ERROR_SUCCESS;
5537 }
5538
5539 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5540 {
5541     UINT rc;
5542     MSIQUERY *view;
5543
5544     static const WCHAR ExecSeqQuery[] =
5545         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5546          '`','M','o','v','e','F','i','l','e','`',0};
5547
5548     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5549     if (rc != ERROR_SUCCESS)
5550         return ERROR_SUCCESS;
5551
5552     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5553     msiobj_release(&view->hdr);
5554
5555     return rc;
5556 }
5557
5558 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5559                                               DWORD dwReserved);
5560 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5561                                           LPVOID pvReserved, HMODULE *phModDll);
5562
5563 static BOOL init_functionpointers(void)
5564 {
5565     HRESULT hr;
5566     HMODULE hfusion;
5567     HMODULE hmscoree;
5568
5569     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5570
5571     hmscoree = LoadLibraryA("mscoree.dll");
5572     if (!hmscoree)
5573     {
5574         WARN("mscoree.dll not available\n");
5575         return FALSE;
5576     }
5577
5578     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5579     if (!pLoadLibraryShim)
5580     {
5581         WARN("LoadLibraryShim not available\n");
5582         FreeLibrary(hmscoree);
5583         return FALSE;
5584     }
5585
5586     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5587     if (FAILED(hr))
5588     {
5589         WARN("fusion.dll not available\n");
5590         FreeLibrary(hmscoree);
5591         return FALSE;
5592     }
5593
5594     pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5595
5596     FreeLibrary(hmscoree);
5597     return TRUE;
5598 }
5599
5600 static UINT install_assembly(LPWSTR path)
5601 {
5602     IAssemblyCache *cache;
5603     HRESULT hr;
5604     UINT r = ERROR_FUNCTION_FAILED;
5605
5606     if (!init_functionpointers() || !pCreateAssemblyCache)
5607         return ERROR_FUNCTION_FAILED;
5608
5609     hr = pCreateAssemblyCache(&cache, 0);
5610     if (FAILED(hr))
5611         goto done;
5612
5613     hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5614     if (FAILED(hr))
5615         ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5616
5617     r = ERROR_SUCCESS;
5618
5619 done:
5620     IAssemblyCache_Release(cache);
5621     return r;
5622 }
5623
5624 static UINT ITERATE_PublishAssembly( MSIRECORD *rec, LPVOID param )
5625 {
5626     MSIPACKAGE *package = param;
5627     MSICOMPONENT *comp;
5628     MSIFEATURE *feature;
5629     MSIFILE *file;
5630     WCHAR path[MAX_PATH];
5631     LPCWSTR app;
5632     DWORD attr;
5633     UINT r;
5634
5635     comp = get_loaded_component(package, MSI_RecordGetString(rec, 1));
5636     if (!comp || !comp->Enabled ||
5637         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5638     {
5639         ERR("Component not set for install, not publishing assembly\n");
5640         return ERROR_SUCCESS;
5641     }
5642
5643     feature = find_feature_by_name(package, MSI_RecordGetString(rec, 2));
5644     if (feature)
5645         msi_feature_set_state(feature, INSTALLSTATE_LOCAL);
5646
5647     if (MSI_RecordGetString(rec, 3))
5648         FIXME("Manifest unhandled\n");
5649
5650     app = MSI_RecordGetString(rec, 4);
5651     if (app)
5652     {
5653         FIXME("Assembly should be privately installed\n");
5654         return ERROR_SUCCESS;
5655     }
5656
5657     attr = MSI_RecordGetInteger(rec, 5);
5658     if (attr == msidbAssemblyAttributesWin32)
5659     {
5660         FIXME("Win32 assemblies not handled\n");
5661         return ERROR_SUCCESS;
5662     }
5663
5664     /* FIXME: extract all files belonging to this component */
5665     file = msi_find_file(package, comp->KeyPath);
5666     if (!file)
5667     {
5668         ERR("File %s not found\n", debugstr_w(comp->KeyPath));
5669         return ERROR_FUNCTION_FAILED;
5670     }
5671
5672     GetTempPathW(MAX_PATH, path);
5673
5674     if (file->IsCompressed)
5675     {
5676         r = msi_extract_file(package, file, path);
5677         if (r != ERROR_SUCCESS)
5678         {
5679             ERR("Failed to extract temporary assembly\n");
5680             return r;
5681         }
5682
5683         PathAddBackslashW(path);
5684         lstrcatW(path, file->FileName);
5685     }
5686     else
5687     {
5688         PathAddBackslashW(path);
5689         lstrcatW(path, file->FileName);
5690
5691         if (!CopyFileW(file->SourcePath, path, FALSE))
5692         {
5693             ERR("Failed to copy temporary assembly: %d\n", GetLastError());
5694             return ERROR_FUNCTION_FAILED;
5695         }
5696     }
5697
5698     r = install_assembly(path);
5699     if (r != ERROR_SUCCESS)
5700         ERR("Failed to install assembly\n");
5701
5702     /* FIXME: write Installer assembly reg values */
5703
5704     return r;
5705 }
5706
5707 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5708 {
5709     UINT rc;
5710     MSIQUERY *view;
5711
5712     static const WCHAR ExecSeqQuery[] =
5713         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5714          '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5715
5716     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5717     if (rc != ERROR_SUCCESS)
5718         return ERROR_SUCCESS;
5719
5720     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishAssembly, package);
5721     msiobj_release(&view->hdr);
5722
5723     return rc;
5724 }
5725
5726 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5727                                            LPCSTR action, LPCWSTR table )
5728 {
5729     static const WCHAR query[] = {
5730         'S','E','L','E','C','T',' ','*',' ',
5731         'F','R','O','M',' ','`','%','s','`',0 };
5732     MSIQUERY *view = NULL;
5733     DWORD count = 0;
5734     UINT r;
5735     
5736     r = MSI_OpenQuery( package->db, &view, query, table );
5737     if (r == ERROR_SUCCESS)
5738     {
5739         r = MSI_IterateRecords(view, &count, NULL, package);
5740         msiobj_release(&view->hdr);
5741     }
5742
5743     if (count)
5744         FIXME("%s -> %u ignored %s table values\n",
5745               action, count, debugstr_w(table));
5746
5747     return ERROR_SUCCESS;
5748 }
5749
5750 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5751 {
5752     TRACE("%p\n", package);
5753     return ERROR_SUCCESS;
5754 }
5755
5756 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5757 {
5758     static const WCHAR table[] =
5759          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5760     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5761 }
5762
5763 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5764 {
5765     static const WCHAR table[] = { 'P','a','t','c','h',0 };
5766     return msi_unimplemented_action_stub( package, "PatchFiles", table );
5767 }
5768
5769 static UINT ACTION_BindImage( MSIPACKAGE *package )
5770 {
5771     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5772     return msi_unimplemented_action_stub( package, "BindImage", table );
5773 }
5774
5775 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5776 {
5777     static const WCHAR table[] = {
5778         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5779     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5780 }
5781
5782 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5783 {
5784     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5785     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5786 }
5787
5788 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5789 {
5790     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5791     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5792 }
5793
5794 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5795 {
5796     static const WCHAR table[] = {
5797         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5798     return msi_unimplemented_action_stub( package, "DeleteServices", table );
5799 }
5800 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5801 {
5802         static const WCHAR table[] = {
5803                 'P','r','o','d','u','c','t','I','D',0 };
5804         return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5805 }
5806
5807 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5808 {
5809     static const WCHAR table[] = {
5810         'E','n','v','i','r','o','n','m','e','n','t',0 };
5811     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5812 }
5813
5814 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5815 {
5816     static const WCHAR table[] = {
5817         'M','s','i','A','s','s','e','m','b','l','y',0 };
5818     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5819 }
5820
5821 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5822 {
5823     static const WCHAR table[] = { 'F','o','n','t',0 };
5824     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5825 }
5826
5827 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5828 {
5829     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5830     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5831 }
5832
5833 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5834 {
5835     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5836     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5837 }
5838
5839 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5840 {
5841     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5842     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5843 }
5844
5845 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5846 {
5847     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5848     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5849 }
5850
5851 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5852 {
5853     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5854     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5855 }
5856
5857 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5858 {
5859     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5860     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5861 }
5862
5863 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5864 {
5865     static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5866     return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5867 }
5868
5869 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5870 {
5871     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5872     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5873 }
5874
5875 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5876 {
5877     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5878     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5879 }
5880
5881 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5882 {
5883     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5884     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5885 }
5886
5887 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5888 {
5889     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5890     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5891 }
5892
5893 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5894 {
5895     static const WCHAR table[] = { 'A','p','p','I','d',0 };
5896     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5897 }
5898
5899 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5900 {
5901     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5902     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5903 }
5904
5905 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5906 {
5907     static const WCHAR table[] = { 'M','I','M','E',0 };
5908     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5909 }
5910
5911 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5912 {
5913     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5914     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5915 }
5916
5917 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5918 {
5919     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5920     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5921 }
5922
5923 static const struct _actions StandardActions[] = {
5924     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5925     { szAppSearch, ACTION_AppSearch },
5926     { szBindImage, ACTION_BindImage },
5927     { szCCPSearch, ACTION_CCPSearch },
5928     { szCostFinalize, ACTION_CostFinalize },
5929     { szCostInitialize, ACTION_CostInitialize },
5930     { szCreateFolders, ACTION_CreateFolders },
5931     { szCreateShortcuts, ACTION_CreateShortcuts },
5932     { szDeleteServices, ACTION_DeleteServices },
5933     { szDisableRollback, NULL },
5934     { szDuplicateFiles, ACTION_DuplicateFiles },
5935     { szExecuteAction, ACTION_ExecuteAction },
5936     { szFileCost, ACTION_FileCost },
5937     { szFindRelatedProducts, ACTION_FindRelatedProducts },
5938     { szForceReboot, ACTION_ForceReboot },
5939     { szInstallAdminPackage, NULL },
5940     { szInstallExecute, ACTION_InstallExecute },
5941     { szInstallExecuteAgain, ACTION_InstallExecute },
5942     { szInstallFiles, ACTION_InstallFiles},
5943     { szInstallFinalize, ACTION_InstallFinalize },
5944     { szInstallInitialize, ACTION_InstallInitialize },
5945     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5946     { szInstallValidate, ACTION_InstallValidate },
5947     { szIsolateComponents, ACTION_IsolateComponents },
5948     { szLaunchConditions, ACTION_LaunchConditions },
5949     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5950     { szMoveFiles, ACTION_MoveFiles },
5951     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5952     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5953     { szInstallODBC, ACTION_InstallODBC },
5954     { szInstallServices, ACTION_InstallServices },
5955     { szPatchFiles, ACTION_PatchFiles },
5956     { szProcessComponents, ACTION_ProcessComponents },
5957     { szPublishComponents, ACTION_PublishComponents },
5958     { szPublishFeatures, ACTION_PublishFeatures },
5959     { szPublishProduct, ACTION_PublishProduct },
5960     { szRegisterClassInfo, ACTION_RegisterClassInfo },
5961     { szRegisterComPlus, ACTION_RegisterComPlus},
5962     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5963     { szRegisterFonts, ACTION_RegisterFonts },
5964     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5965     { szRegisterProduct, ACTION_RegisterProduct },
5966     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5967     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5968     { szRegisterUser, ACTION_RegisterUser },
5969     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5970     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5971     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5972     { szRemoveFiles, ACTION_RemoveFiles },
5973     { szRemoveFolders, ACTION_RemoveFolders },
5974     { szRemoveIniValues, ACTION_RemoveIniValues },
5975     { szRemoveODBC, ACTION_RemoveODBC },
5976     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5977     { szRemoveShortcuts, ACTION_RemoveShortcuts },
5978     { szResolveSource, ACTION_ResolveSource },
5979     { szRMCCPSearch, ACTION_RMCCPSearch },
5980     { szScheduleReboot, NULL },
5981     { szSelfRegModules, ACTION_SelfRegModules },
5982     { szSelfUnregModules, ACTION_SelfUnregModules },
5983     { szSetODBCFolders, NULL },
5984     { szStartServices, ACTION_StartServices },
5985     { szStopServices, ACTION_StopServices },
5986     { szUnpublishComponents, ACTION_UnpublishComponents },
5987     { szUnpublishFeatures, ACTION_UnpublishFeatures },
5988     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5989     { szUnregisterComPlus, ACTION_UnregisterComPlus },
5990     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5991     { szUnregisterFonts, ACTION_UnregisterFonts },
5992     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5993     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5994     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5995     { szValidateProductID, ACTION_ValidateProductID },
5996     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5997     { szWriteIniValues, ACTION_WriteIniValues },
5998     { szWriteRegistryValues, ACTION_WriteRegistryValues },
5999     { NULL, NULL },
6000 };
6001
6002 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6003                                         UINT* rc, BOOL force )
6004 {
6005     BOOL ret = FALSE;
6006     BOOL run = force;
6007     int i;
6008
6009     if (!run && !package->script->CurrentlyScripting)
6010         run = TRUE;
6011
6012     if (!run)
6013     {
6014         if (strcmpW(action,szInstallFinalize) == 0 ||
6015             strcmpW(action,szInstallExecute) == 0 ||
6016             strcmpW(action,szInstallExecuteAgain) == 0)
6017                 run = TRUE;
6018     }
6019
6020     i = 0;
6021     while (StandardActions[i].action != NULL)
6022     {
6023         if (strcmpW(StandardActions[i].action, action)==0)
6024         {
6025             if (!run)
6026             {
6027                 ui_actioninfo(package, action, TRUE, 0);
6028                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6029                 ui_actioninfo(package, action, FALSE, *rc);
6030             }
6031             else
6032             {
6033                 ui_actionstart(package, action);
6034                 if (StandardActions[i].handler)
6035                 {
6036                     *rc = StandardActions[i].handler(package);
6037                 }
6038                 else
6039                 {
6040                     FIXME("unhandled standard action %s\n",debugstr_w(action));
6041                     *rc = ERROR_SUCCESS;
6042                 }
6043             }
6044             ret = TRUE;
6045             break;
6046         }
6047         i++;
6048     }
6049     return ret;
6050 }