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