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