msi: Look at the requested action when installing services, not the action taken.
[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 component, *vector = NULL;
5005     LPWSTR name, args;
5006     DWORD event, numargs;
5007     UINT r = ERROR_FUNCTION_FAILED;
5008
5009     component = MSI_RecordGetString(rec, 6);
5010     comp = get_loaded_component(package, component);
5011     if (!comp)
5012         return ERROR_SUCCESS;
5013
5014     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5015     {
5016         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5017         comp->Action = comp->Installed;
5018         return ERROR_SUCCESS;
5019     }
5020     comp->Action = INSTALLSTATE_LOCAL;
5021
5022     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5023     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5024     event = MSI_RecordGetInteger(rec, 3);
5025
5026     if (!(event & msidbServiceControlEventStart))
5027     {
5028         r = ERROR_SUCCESS;
5029         goto done;
5030     }
5031
5032     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5033     if (!scm)
5034     {
5035         ERR("Failed to open the service control manager\n");
5036         goto done;
5037     }
5038
5039     service = OpenServiceW(scm, name, SERVICE_START);
5040     if (!service)
5041     {
5042         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5043         goto done;
5044     }
5045
5046     vector = msi_service_args_to_vector(args, &numargs);
5047
5048     if (!StartServiceW(service, numargs, vector) &&
5049         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5050     {
5051         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5052         goto done;
5053     }
5054
5055     r = ERROR_SUCCESS;
5056
5057 done:
5058     CloseServiceHandle(service);
5059     CloseServiceHandle(scm);
5060
5061     msi_free(name);
5062     msi_free(args);
5063     msi_free(vector);
5064     return r;
5065 }
5066
5067 static UINT ACTION_StartServices( MSIPACKAGE *package )
5068 {
5069     UINT rc;
5070     MSIQUERY *view;
5071
5072     static const WCHAR query[] = {
5073         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5074         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5075
5076     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5077     if (rc != ERROR_SUCCESS)
5078         return ERROR_SUCCESS;
5079
5080     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5081     msiobj_release(&view->hdr);
5082
5083     return rc;
5084 }
5085
5086 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5087 {
5088     DWORD i, needed, count;
5089     ENUM_SERVICE_STATUSW *dependencies;
5090     SERVICE_STATUS ss;
5091     SC_HANDLE depserv;
5092
5093     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5094                                0, &needed, &count))
5095         return TRUE;
5096
5097     if (GetLastError() != ERROR_MORE_DATA)
5098         return FALSE;
5099
5100     dependencies = msi_alloc(needed);
5101     if (!dependencies)
5102         return FALSE;
5103
5104     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5105                                 needed, &needed, &count))
5106         goto error;
5107
5108     for (i = 0; i < count; i++)
5109     {
5110         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5111                                SERVICE_STOP | SERVICE_QUERY_STATUS);
5112         if (!depserv)
5113             goto error;
5114
5115         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5116             goto error;
5117     }
5118
5119     return TRUE;
5120
5121 error:
5122     msi_free(dependencies);
5123     return FALSE;
5124 }
5125
5126 static UINT stop_service( LPCWSTR name )
5127 {
5128     SC_HANDLE scm = NULL, service = NULL;
5129     SERVICE_STATUS status;
5130     SERVICE_STATUS_PROCESS ssp;
5131     DWORD needed;
5132
5133     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5134     if (!scm)
5135     {
5136         WARN("Failed to open the SCM: %d\n", GetLastError());
5137         goto done;
5138     }
5139
5140     service = OpenServiceW(scm, name,
5141                            SERVICE_STOP |
5142                            SERVICE_QUERY_STATUS |
5143                            SERVICE_ENUMERATE_DEPENDENTS);
5144     if (!service)
5145     {
5146         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5147         goto done;
5148     }
5149
5150     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5151                               sizeof(SERVICE_STATUS_PROCESS), &needed))
5152     {
5153         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5154         goto done;
5155     }
5156
5157     if (ssp.dwCurrentState == SERVICE_STOPPED)
5158         goto done;
5159
5160     stop_service_dependents(scm, service);
5161
5162     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5163         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5164
5165 done:
5166     CloseServiceHandle(service);
5167     CloseServiceHandle(scm);
5168
5169     return ERROR_SUCCESS;
5170 }
5171
5172 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5173 {
5174     MSIPACKAGE *package = param;
5175     MSICOMPONENT *comp;
5176     LPWSTR name;
5177     DWORD event;
5178
5179     event = MSI_RecordGetInteger( rec, 3 );
5180     if (!(event & msidbServiceControlEventStop))
5181         return ERROR_SUCCESS;
5182
5183     comp = get_loaded_component( package, MSI_RecordGetString( rec, 6 ) );
5184     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
5185         return ERROR_SUCCESS;
5186
5187     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5188     stop_service( name );
5189     msi_free( name );
5190
5191     return ERROR_SUCCESS;
5192 }
5193
5194 static UINT ACTION_StopServices( MSIPACKAGE *package )
5195 {
5196     UINT rc;
5197     MSIQUERY *view;
5198
5199     static const WCHAR query[] = {
5200         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5201         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5202
5203     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5204     if (rc != ERROR_SUCCESS)
5205         return ERROR_SUCCESS;
5206
5207     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5208     msiobj_release(&view->hdr);
5209
5210     return rc;
5211 }
5212
5213 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5214 {
5215     MSIPACKAGE *package = param;
5216     MSICOMPONENT *comp;
5217     LPWSTR name = NULL;
5218     DWORD event;
5219     SC_HANDLE scm = NULL, service = NULL;
5220
5221     event = MSI_RecordGetInteger( rec, 3 );
5222     if (!(event & msidbServiceControlEventDelete))
5223         return ERROR_SUCCESS;
5224
5225     comp = get_loaded_component( package, MSI_RecordGetString(rec, 6) );
5226     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
5227         return ERROR_SUCCESS;
5228
5229     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5230     stop_service( name );
5231
5232     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5233     if (!scm)
5234     {
5235         WARN("Failed to open the SCM: %d\n", GetLastError());
5236         goto done;
5237     }
5238
5239     service = OpenServiceW( scm, name, DELETE );
5240     if (!service)
5241     {
5242         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5243         goto done;
5244     }
5245
5246     if (!DeleteService( service ))
5247         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5248
5249 done:
5250     CloseServiceHandle( service );
5251     CloseServiceHandle( scm );
5252     msi_free( name );
5253
5254     return ERROR_SUCCESS;
5255 }
5256
5257 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5258 {
5259     UINT rc;
5260     MSIQUERY *view;
5261
5262     static const WCHAR query[] = {
5263         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5264         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5265
5266     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5267     if (rc != ERROR_SUCCESS)
5268         return ERROR_SUCCESS;
5269
5270     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5271     msiobj_release( &view->hdr );
5272
5273     return rc;
5274 }
5275
5276 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5277 {
5278     MSIFILE *file;
5279
5280     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5281     {
5282         if (!lstrcmpW(file->File, filename))
5283             return file;
5284     }
5285
5286     return NULL;
5287 }
5288
5289 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5290 {
5291     MSIPACKAGE *package = param;
5292     LPWSTR driver, driver_path, ptr;
5293     WCHAR outpath[MAX_PATH];
5294     MSIFILE *driver_file, *setup_file;
5295     LPCWSTR desc;
5296     DWORD len, usage;
5297     UINT r = ERROR_SUCCESS;
5298
5299     static const WCHAR driver_fmt[] = {
5300         'D','r','i','v','e','r','=','%','s',0};
5301     static const WCHAR setup_fmt[] = {
5302         'S','e','t','u','p','=','%','s',0};
5303     static const WCHAR usage_fmt[] = {
5304         'F','i','l','e','U','s','a','g','e','=','1',0};
5305
5306     desc = MSI_RecordGetString(rec, 3);
5307
5308     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5309     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5310
5311     if (!driver_file)
5312     {
5313         ERR("ODBC Driver entry not found!\n");
5314         return ERROR_FUNCTION_FAILED;
5315     }
5316
5317     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5318     if (setup_file)
5319         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5320     len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5321
5322     driver = msi_alloc(len * sizeof(WCHAR));
5323     if (!driver)
5324         return ERROR_OUTOFMEMORY;
5325
5326     ptr = driver;
5327     lstrcpyW(ptr, desc);
5328     ptr += lstrlenW(ptr) + 1;
5329
5330     len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5331     ptr += len + 1;
5332
5333     if (setup_file)
5334     {
5335         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5336         ptr += len + 1;
5337     }
5338
5339     lstrcpyW(ptr, usage_fmt);
5340     ptr += lstrlenW(ptr) + 1;
5341     *ptr = '\0';
5342
5343     driver_path = strdupW(driver_file->TargetPath);
5344     ptr = strrchrW(driver_path, '\\');
5345     if (ptr) *ptr = '\0';
5346
5347     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5348                              NULL, ODBC_INSTALL_COMPLETE, &usage))
5349     {
5350         ERR("Failed to install SQL driver!\n");
5351         r = ERROR_FUNCTION_FAILED;
5352     }
5353
5354     msi_free(driver);
5355     msi_free(driver_path);
5356
5357     return r;
5358 }
5359
5360 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5361 {
5362     MSIPACKAGE *package = param;
5363     LPWSTR translator, translator_path, ptr;
5364     WCHAR outpath[MAX_PATH];
5365     MSIFILE *translator_file, *setup_file;
5366     LPCWSTR desc;
5367     DWORD len, usage;
5368     UINT r = ERROR_SUCCESS;
5369
5370     static const WCHAR translator_fmt[] = {
5371         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5372     static const WCHAR setup_fmt[] = {
5373         'S','e','t','u','p','=','%','s',0};
5374
5375     desc = MSI_RecordGetString(rec, 3);
5376
5377     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5378     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5379
5380     if (!translator_file)
5381     {
5382         ERR("ODBC Translator entry not found!\n");
5383         return ERROR_FUNCTION_FAILED;
5384     }
5385
5386     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5387     if (setup_file)
5388         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5389
5390     translator = msi_alloc(len * sizeof(WCHAR));
5391     if (!translator)
5392         return ERROR_OUTOFMEMORY;
5393
5394     ptr = translator;
5395     lstrcpyW(ptr, desc);
5396     ptr += lstrlenW(ptr) + 1;
5397
5398     len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5399     ptr += len + 1;
5400
5401     if (setup_file)
5402     {
5403         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5404         ptr += len + 1;
5405     }
5406     *ptr = '\0';
5407
5408     translator_path = strdupW(translator_file->TargetPath);
5409     ptr = strrchrW(translator_path, '\\');
5410     if (ptr) *ptr = '\0';
5411
5412     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5413                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
5414     {
5415         ERR("Failed to install SQL translator!\n");
5416         r = ERROR_FUNCTION_FAILED;
5417     }
5418
5419     msi_free(translator);
5420     msi_free(translator_path);
5421
5422     return r;
5423 }
5424
5425 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5426 {
5427     LPWSTR attrs;
5428     LPCWSTR desc, driver;
5429     WORD request = ODBC_ADD_SYS_DSN;
5430     INT registration;
5431     DWORD len;
5432     UINT r = ERROR_SUCCESS;
5433
5434     static const WCHAR attrs_fmt[] = {
5435         'D','S','N','=','%','s',0 };
5436
5437     desc = MSI_RecordGetString(rec, 3);
5438     driver = MSI_RecordGetString(rec, 4);
5439     registration = MSI_RecordGetInteger(rec, 5);
5440
5441     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5442     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5443
5444     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5445     attrs = msi_alloc(len * sizeof(WCHAR));
5446     if (!attrs)
5447         return ERROR_OUTOFMEMORY;
5448
5449     len = sprintfW(attrs, attrs_fmt, desc);
5450     attrs[len + 1] = 0;
5451
5452     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5453     {
5454         ERR("Failed to install SQL data source!\n");
5455         r = ERROR_FUNCTION_FAILED;
5456     }
5457
5458     msi_free(attrs);
5459
5460     return r;
5461 }
5462
5463 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5464 {
5465     UINT rc;
5466     MSIQUERY *view;
5467
5468     static const WCHAR driver_query[] = {
5469         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5470         'O','D','B','C','D','r','i','v','e','r',0 };
5471
5472     static const WCHAR translator_query[] = {
5473         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5474         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5475
5476     static const WCHAR source_query[] = {
5477         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5478         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5479
5480     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5481     if (rc != ERROR_SUCCESS)
5482         return ERROR_SUCCESS;
5483
5484     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5485     msiobj_release(&view->hdr);
5486
5487     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5488     if (rc != ERROR_SUCCESS)
5489         return ERROR_SUCCESS;
5490
5491     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5492     msiobj_release(&view->hdr);
5493
5494     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5495     if (rc != ERROR_SUCCESS)
5496         return ERROR_SUCCESS;
5497
5498     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5499     msiobj_release(&view->hdr);
5500
5501     return rc;
5502 }
5503
5504 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5505 {
5506     DWORD usage;
5507     LPCWSTR desc;
5508
5509     desc = MSI_RecordGetString( rec, 3 );
5510     if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5511     {
5512         WARN("Failed to remove ODBC driver\n");
5513     }
5514     else if (!usage)
5515     {
5516         FIXME("Usage count reached 0\n");
5517     }
5518
5519     return ERROR_SUCCESS;
5520 }
5521
5522 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5523 {
5524     DWORD usage;
5525     LPCWSTR desc;
5526
5527     desc = MSI_RecordGetString( rec, 3 );
5528     if (!SQLRemoveTranslatorW( desc, &usage ))
5529     {
5530         WARN("Failed to remove ODBC translator\n");
5531     }
5532     else if (!usage)
5533     {
5534         FIXME("Usage count reached 0\n");
5535     }
5536
5537     return ERROR_SUCCESS;
5538 }
5539
5540 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5541 {
5542     LPWSTR attrs;
5543     LPCWSTR desc, driver;
5544     WORD request = ODBC_REMOVE_SYS_DSN;
5545     INT registration;
5546     DWORD len;
5547
5548     static const WCHAR attrs_fmt[] = {
5549         'D','S','N','=','%','s',0 };
5550
5551     desc = MSI_RecordGetString( rec, 3 );
5552     driver = MSI_RecordGetString( rec, 4 );
5553     registration = MSI_RecordGetInteger( rec, 5 );
5554
5555     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
5556     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
5557
5558     len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
5559     attrs = msi_alloc( len * sizeof(WCHAR) );
5560     if (!attrs)
5561         return ERROR_OUTOFMEMORY;
5562
5563     FIXME("Use ODBCSourceAttribute table\n");
5564
5565     len = sprintfW( attrs, attrs_fmt, desc );
5566     attrs[len + 1] = 0;
5567
5568     if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
5569     {
5570         WARN("Failed to remove ODBC data source\n");
5571     }
5572     msi_free( attrs );
5573
5574     return ERROR_SUCCESS;
5575 }
5576
5577 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5578 {
5579     UINT rc;
5580     MSIQUERY *view;
5581
5582     static const WCHAR driver_query[] = {
5583         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5584         'O','D','B','C','D','r','i','v','e','r',0 };
5585
5586     static const WCHAR translator_query[] = {
5587         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5588         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5589
5590     static const WCHAR source_query[] = {
5591         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5592         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5593
5594     rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
5595     if (rc != ERROR_SUCCESS)
5596         return ERROR_SUCCESS;
5597
5598     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
5599     msiobj_release( &view->hdr );
5600
5601     rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
5602     if (rc != ERROR_SUCCESS)
5603         return ERROR_SUCCESS;
5604
5605     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
5606     msiobj_release( &view->hdr );
5607
5608     rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
5609     if (rc != ERROR_SUCCESS)
5610         return ERROR_SUCCESS;
5611
5612     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
5613     msiobj_release( &view->hdr );
5614
5615     return rc;
5616 }
5617
5618 #define ENV_ACT_SETALWAYS   0x1
5619 #define ENV_ACT_SETABSENT   0x2
5620 #define ENV_ACT_REMOVE      0x4
5621 #define ENV_ACT_REMOVEMATCH 0x8
5622
5623 #define ENV_MOD_MACHINE     0x20000000
5624 #define ENV_MOD_APPEND      0x40000000
5625 #define ENV_MOD_PREFIX      0x80000000
5626 #define ENV_MOD_MASK        0xC0000000
5627
5628 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5629
5630 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5631 {
5632     LPCWSTR cptr = *name;
5633
5634     static const WCHAR prefix[] = {'[','~',']',0};
5635     static const int prefix_len = 3;
5636
5637     *flags = 0;
5638     while (*cptr)
5639     {
5640         if (*cptr == '=')
5641             *flags |= ENV_ACT_SETALWAYS;
5642         else if (*cptr == '+')
5643             *flags |= ENV_ACT_SETABSENT;
5644         else if (*cptr == '-')
5645             *flags |= ENV_ACT_REMOVE;
5646         else if (*cptr == '!')
5647             *flags |= ENV_ACT_REMOVEMATCH;
5648         else if (*cptr == '*')
5649             *flags |= ENV_MOD_MACHINE;
5650         else
5651             break;
5652
5653         cptr++;
5654         (*name)++;
5655     }
5656
5657     if (!*cptr)
5658     {
5659         ERR("Missing environment variable\n");
5660         return ERROR_FUNCTION_FAILED;
5661     }
5662
5663     if (*value)
5664     {
5665         LPCWSTR ptr = *value;
5666         if (!strncmpW(ptr, prefix, prefix_len))
5667         {
5668             if (ptr[prefix_len] == szSemiColon[0])
5669             {
5670                 *flags |= ENV_MOD_APPEND;
5671                 *value += lstrlenW(prefix);
5672             }
5673             else
5674             {
5675                 *value = NULL;
5676             }
5677         }
5678         else if (lstrlenW(*value) >= prefix_len)
5679         {
5680             ptr += lstrlenW(ptr) - prefix_len;
5681             if (!lstrcmpW(ptr, prefix))
5682             {
5683                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5684                 {
5685                     *flags |= ENV_MOD_PREFIX;
5686                     /* the "[~]" will be removed by deformat_string */;
5687                 }
5688                 else
5689                 {
5690                     *value = NULL;
5691                 }
5692             }
5693         }
5694     }
5695
5696     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5697         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5698         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5699         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5700     {
5701         ERR("Invalid flags: %08x\n", *flags);
5702         return ERROR_FUNCTION_FAILED;
5703     }
5704
5705     if (!*flags)
5706         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5707
5708     return ERROR_SUCCESS;
5709 }
5710
5711 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5712 {
5713     MSIPACKAGE *package = param;
5714     LPCWSTR name, value, component;
5715     LPWSTR data = NULL, newval = NULL;
5716     LPWSTR deformatted = NULL, ptr;
5717     DWORD flags, type, size;
5718     LONG res;
5719     HKEY env = NULL, root;
5720     LPCWSTR environment;
5721     MSICOMPONENT *comp;
5722
5723     static const WCHAR user_env[] =
5724         {'E','n','v','i','r','o','n','m','e','n','t',0};
5725     static const WCHAR machine_env[] =
5726         {'S','y','s','t','e','m','\\',
5727          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5728          'C','o','n','t','r','o','l','\\',
5729          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5730          'E','n','v','i','r','o','n','m','e','n','t',0};
5731
5732     component = MSI_RecordGetString(rec, 4);
5733     comp = get_loaded_component(package, component);
5734     if (!comp)
5735         return ERROR_SUCCESS;
5736
5737     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5738     {
5739         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5740         comp->Action = comp->Installed;
5741         return ERROR_SUCCESS;
5742     }
5743     comp->Action = INSTALLSTATE_LOCAL;
5744
5745     name = MSI_RecordGetString(rec, 2);
5746     value = MSI_RecordGetString(rec, 3);
5747
5748     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5749
5750     res = env_set_flags(&name, &value, &flags);
5751     if (res != ERROR_SUCCESS || !value)
5752        goto done;
5753
5754     if (value && !deformat_string(package, value, &deformatted))
5755     {
5756         res = ERROR_OUTOFMEMORY;
5757         goto done;
5758     }
5759
5760     value = deformatted;
5761
5762     if (flags & ENV_MOD_MACHINE)
5763     {
5764         environment = machine_env;
5765         root = HKEY_LOCAL_MACHINE;
5766     }
5767     else
5768     {
5769         environment = user_env;
5770         root = HKEY_CURRENT_USER;
5771     }
5772
5773     res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5774                           KEY_ALL_ACCESS, NULL, &env, NULL);
5775     if (res != ERROR_SUCCESS)
5776         goto done;
5777
5778     if (flags & ENV_ACT_REMOVE)
5779         FIXME("Not removing environment variable on uninstall!\n");
5780
5781     size = 0;
5782     type = REG_SZ;
5783     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5784     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5785         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5786         goto done;
5787
5788     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5789     {
5790         /* Nothing to do. */
5791         if (!value)
5792         {
5793             res = ERROR_SUCCESS;
5794             goto done;
5795         }
5796
5797         /* If we are appending but the string was empty, strip ; */
5798         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5799
5800         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5801         newval = strdupW(value);
5802         if (!newval)
5803         {
5804             res = ERROR_OUTOFMEMORY;
5805             goto done;
5806         }
5807     }
5808     else
5809     {
5810         /* Contrary to MSDN, +-variable to [~];path works */
5811         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
5812         {
5813             res = ERROR_SUCCESS;
5814             goto done;
5815         }
5816
5817         data = msi_alloc(size);
5818         if (!data)
5819         {
5820             RegCloseKey(env);
5821             return ERROR_OUTOFMEMORY;
5822         }
5823
5824         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5825         if (res != ERROR_SUCCESS)
5826             goto done;
5827
5828         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5829         {
5830             res = RegDeleteKeyW(env, name);
5831             goto done;
5832         }
5833
5834         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
5835         if (flags & ENV_MOD_MASK)
5836         {
5837             DWORD mod_size;
5838             int multiplier = 0;
5839             if (flags & ENV_MOD_APPEND) multiplier++;
5840             if (flags & ENV_MOD_PREFIX) multiplier++;
5841             mod_size = lstrlenW(value) * multiplier;
5842             size += mod_size * sizeof(WCHAR);
5843         }
5844
5845         newval = msi_alloc(size);
5846         ptr = newval;
5847         if (!newval)
5848         {
5849             res = ERROR_OUTOFMEMORY;
5850             goto done;
5851         }
5852
5853         if (flags & ENV_MOD_PREFIX)
5854         {
5855             lstrcpyW(newval, value);
5856             ptr = newval + lstrlenW(value);
5857         }
5858
5859         lstrcpyW(ptr, data);
5860
5861         if (flags & ENV_MOD_APPEND)
5862         {
5863             lstrcatW(newval, value);
5864         }
5865     }
5866     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5867     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5868
5869 done:
5870     if (env) RegCloseKey(env);
5871     msi_free(deformatted);
5872     msi_free(data);
5873     msi_free(newval);
5874     return res;
5875 }
5876
5877 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5878 {
5879     UINT rc;
5880     MSIQUERY * view;
5881     static const WCHAR ExecSeqQuery[] =
5882         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5883          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5884     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5885     if (rc != ERROR_SUCCESS)
5886         return ERROR_SUCCESS;
5887
5888     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5889     msiobj_release(&view->hdr);
5890
5891     return rc;
5892 }
5893
5894 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5895
5896 typedef struct
5897 {
5898     struct list entry;
5899     LPWSTR sourcename;
5900     LPWSTR destname;
5901     LPWSTR source;
5902     LPWSTR dest;
5903 } FILE_LIST;
5904
5905 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5906 {
5907     BOOL ret;
5908
5909     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5910         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5911     {
5912         WARN("Source or dest is directory, not moving\n");
5913         return FALSE;
5914     }
5915
5916     if (options == msidbMoveFileOptionsMove)
5917     {
5918         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5919         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5920         if (!ret)
5921         {
5922             WARN("MoveFile failed: %d\n", GetLastError());
5923             return FALSE;
5924         }
5925     }
5926     else
5927     {
5928         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5929         ret = CopyFileW(source, dest, FALSE);
5930         if (!ret)
5931         {
5932             WARN("CopyFile failed: %d\n", GetLastError());
5933             return FALSE;
5934         }
5935     }
5936
5937     return TRUE;
5938 }
5939
5940 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5941 {
5942     LPWSTR path, ptr;
5943     DWORD dirlen, pathlen;
5944
5945     ptr = strrchrW(wildcard, '\\');
5946     dirlen = ptr - wildcard + 1;
5947
5948     pathlen = dirlen + lstrlenW(filename) + 1;
5949     path = msi_alloc(pathlen * sizeof(WCHAR));
5950
5951     lstrcpynW(path, wildcard, dirlen + 1);
5952     lstrcatW(path, filename);
5953
5954     return path;
5955 }
5956
5957 static void free_file_entry(FILE_LIST *file)
5958 {
5959     msi_free(file->source);
5960     msi_free(file->dest);
5961     msi_free(file);
5962 }
5963
5964 static void free_list(FILE_LIST *list)
5965 {
5966     while (!list_empty(&list->entry))
5967     {
5968         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5969
5970         list_remove(&file->entry);
5971         free_file_entry(file);
5972     }
5973 }
5974
5975 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5976 {
5977     FILE_LIST *new, *file;
5978     LPWSTR ptr, filename;
5979     DWORD size;
5980
5981     new = msi_alloc_zero(sizeof(FILE_LIST));
5982     if (!new)
5983         return FALSE;
5984
5985     new->source = strdupW(source);
5986     ptr = strrchrW(dest, '\\') + 1;
5987     filename = strrchrW(new->source, '\\') + 1;
5988
5989     new->sourcename = filename;
5990
5991     if (*ptr)
5992         new->destname = ptr;
5993     else
5994         new->destname = new->sourcename;
5995
5996     size = (ptr - dest) + lstrlenW(filename) + 1;
5997     new->dest = msi_alloc(size * sizeof(WCHAR));
5998     if (!new->dest)
5999     {
6000         free_file_entry(new);
6001         return FALSE;
6002     }
6003
6004     lstrcpynW(new->dest, dest, ptr - dest + 1);
6005     lstrcatW(new->dest, filename);
6006
6007     if (list_empty(&files->entry))
6008     {
6009         list_add_head(&files->entry, &new->entry);
6010         return TRUE;
6011     }
6012
6013     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
6014     {
6015         if (lstrcmpW(source, file->source) < 0)
6016         {
6017             list_add_before(&file->entry, &new->entry);
6018             return TRUE;
6019         }
6020     }
6021
6022     list_add_after(&file->entry, &new->entry);
6023     return TRUE;
6024 }
6025
6026 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
6027 {
6028     WIN32_FIND_DATAW wfd;
6029     HANDLE hfile;
6030     LPWSTR path;
6031     BOOL res;
6032     FILE_LIST files, *file;
6033     DWORD size;
6034
6035     hfile = FindFirstFileW(source, &wfd);
6036     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
6037
6038     list_init(&files.entry);
6039
6040     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
6041     {
6042         if (is_dot_dir(wfd.cFileName)) continue;
6043
6044         path = wildcard_to_file(source, wfd.cFileName);
6045         if (!path)
6046         {
6047             res = FALSE;
6048             goto done;
6049         }
6050
6051         add_wildcard(&files, path, dest);
6052         msi_free(path);
6053     }
6054
6055     /* no files match the wildcard */
6056     if (list_empty(&files.entry))
6057         goto done;
6058
6059     /* only the first wildcard match gets renamed to dest */
6060     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
6061     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
6062     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
6063     if (!file->dest)
6064     {
6065         res = FALSE;
6066         goto done;
6067     }
6068
6069     /* file->dest may be shorter after the reallocation, so add a NULL
6070      * terminator.  This is needed for the call to strrchrW, as there will no
6071      * longer be a NULL terminator within the bounds of the allocation in this case.
6072      */
6073     file->dest[size - 1] = '\0';
6074     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
6075
6076     while (!list_empty(&files.entry))
6077     {
6078         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
6079
6080         msi_move_file(file->source, file->dest, options);
6081
6082         list_remove(&file->entry);
6083         free_file_entry(file);
6084     }
6085
6086     res = TRUE;
6087
6088 done:
6089     free_list(&files);
6090     FindClose(hfile);
6091     return res;
6092 }
6093
6094 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
6095 {
6096     MSIPACKAGE *package = param;
6097     MSICOMPONENT *comp;
6098     LPCWSTR sourcename;
6099     LPWSTR destname = NULL;
6100     LPWSTR sourcedir = NULL, destdir = NULL;
6101     LPWSTR source = NULL, dest = NULL;
6102     int options;
6103     DWORD size;
6104     BOOL ret, wildcards;
6105
6106     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
6107     if (!comp || !comp->Enabled ||
6108         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
6109     {
6110         TRACE("Component not set for install, not moving file\n");
6111         return ERROR_SUCCESS;
6112     }
6113
6114     sourcename = MSI_RecordGetString(rec, 3);
6115     options = MSI_RecordGetInteger(rec, 7);
6116
6117     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
6118     if (!sourcedir)
6119         goto done;
6120
6121     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
6122     if (!destdir)
6123         goto done;
6124
6125     if (!sourcename)
6126     {
6127         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
6128             goto done;
6129
6130         source = strdupW(sourcedir);
6131         if (!source)
6132             goto done;
6133     }
6134     else
6135     {
6136         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
6137         source = msi_alloc(size * sizeof(WCHAR));
6138         if (!source)
6139             goto done;
6140
6141         lstrcpyW(source, sourcedir);
6142         if (source[lstrlenW(source) - 1] != '\\')
6143             lstrcatW(source, szBackSlash);
6144         lstrcatW(source, sourcename);
6145     }
6146
6147     wildcards = strchrW(source, '*') || strchrW(source, '?');
6148
6149     if (MSI_RecordIsNull(rec, 4))
6150     {
6151         if (!wildcards)
6152         {
6153             destname = strdupW(sourcename);
6154             if (!destname)
6155                 goto done;
6156         }
6157     }
6158     else
6159     {
6160         destname = strdupW(MSI_RecordGetString(rec, 4));
6161         if (destname)
6162             reduce_to_longfilename(destname);
6163     }
6164
6165     size = 0;
6166     if (destname)
6167         size = lstrlenW(destname);
6168
6169     size += lstrlenW(destdir) + 2;
6170     dest = msi_alloc(size * sizeof(WCHAR));
6171     if (!dest)
6172         goto done;
6173
6174     lstrcpyW(dest, destdir);
6175     if (dest[lstrlenW(dest) - 1] != '\\')
6176         lstrcatW(dest, szBackSlash);
6177
6178     if (destname)
6179         lstrcatW(dest, destname);
6180
6181     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
6182     {
6183         ret = CreateDirectoryW(destdir, NULL);
6184         if (!ret)
6185         {
6186             WARN("CreateDirectory failed: %d\n", GetLastError());
6187             return ERROR_SUCCESS;
6188         }
6189     }
6190
6191     if (!wildcards)
6192         msi_move_file(source, dest, options);
6193     else
6194         move_files_wildcard(source, dest, options);
6195
6196 done:
6197     msi_free(sourcedir);
6198     msi_free(destdir);
6199     msi_free(destname);
6200     msi_free(source);
6201     msi_free(dest);
6202
6203     return ERROR_SUCCESS;
6204 }
6205
6206 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
6207 {
6208     UINT rc;
6209     MSIQUERY *view;
6210
6211     static const WCHAR ExecSeqQuery[] =
6212         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6213          '`','M','o','v','e','F','i','l','e','`',0};
6214
6215     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6216     if (rc != ERROR_SUCCESS)
6217         return ERROR_SUCCESS;
6218
6219     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
6220     msiobj_release(&view->hdr);
6221
6222     return rc;
6223 }
6224
6225 typedef struct tagMSIASSEMBLY
6226 {
6227     struct list entry;
6228     MSICOMPONENT *component;
6229     MSIFEATURE *feature;
6230     MSIFILE *file;
6231     LPWSTR manifest;
6232     LPWSTR application;
6233     DWORD attributes;
6234     BOOL installed;
6235 } MSIASSEMBLY;
6236
6237 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6238                                               DWORD dwReserved);
6239 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6240                                           LPVOID pvReserved, HMODULE *phModDll);
6241
6242 static BOOL init_functionpointers(void)
6243 {
6244     HRESULT hr;
6245     HMODULE hfusion;
6246     HMODULE hmscoree;
6247
6248     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6249
6250     hmscoree = LoadLibraryA("mscoree.dll");
6251     if (!hmscoree)
6252     {
6253         WARN("mscoree.dll not available\n");
6254         return FALSE;
6255     }
6256
6257     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6258     if (!pLoadLibraryShim)
6259     {
6260         WARN("LoadLibraryShim not available\n");
6261         FreeLibrary(hmscoree);
6262         return FALSE;
6263     }
6264
6265     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6266     if (FAILED(hr))
6267     {
6268         WARN("fusion.dll not available\n");
6269         FreeLibrary(hmscoree);
6270         return FALSE;
6271     }
6272
6273     pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6274
6275     FreeLibrary(hmscoree);
6276     return TRUE;
6277 }
6278
6279 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6280                              LPWSTR path)
6281 {
6282     IAssemblyCache *cache;
6283     HRESULT hr;
6284     UINT r = ERROR_FUNCTION_FAILED;
6285
6286     TRACE("installing assembly: %s\n", debugstr_w(path));
6287
6288     if (assembly->feature)
6289         msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6290
6291     if (assembly->manifest)
6292         FIXME("Manifest unhandled\n");
6293
6294     if (assembly->application)
6295     {
6296         FIXME("Assembly should be privately installed\n");
6297         return ERROR_SUCCESS;
6298     }
6299
6300     if (assembly->attributes == msidbAssemblyAttributesWin32)
6301     {
6302         FIXME("Win32 assemblies not handled\n");
6303         return ERROR_SUCCESS;
6304     }
6305
6306     hr = pCreateAssemblyCache(&cache, 0);
6307     if (FAILED(hr))
6308         goto done;
6309
6310     hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6311     if (FAILED(hr))
6312         ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6313
6314     r = ERROR_SUCCESS;
6315
6316 done:
6317     IAssemblyCache_Release(cache);
6318     return r;
6319 }
6320
6321 typedef struct tagASSEMBLY_LIST
6322 {
6323     MSIPACKAGE *package;
6324     IAssemblyCache *cache;
6325     struct list *assemblies;
6326 } ASSEMBLY_LIST;
6327
6328 typedef struct tagASSEMBLY_NAME
6329 {
6330     LPWSTR name;
6331     LPWSTR version;
6332     LPWSTR culture;
6333     LPWSTR pubkeytoken;
6334 } ASSEMBLY_NAME;
6335
6336 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6337 {
6338     ASSEMBLY_NAME *asmname = param;
6339     LPCWSTR name = MSI_RecordGetString(rec, 2);
6340     LPWSTR val = msi_dup_record_field(rec, 3);
6341
6342     static const WCHAR Name[] = {'N','a','m','e',0};
6343     static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6344     static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6345     static const WCHAR PublicKeyToken[] = {
6346         'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6347
6348     if (!strcmpiW(name, Name))
6349         asmname->name = val;
6350     else if (!strcmpiW(name, Version))
6351         asmname->version = val;
6352     else if (!strcmpiW(name, Culture))
6353         asmname->culture = val;
6354     else if (!strcmpiW(name, PublicKeyToken))
6355         asmname->pubkeytoken = val;
6356     else
6357         msi_free(val);
6358
6359     return ERROR_SUCCESS;
6360 }
6361
6362 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6363 {
6364     if (!*str)
6365     {
6366         *size = lstrlenW(append) + 1;
6367         *str = msi_alloc((*size) * sizeof(WCHAR));
6368         lstrcpyW(*str, append);
6369         return;
6370     }
6371
6372     (*size) += lstrlenW(append);
6373     *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6374     lstrcatW(*str, append);
6375 }
6376
6377 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
6378                                      MSICOMPONENT *comp)
6379 {
6380     ASSEMBLY_INFO asminfo;
6381     ASSEMBLY_NAME name;
6382     MSIQUERY *view;
6383     LPWSTR disp;
6384     DWORD size;
6385     BOOL found;
6386     UINT r;
6387
6388     static const WCHAR separator[] = {',',' ',0};
6389     static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6390     static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6391     static const WCHAR PublicKeyToken[] = {
6392         'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6393     static const WCHAR query[] = {
6394         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6395         '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6396         'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6397         '=','\'','%','s','\'',0};
6398
6399     disp = NULL;
6400     found = FALSE;
6401     ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
6402     ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
6403
6404     r = MSI_OpenQuery(db, &view, query, comp->Component);
6405     if (r != ERROR_SUCCESS)
6406         return ERROR_SUCCESS;
6407
6408     MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
6409     msiobj_release(&view->hdr);
6410
6411     if (!name.name)
6412     {
6413         ERR("No assembly name specified!\n");
6414         goto done;
6415     }
6416
6417     append_str(&disp, &size, name.name);
6418
6419     if (name.version)
6420     {
6421         append_str(&disp, &size, separator);
6422         append_str(&disp, &size, Version);
6423         append_str(&disp, &size, name.version);
6424     }
6425
6426     if (name.culture)
6427     {
6428         append_str(&disp, &size, separator);
6429         append_str(&disp, &size, Culture);
6430         append_str(&disp, &size, name.culture);
6431     }
6432
6433     if (name.pubkeytoken)
6434     {
6435         append_str(&disp, &size, separator);
6436         append_str(&disp, &size, PublicKeyToken);
6437         append_str(&disp, &size, name.pubkeytoken);
6438     }
6439
6440     asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6441     IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
6442                                      disp, &asminfo);
6443     found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6444
6445 done:
6446     msi_free(disp);
6447     msi_free(name.name);
6448     msi_free(name.version);
6449     msi_free(name.culture);
6450     msi_free(name.pubkeytoken);
6451
6452     return found;
6453 }
6454
6455 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6456 {
6457     ASSEMBLY_LIST *list = param;
6458     MSIASSEMBLY *assembly;
6459
6460     assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6461     if (!assembly)
6462         return ERROR_OUTOFMEMORY;
6463
6464     assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
6465
6466     if (!assembly->component || !assembly->component->Enabled ||
6467         !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
6468     {
6469         TRACE("Component not set for install, not publishing assembly\n");
6470         msi_free(assembly);
6471         return ERROR_SUCCESS;
6472     }
6473
6474     assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6475     assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6476
6477     if (!assembly->file)
6478     {
6479         ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6480         return ERROR_FUNCTION_FAILED;
6481     }
6482
6483     assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6484     assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6485     assembly->attributes = MSI_RecordGetInteger(rec, 5);
6486
6487     if (assembly->application)
6488     {
6489         WCHAR version[24];
6490         DWORD size = sizeof(version)/sizeof(WCHAR);
6491
6492         /* FIXME: we should probably check the manifest file here */
6493
6494         if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6495             (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6496         {
6497             assembly->installed = TRUE;
6498         }
6499     }
6500     else
6501         assembly->installed = check_assembly_installed(list->package->db,
6502                                                        list->cache,
6503                                                        assembly->component);
6504
6505     list_add_head(list->assemblies, &assembly->entry);
6506     return ERROR_SUCCESS;
6507 }
6508
6509 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6510 {
6511     IAssemblyCache *cache = NULL;
6512     ASSEMBLY_LIST list;
6513     MSIQUERY *view;
6514     HRESULT hr;
6515     UINT r;
6516
6517     static const WCHAR query[] =
6518         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6519          '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6520
6521     r = MSI_DatabaseOpenViewW(package->db, query, &view);
6522     if (r != ERROR_SUCCESS)
6523         return ERROR_SUCCESS;
6524
6525     hr = pCreateAssemblyCache(&cache, 0);
6526     if (FAILED(hr))
6527         return ERROR_FUNCTION_FAILED;
6528
6529     list.package = package;
6530     list.cache = cache;
6531     list.assemblies = assemblies;
6532
6533     r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6534     msiobj_release(&view->hdr);
6535
6536     IAssemblyCache_Release(cache);
6537
6538     return r;
6539 }
6540
6541 static void free_assemblies(struct list *assemblies)
6542 {
6543     struct list *item, *cursor;
6544
6545     LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6546     {
6547         MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6548
6549         list_remove(&assembly->entry);
6550         msi_free(assembly->application);
6551         msi_free(assembly->manifest);
6552         msi_free(assembly);
6553     }
6554 }
6555
6556 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6557 {
6558     MSIASSEMBLY *assembly;
6559
6560     LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6561     {
6562         if (!lstrcmpW(assembly->file->File, file))
6563         {
6564             *out = assembly;
6565             return TRUE;
6566         }
6567     }
6568
6569     return FALSE;
6570 }
6571
6572 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6573                                LPWSTR *path, DWORD *attrs, PVOID user)
6574 {
6575     MSIASSEMBLY *assembly;
6576     WCHAR temppath[MAX_PATH];
6577     struct list *assemblies = user;
6578     UINT r;
6579
6580     if (!find_assembly(assemblies, file, &assembly))
6581         return FALSE;
6582
6583     GetTempPathW(MAX_PATH, temppath);
6584     PathAddBackslashW(temppath);
6585     lstrcatW(temppath, assembly->file->FileName);
6586
6587     if (action == MSICABEXTRACT_BEGINEXTRACT)
6588     {
6589         if (assembly->installed)
6590             return FALSE;
6591
6592         *path = strdupW(temppath);
6593         *attrs = assembly->file->Attributes;
6594     }
6595     else if (action == MSICABEXTRACT_FILEEXTRACTED)
6596     {
6597         assembly->installed = TRUE;
6598
6599         r = install_assembly(package, assembly, temppath);
6600         if (r != ERROR_SUCCESS)
6601             ERR("Failed to install assembly\n");
6602     }
6603
6604     return TRUE;
6605 }
6606
6607 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6608 {
6609     UINT r;
6610     struct list assemblies = LIST_INIT(assemblies);
6611     MSIASSEMBLY *assembly;
6612     MSIMEDIAINFO *mi;
6613
6614     if (!init_functionpointers() || !pCreateAssemblyCache)
6615         return ERROR_FUNCTION_FAILED;
6616
6617     r = load_assemblies(package, &assemblies);
6618     if (r != ERROR_SUCCESS)
6619         goto done;
6620
6621     if (list_empty(&assemblies))
6622         goto done;
6623
6624     mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6625     if (!mi)
6626     {
6627         r = ERROR_OUTOFMEMORY;
6628         goto done;
6629     }
6630
6631     LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6632     {
6633         if (assembly->installed && !mi->is_continuous)
6634             continue;
6635
6636         if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6637             (assembly->file->IsCompressed && !mi->is_extracted))
6638         {
6639             MSICABDATA data;
6640
6641             r = ready_media(package, assembly->file, mi);
6642             if (r != ERROR_SUCCESS)
6643             {
6644                 ERR("Failed to ready media\n");
6645                 break;
6646             }
6647
6648             data.mi = mi;
6649             data.package = package;
6650             data.cb = installassembly_cb;
6651             data.user = &assemblies;
6652
6653             if (assembly->file->IsCompressed &&
6654                 !msi_cabextract(package, mi, &data))
6655             {
6656                 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6657                 r = ERROR_FUNCTION_FAILED;
6658                 break;
6659             }
6660         }
6661
6662         if (!assembly->file->IsCompressed)
6663         {
6664             LPWSTR source = resolve_file_source(package, assembly->file);
6665
6666             r = install_assembly(package, assembly, source);
6667             if (r != ERROR_SUCCESS)
6668                 ERR("Failed to install assembly\n");
6669
6670             msi_free(source);
6671         }
6672
6673         /* FIXME: write Installer assembly reg values */
6674     }
6675
6676 done:
6677     free_assemblies(&assemblies);
6678     return r;
6679 }
6680
6681 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6682 {
6683     LPWSTR key, template, id;
6684     UINT r = ERROR_SUCCESS;
6685
6686     id = msi_dup_property( package, szProductID );
6687     if (id)
6688     {
6689         msi_free( id );
6690         return ERROR_SUCCESS;
6691     }
6692     template = msi_dup_property( package, szPIDTemplate );
6693     key = msi_dup_property( package, szPIDKEY );
6694
6695     if (key && template)
6696     {
6697         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6698         r = MSI_SetPropertyW( package, szProductID, key );
6699     }
6700     msi_free( template );
6701     msi_free( key );
6702     return r;
6703 }
6704
6705 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6706 {
6707     TRACE("\n");
6708     package->need_reboot = 1;
6709     return ERROR_SUCCESS;
6710 }
6711
6712 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6713 {
6714     static const WCHAR szAvailableFreeReg[] =
6715         {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6716
6717     TRACE("%p %d kilobytes\n", package, msi_get_property_int( package, szAvailableFreeReg, 0 ));
6718     return ERROR_SUCCESS;
6719 }
6720
6721 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6722 {
6723     FIXME("%p\n", package);
6724     return ERROR_SUCCESS;
6725 }
6726
6727 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6728 {
6729     FIXME("%p\n", package);
6730     return ERROR_SUCCESS;
6731 }
6732
6733 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6734                                            LPCSTR action, LPCWSTR table )
6735 {
6736     static const WCHAR query[] = {
6737         'S','E','L','E','C','T',' ','*',' ',
6738         'F','R','O','M',' ','`','%','s','`',0 };
6739     MSIQUERY *view = NULL;
6740     DWORD count = 0;
6741     UINT r;
6742     
6743     r = MSI_OpenQuery( package->db, &view, query, table );
6744     if (r == ERROR_SUCCESS)
6745     {
6746         r = MSI_IterateRecords(view, &count, NULL, package);
6747         msiobj_release(&view->hdr);
6748     }
6749
6750     if (count)
6751         FIXME("%s -> %u ignored %s table values\n",
6752               action, count, debugstr_w(table));
6753
6754     return ERROR_SUCCESS;
6755 }
6756
6757 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6758 {
6759     static const WCHAR table[] =
6760          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6761     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6762 }
6763
6764 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6765 {
6766     static const WCHAR table[] = { 'P','a','t','c','h',0 };
6767     return msi_unimplemented_action_stub( package, "PatchFiles", table );
6768 }
6769
6770 static UINT ACTION_BindImage( MSIPACKAGE *package )
6771 {
6772     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6773     return msi_unimplemented_action_stub( package, "BindImage", table );
6774 }
6775
6776 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6777 {
6778     static const WCHAR table[] = {
6779         'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
6780     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6781 }
6782
6783 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6784 {
6785     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6786     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6787 }
6788
6789 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6790 {
6791     static const WCHAR table[] = {
6792         'E','n','v','i','r','o','n','m','e','n','t',0 };
6793     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6794 }
6795
6796 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6797 {
6798     static const WCHAR table[] = {
6799         'M','s','i','A','s','s','e','m','b','l','y',0 };
6800     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6801 }
6802
6803 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6804 {
6805     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6806     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6807 }
6808
6809 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6810 {
6811     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6812     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6813 }
6814
6815 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6816 {
6817     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6818     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6819 }
6820
6821 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6822 {
6823     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6824     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6825 }
6826
6827 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6828 {
6829     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6830     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6831 }
6832
6833 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6834 {
6835     static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
6836     return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
6837 }
6838
6839 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6840 {
6841     static const WCHAR table[] = { 'A','p','p','I','d',0 };
6842     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6843 }
6844
6845 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6846 {
6847     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6848     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6849 }
6850
6851 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6852 {
6853     static const WCHAR table[] = { 'M','I','M','E',0 };
6854     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6855 }
6856
6857 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6858 {
6859     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6860     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6861 }
6862
6863 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6864
6865 static const struct
6866 {
6867     const WCHAR *action;
6868     UINT (*handler)(MSIPACKAGE *);
6869 }
6870 StandardActions[] =
6871 {
6872     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6873     { szAppSearch, ACTION_AppSearch },
6874     { szBindImage, ACTION_BindImage },
6875     { szCCPSearch, ACTION_CCPSearch },
6876     { szCostFinalize, ACTION_CostFinalize },
6877     { szCostInitialize, ACTION_CostInitialize },
6878     { szCreateFolders, ACTION_CreateFolders },
6879     { szCreateShortcuts, ACTION_CreateShortcuts },
6880     { szDeleteServices, ACTION_DeleteServices },
6881     { szDisableRollback, ACTION_DisableRollback },
6882     { szDuplicateFiles, ACTION_DuplicateFiles },
6883     { szExecuteAction, ACTION_ExecuteAction },
6884     { szFileCost, ACTION_FileCost },
6885     { szFindRelatedProducts, ACTION_FindRelatedProducts },
6886     { szForceReboot, ACTION_ForceReboot },
6887     { szInstallAdminPackage, ACTION_InstallAdminPackage },
6888     { szInstallExecute, ACTION_InstallExecute },
6889     { szInstallExecuteAgain, ACTION_InstallExecute },
6890     { szInstallFiles, ACTION_InstallFiles},
6891     { szInstallFinalize, ACTION_InstallFinalize },
6892     { szInstallInitialize, ACTION_InstallInitialize },
6893     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6894     { szInstallValidate, ACTION_InstallValidate },
6895     { szIsolateComponents, ACTION_IsolateComponents },
6896     { szLaunchConditions, ACTION_LaunchConditions },
6897     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6898     { szMoveFiles, ACTION_MoveFiles },
6899     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6900     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6901     { szInstallODBC, ACTION_InstallODBC },
6902     { szInstallServices, ACTION_InstallServices },
6903     { szPatchFiles, ACTION_PatchFiles },
6904     { szProcessComponents, ACTION_ProcessComponents },
6905     { szPublishComponents, ACTION_PublishComponents },
6906     { szPublishFeatures, ACTION_PublishFeatures },
6907     { szPublishProduct, ACTION_PublishProduct },
6908     { szRegisterClassInfo, ACTION_RegisterClassInfo },
6909     { szRegisterComPlus, ACTION_RegisterComPlus},
6910     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6911     { szRegisterFonts, ACTION_RegisterFonts },
6912     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6913     { szRegisterProduct, ACTION_RegisterProduct },
6914     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6915     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6916     { szRegisterUser, ACTION_RegisterUser },
6917     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6918     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6919     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6920     { szRemoveFiles, ACTION_RemoveFiles },
6921     { szRemoveFolders, ACTION_RemoveFolders },
6922     { szRemoveIniValues, ACTION_RemoveIniValues },
6923     { szRemoveODBC, ACTION_RemoveODBC },
6924     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6925     { szRemoveShortcuts, ACTION_RemoveShortcuts },
6926     { szResolveSource, ACTION_ResolveSource },
6927     { szRMCCPSearch, ACTION_RMCCPSearch },
6928     { szScheduleReboot, ACTION_ScheduleReboot },
6929     { szSelfRegModules, ACTION_SelfRegModules },
6930     { szSelfUnregModules, ACTION_SelfUnregModules },
6931     { szSetODBCFolders, ACTION_SetODBCFolders },
6932     { szStartServices, ACTION_StartServices },
6933     { szStopServices, ACTION_StopServices },
6934     { szUnpublishComponents, ACTION_UnpublishComponents },
6935     { szUnpublishFeatures, ACTION_UnpublishFeatures },
6936     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6937     { szUnregisterComPlus, ACTION_UnregisterComPlus },
6938     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6939     { szUnregisterFonts, ACTION_UnregisterFonts },
6940     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6941     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6942     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6943     { szValidateProductID, ACTION_ValidateProductID },
6944     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6945     { szWriteIniValues, ACTION_WriteIniValues },
6946     { szWriteRegistryValues, ACTION_WriteRegistryValues },
6947     { NULL, NULL },
6948 };
6949
6950 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6951                                         UINT* rc, BOOL force )
6952 {
6953     BOOL ret = FALSE;
6954     BOOL run = force;
6955     int i;
6956
6957     if (!run && !package->script->CurrentlyScripting)
6958         run = TRUE;
6959
6960     if (!run)
6961     {
6962         if (strcmpW(action,szInstallFinalize) == 0 ||
6963             strcmpW(action,szInstallExecute) == 0 ||
6964             strcmpW(action,szInstallExecuteAgain) == 0)
6965                 run = TRUE;
6966     }
6967
6968     i = 0;
6969     while (StandardActions[i].action != NULL)
6970     {
6971         if (strcmpW(StandardActions[i].action, action)==0)
6972         {
6973             if (!run)
6974             {
6975                 ui_actioninfo(package, action, TRUE, 0);
6976                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6977                 ui_actioninfo(package, action, FALSE, *rc);
6978             }
6979             else
6980             {
6981                 ui_actionstart(package, action);
6982                 if (StandardActions[i].handler)
6983                 {
6984                     *rc = StandardActions[i].handler(package);
6985                 }
6986                 else
6987                 {
6988                     FIXME("unhandled standard action %s\n",debugstr_w(action));
6989                     *rc = ERROR_SUCCESS;
6990                 }
6991             }
6992             ret = TRUE;
6993             break;
6994         }
6995         i++;
6996     }
6997     return ret;
6998 }
6999
7000 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
7001 {
7002     UINT rc = ERROR_SUCCESS;
7003     BOOL handled;
7004
7005     TRACE("Performing action (%s)\n", debugstr_w(action));
7006
7007     handled = ACTION_HandleStandardAction(package, action, &rc, force);
7008
7009     if (!handled)
7010         handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
7011
7012     if (!handled)
7013     {
7014         WARN("unhandled msi action %s\n", debugstr_w(action));
7015         rc = ERROR_FUNCTION_NOT_CALLED;
7016     }
7017
7018     return rc;
7019 }
7020
7021 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7022 {
7023     UINT rc = ERROR_SUCCESS;
7024     BOOL handled = FALSE;
7025
7026     TRACE("Performing action (%s)\n", debugstr_w(action));
7027
7028     handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
7029
7030     if (!handled)
7031         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7032
7033     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7034         handled = TRUE;
7035
7036     if (!handled)
7037     {
7038         WARN("unhandled msi action %s\n", debugstr_w(action));
7039         rc = ERROR_FUNCTION_NOT_CALLED;
7040     }
7041
7042     return rc;
7043 }
7044
7045 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7046 {
7047     UINT rc = ERROR_SUCCESS;
7048     MSIRECORD *row;
7049
7050     static const WCHAR ExecSeqQuery[] =
7051         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7052          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7053          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7054          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7055     static const WCHAR UISeqQuery[] =
7056         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7057      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7058      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7059          ' ', '=',' ','%','i',0};
7060
7061     if (needs_ui_sequence(package))
7062         row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7063     else
7064         row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7065
7066     if (row)
7067     {
7068         LPCWSTR action, cond;
7069
7070         TRACE("Running the actions\n");
7071
7072         /* check conditions */
7073         cond = MSI_RecordGetString(row, 2);
7074
7075         /* this is a hack to skip errors in the condition code */
7076         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7077         {
7078             msiobj_release(&row->hdr);
7079             return ERROR_SUCCESS;
7080         }
7081
7082         action = MSI_RecordGetString(row, 1);
7083         if (!action)
7084         {
7085             ERR("failed to fetch action\n");
7086             msiobj_release(&row->hdr);
7087             return ERROR_FUNCTION_FAILED;
7088         }
7089
7090         if (needs_ui_sequence(package))
7091             rc = ACTION_PerformUIAction(package, action, -1);
7092         else
7093             rc = ACTION_PerformAction(package, action, -1, FALSE);
7094
7095         msiobj_release(&row->hdr);
7096     }
7097
7098     return rc;
7099 }
7100
7101 /****************************************************
7102  * TOP level entry points
7103  *****************************************************/
7104
7105 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7106                          LPCWSTR szCommandLine )
7107 {
7108     UINT rc;
7109     BOOL ui_exists;
7110
7111     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7112     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7113
7114     MSI_SetPropertyW(package, szAction, szInstall);
7115
7116     package->script->InWhatSequence = SEQUENCE_INSTALL;
7117
7118     if (szPackagePath)
7119     {
7120         LPWSTR p, dir;
7121         LPCWSTR file;
7122
7123         dir = strdupW(szPackagePath);
7124         p = strrchrW(dir, '\\');
7125         if (p)
7126         {
7127             *(++p) = 0;
7128             file = szPackagePath + (p - dir);
7129         }
7130         else
7131         {
7132             msi_free(dir);
7133             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7134             GetCurrentDirectoryW(MAX_PATH, dir);
7135             lstrcatW(dir, szBackSlash);
7136             file = szPackagePath;
7137         }
7138
7139         msi_free( package->PackagePath );
7140         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7141         if (!package->PackagePath)
7142         {
7143             msi_free(dir);
7144             return ERROR_OUTOFMEMORY;
7145         }
7146
7147         lstrcpyW(package->PackagePath, dir);
7148         lstrcatW(package->PackagePath, file);
7149         msi_free(dir);
7150
7151         msi_set_sourcedir_props(package, FALSE);
7152     }
7153
7154     msi_parse_command_line( package, szCommandLine, FALSE );
7155
7156     msi_apply_transforms( package );
7157     msi_apply_patches( package );
7158
7159     if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
7160     {
7161         TRACE("setting reinstall property\n");
7162         MSI_SetPropertyW( package, szReinstall, szAll );
7163     }
7164
7165     /* properties may have been added by a transform */
7166     msi_clone_properties( package );
7167     msi_set_context( package );
7168
7169     if (needs_ui_sequence( package))
7170     {
7171         package->script->InWhatSequence |= SEQUENCE_UI;
7172         rc = ACTION_ProcessUISequence(package);
7173         ui_exists = ui_sequence_exists(package);
7174         if (rc == ERROR_SUCCESS || !ui_exists)
7175         {
7176             package->script->InWhatSequence |= SEQUENCE_EXEC;
7177             rc = ACTION_ProcessExecSequence(package, ui_exists);
7178         }
7179     }
7180     else
7181         rc = ACTION_ProcessExecSequence(package, FALSE);
7182
7183     package->script->CurrentlyScripting = FALSE;
7184
7185     /* process the ending type action */
7186     if (rc == ERROR_SUCCESS)
7187         ACTION_PerformActionSequence(package, -1);
7188     else if (rc == ERROR_INSTALL_USEREXIT)
7189         ACTION_PerformActionSequence(package, -2);
7190     else if (rc == ERROR_INSTALL_SUSPEND)
7191         ACTION_PerformActionSequence(package, -4);
7192     else  /* failed */
7193         ACTION_PerformActionSequence(package, -3);
7194
7195     /* finish up running custom actions */
7196     ACTION_FinishCustomActions(package);
7197
7198     if (rc == ERROR_SUCCESS && package->need_reboot)
7199         return ERROR_SUCCESS_REBOOT_REQUIRED;
7200
7201     return rc;
7202 }