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