msi: Initialize a couple of variables.
[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 WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
3831 {
3832     WCHAR *filename, *ptr, *folder, *ret;
3833     const WCHAR *dirprop;
3834
3835     filename = msi_dup_record_field( row, 2 );
3836     if (filename && (ptr = strchrW( filename, '|' )))
3837         ptr++;
3838     else
3839         ptr = filename;
3840
3841     dirprop = MSI_RecordGetString( row, 3 );
3842     if (dirprop)
3843     {
3844         folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
3845         if (!folder)
3846             folder = msi_dup_property( package, dirprop );
3847     }
3848     else
3849         folder = msi_dup_property( package, szWindowsFolder );
3850
3851     if (!folder)
3852     {
3853         ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
3854         msi_free( filename );
3855         return NULL;
3856     }
3857
3858     ret = build_directory_name( 2, folder, ptr );
3859
3860     msi_free( filename );
3861     msi_free( folder );
3862     return ret;
3863 }
3864
3865 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3866 {
3867     MSIPACKAGE *package = param;
3868     LPCWSTR component, section, key, value, identifier;
3869     LPWSTR deformated_section, deformated_key, deformated_value, fullname;
3870     MSIRECORD * uirow;
3871     INT action;
3872     MSICOMPONENT *comp;
3873
3874     component = MSI_RecordGetString(row, 8);
3875     comp = get_loaded_component(package,component);
3876     if (!comp)
3877         return ERROR_SUCCESS;
3878
3879     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3880     {
3881         TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3882         comp->Action = comp->Installed;
3883         return ERROR_SUCCESS;
3884     }
3885     comp->Action = INSTALLSTATE_LOCAL;
3886
3887     identifier = MSI_RecordGetString(row,1); 
3888     section = MSI_RecordGetString(row,4);
3889     key = MSI_RecordGetString(row,5);
3890     value = MSI_RecordGetString(row,6);
3891     action = MSI_RecordGetInteger(row,7);
3892
3893     deformat_string(package,section,&deformated_section);
3894     deformat_string(package,key,&deformated_key);
3895     deformat_string(package,value,&deformated_value);
3896
3897     fullname = get_ini_file_name(package, row);
3898
3899     if (action == 0)
3900     {
3901         TRACE("Adding value %s to section %s in %s\n",
3902                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3903                 debugstr_w(fullname));
3904         WritePrivateProfileStringW(deformated_section, deformated_key,
3905                                    deformated_value, fullname);
3906     }
3907     else if (action == 1)
3908     {
3909         WCHAR returned[10];
3910         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3911                                  returned, 10, fullname);
3912         if (returned[0] == 0)
3913         {
3914             TRACE("Adding value %s to section %s in %s\n",
3915                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3916                     debugstr_w(fullname));
3917
3918             WritePrivateProfileStringW(deformated_section, deformated_key,
3919                                        deformated_value, fullname);
3920         }
3921     }
3922     else if (action == 3)
3923         FIXME("Append to existing section not yet implemented\n");
3924
3925     uirow = MSI_CreateRecord(4);
3926     MSI_RecordSetStringW(uirow,1,identifier);
3927     MSI_RecordSetStringW(uirow,2,deformated_section);
3928     MSI_RecordSetStringW(uirow,3,deformated_key);
3929     MSI_RecordSetStringW(uirow,4,deformated_value);
3930     ui_actiondata(package,szWriteIniValues,uirow);
3931     msiobj_release( &uirow->hdr );
3932
3933     msi_free(fullname);
3934     msi_free(deformated_key);
3935     msi_free(deformated_value);
3936     msi_free(deformated_section);
3937     return ERROR_SUCCESS;
3938 }
3939
3940 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3941 {
3942     UINT rc;
3943     MSIQUERY * view;
3944     static const WCHAR ExecSeqQuery[] = 
3945         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3946          '`','I','n','i','F','i','l','e','`',0};
3947
3948     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3949     if (rc != ERROR_SUCCESS)
3950     {
3951         TRACE("no IniFile table\n");
3952         return ERROR_SUCCESS;
3953     }
3954
3955     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3956     msiobj_release(&view->hdr);
3957     return rc;
3958 }
3959
3960 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
3961 {
3962     MSIPACKAGE *package = param;
3963     LPCWSTR component, section, key, value, identifier;
3964     LPWSTR deformated_section, deformated_key, deformated_value, filename;
3965     MSICOMPONENT *comp;
3966     MSIRECORD *uirow;
3967     INT action;
3968
3969     component = MSI_RecordGetString( row, 8 );
3970     comp = get_loaded_component( package, component );
3971     if (!comp)
3972         return ERROR_SUCCESS;
3973
3974     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3975     {
3976         TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3977         comp->Action = comp->Installed;
3978         return ERROR_SUCCESS;
3979     }
3980     comp->Action = INSTALLSTATE_ABSENT;
3981
3982     identifier = MSI_RecordGetString( row, 1 );
3983     section = MSI_RecordGetString( row, 4 );
3984     key = MSI_RecordGetString( row, 5 );
3985     value = MSI_RecordGetString( row, 6 );
3986     action = MSI_RecordGetInteger( row, 7 );
3987
3988     deformat_string( package, section, &deformated_section );
3989     deformat_string( package, key, &deformated_key );
3990     deformat_string( package, value, &deformated_value );
3991
3992     if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
3993     {
3994         filename = get_ini_file_name( package, row );
3995
3996         TRACE("Removing key %s from section %s in %s\n",
3997                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
3998
3999         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4000         {
4001             WARN("Unable to remove key %u\n", GetLastError());
4002         }
4003         msi_free( filename );
4004     }
4005     else
4006         FIXME("Unsupported action %d\n", action);
4007
4008
4009     uirow = MSI_CreateRecord( 4 );
4010     MSI_RecordSetStringW( uirow, 1, identifier );
4011     MSI_RecordSetStringW( uirow, 2, deformated_section );
4012     MSI_RecordSetStringW( uirow, 3, deformated_key );
4013     MSI_RecordSetStringW( uirow, 4, deformated_value );
4014     ui_actiondata( package, szRemoveIniValues, uirow );
4015     msiobj_release( &uirow->hdr );
4016
4017     msi_free( deformated_key );
4018     msi_free( deformated_value );
4019     msi_free( deformated_section );
4020     return ERROR_SUCCESS;
4021 }
4022
4023 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4024 {
4025     MSIPACKAGE *package = param;
4026     LPCWSTR component, section, key, value, identifier;
4027     LPWSTR deformated_section, deformated_key, deformated_value, filename;
4028     MSICOMPONENT *comp;
4029     MSIRECORD *uirow;
4030     INT action;
4031
4032     component = MSI_RecordGetString( row, 8 );
4033     comp = get_loaded_component( package, component );
4034     if (!comp)
4035         return ERROR_SUCCESS;
4036
4037     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4038     {
4039         TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4040         comp->Action = comp->Installed;
4041         return ERROR_SUCCESS;
4042     }
4043     comp->Action = INSTALLSTATE_LOCAL;
4044
4045     identifier = MSI_RecordGetString( row, 1 );
4046     section = MSI_RecordGetString( row, 4 );
4047     key = MSI_RecordGetString( row, 5 );
4048     value = MSI_RecordGetString( row, 6 );
4049     action = MSI_RecordGetInteger( row, 7 );
4050
4051     deformat_string( package, section, &deformated_section );
4052     deformat_string( package, key, &deformated_key );
4053     deformat_string( package, value, &deformated_value );
4054
4055     if (action == msidbIniFileActionRemoveLine)
4056     {
4057         filename = get_ini_file_name( package, row );
4058
4059         TRACE("Removing key %s from section %s in %s\n",
4060                debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4061
4062         if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4063         {
4064             WARN("Unable to remove key %u\n", GetLastError());
4065         }
4066         msi_free( filename );
4067     }
4068     else
4069         FIXME("Unsupported action %d\n", action);
4070
4071     uirow = MSI_CreateRecord( 4 );
4072     MSI_RecordSetStringW( uirow, 1, identifier );
4073     MSI_RecordSetStringW( uirow, 2, deformated_section );
4074     MSI_RecordSetStringW( uirow, 3, deformated_key );
4075     MSI_RecordSetStringW( uirow, 4, deformated_value );
4076     ui_actiondata( package, szRemoveIniValues, uirow );
4077     msiobj_release( &uirow->hdr );
4078
4079     msi_free( deformated_key );
4080     msi_free( deformated_value );
4081     msi_free( deformated_section );
4082     return ERROR_SUCCESS;
4083 }
4084
4085 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4086 {
4087     UINT rc;
4088     MSIQUERY *view;
4089     static const WCHAR query[] =
4090         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4091          '`','I','n','i','F','i','l','e','`',0};
4092     static const WCHAR remove_query[] =
4093         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4094          '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4095
4096     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4097     if (rc == ERROR_SUCCESS)
4098     {
4099         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4100         msiobj_release( &view->hdr );
4101         if (rc != ERROR_SUCCESS)
4102             return rc;
4103     }
4104
4105     rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4106     if (rc == ERROR_SUCCESS)
4107     {
4108         rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4109         msiobj_release( &view->hdr );
4110         if (rc != ERROR_SUCCESS)
4111             return rc;
4112     }
4113
4114     return ERROR_SUCCESS;
4115 }
4116
4117 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4118 {
4119     MSIPACKAGE *package = param;
4120     LPCWSTR filename;
4121     LPWSTR FullName;
4122     MSIFILE *file;
4123     DWORD len;
4124     static const WCHAR ExeStr[] =
4125         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
4126     static const WCHAR close[] =  {'\"',0};
4127     STARTUPINFOW si;
4128     PROCESS_INFORMATION info;
4129     BOOL brc;
4130     MSIRECORD *uirow;
4131     LPWSTR uipath, p;
4132
4133     memset(&si,0,sizeof(STARTUPINFOW));
4134
4135     filename = MSI_RecordGetString(row,1);
4136     file = get_loaded_file( package, filename );
4137
4138     if (!file)
4139     {
4140         ERR("Unable to find file id %s\n",debugstr_w(filename));
4141         return ERROR_SUCCESS;
4142     }
4143
4144     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
4145
4146     FullName = msi_alloc(len*sizeof(WCHAR));
4147     strcpyW(FullName,ExeStr);
4148     strcatW( FullName, file->TargetPath );
4149     strcatW(FullName,close);
4150
4151     TRACE("Registering %s\n",debugstr_w(FullName));
4152     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
4153                     &si, &info);
4154
4155     if (brc)
4156     {
4157         CloseHandle(info.hThread);
4158         msi_dialog_check_messages(info.hProcess);
4159         CloseHandle(info.hProcess);
4160     }
4161
4162     msi_free(FullName);
4163
4164     /* the UI chunk */
4165     uirow = MSI_CreateRecord( 2 );
4166     uipath = strdupW( file->TargetPath );
4167     p = strrchrW(uipath,'\\');
4168     if (p)
4169         p[0]=0;
4170     MSI_RecordSetStringW( uirow, 1, &p[1] );
4171     MSI_RecordSetStringW( uirow, 2, uipath);
4172     ui_actiondata( package, szSelfRegModules, uirow);
4173     msiobj_release( &uirow->hdr );
4174     msi_free( uipath );
4175     /* FIXME: call ui_progress? */
4176
4177     return ERROR_SUCCESS;
4178 }
4179
4180 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4181 {
4182     UINT rc;
4183     MSIQUERY * view;
4184     static const WCHAR ExecSeqQuery[] = 
4185         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4186          '`','S','e','l','f','R','e','g','`',0};
4187
4188     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4189     if (rc != ERROR_SUCCESS)
4190     {
4191         TRACE("no SelfReg table\n");
4192         return ERROR_SUCCESS;
4193     }
4194
4195     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4196     msiobj_release(&view->hdr);
4197
4198     return ERROR_SUCCESS;
4199 }
4200
4201 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4202 {
4203     static const WCHAR regsvr32[] =
4204         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
4205     static const WCHAR close[] =  {'\"',0};
4206     MSIPACKAGE *package = param;
4207     LPCWSTR filename;
4208     LPWSTR cmdline;
4209     MSIFILE *file;
4210     DWORD len;
4211     STARTUPINFOW si;
4212     PROCESS_INFORMATION pi;
4213     BOOL ret;
4214     MSIRECORD *uirow;
4215     LPWSTR uipath, p;
4216
4217     memset( &si, 0, sizeof(STARTUPINFOW) );
4218
4219     filename = MSI_RecordGetString( row, 1 );
4220     file = get_loaded_file( package, filename );
4221
4222     if (!file)
4223     {
4224         ERR("Unable to find file id %s\n", debugstr_w(filename));
4225         return ERROR_SUCCESS;
4226     }
4227
4228     len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
4229
4230     cmdline = msi_alloc( len * sizeof(WCHAR) );
4231     strcpyW( cmdline, regsvr32 );
4232     strcatW( cmdline, file->TargetPath );
4233     strcatW( cmdline, close );
4234
4235     TRACE("Unregistering %s\n", debugstr_w(cmdline));
4236
4237     ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
4238     if (ret)
4239     {
4240         CloseHandle( pi.hThread );
4241         msi_dialog_check_messages( pi.hProcess );
4242         CloseHandle( pi.hProcess );
4243     }
4244
4245     msi_free( cmdline );
4246
4247     uirow = MSI_CreateRecord( 2 );
4248     uipath = strdupW( file->TargetPath );
4249     if ((p = strrchrW( uipath, '\\' )))
4250     {
4251         *p = 0;
4252         MSI_RecordSetStringW( uirow, 1, ++p );
4253     }
4254     MSI_RecordSetStringW( uirow, 2, uipath );
4255     ui_actiondata( package, szSelfUnregModules, uirow );
4256     msiobj_release( &uirow->hdr );
4257     msi_free( uipath );
4258     /* FIXME call ui_progress? */
4259
4260     return ERROR_SUCCESS;
4261 }
4262
4263 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4264 {
4265     UINT rc;
4266     MSIQUERY *view;
4267     static const WCHAR query[] =
4268         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4269          '`','S','e','l','f','R','e','g','`',0};
4270
4271     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4272     if (rc != ERROR_SUCCESS)
4273     {
4274         TRACE("no SelfReg table\n");
4275         return ERROR_SUCCESS;
4276     }
4277
4278     MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4279     msiobj_release( &view->hdr );
4280
4281     return ERROR_SUCCESS;
4282 }
4283
4284 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4285 {
4286     MSIFEATURE *feature;
4287     UINT rc;
4288     HKEY hkey = NULL, userdata = NULL;
4289
4290     if (!msi_check_publish(package))
4291         return ERROR_SUCCESS;
4292
4293     rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4294                                 &hkey, TRUE);
4295     if (rc != ERROR_SUCCESS)
4296         goto end;
4297
4298     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4299                                         &userdata, TRUE);
4300     if (rc != ERROR_SUCCESS)
4301         goto end;
4302
4303     /* here the guids are base 85 encoded */
4304     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4305     {
4306         ComponentList *cl;
4307         LPWSTR data = NULL;
4308         GUID clsid;
4309         INT size;
4310         BOOL absent = FALSE;
4311         MSIRECORD *uirow;
4312
4313         if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4314             feature->ActionRequest != INSTALLSTATE_SOURCE &&
4315             feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4316
4317         size = 1;
4318         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4319         {
4320             size += 21;
4321         }
4322         if (feature->Feature_Parent)
4323             size += strlenW( feature->Feature_Parent )+2;
4324
4325         data = msi_alloc(size * sizeof(WCHAR));
4326
4327         data[0] = 0;
4328         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4329         {
4330             MSICOMPONENT* component = cl->component;
4331             WCHAR buf[21];
4332
4333             buf[0] = 0;
4334             if (component->ComponentId)
4335             {
4336                 TRACE("From %s\n",debugstr_w(component->ComponentId));
4337                 CLSIDFromString(component->ComponentId, &clsid);
4338                 encode_base85_guid(&clsid,buf);
4339                 TRACE("to %s\n",debugstr_w(buf));
4340                 strcatW(data,buf);
4341             }
4342         }
4343
4344         if (feature->Feature_Parent)
4345         {
4346             static const WCHAR sep[] = {'\2',0};
4347             strcatW(data,sep);
4348             strcatW(data,feature->Feature_Parent);
4349         }
4350
4351         msi_reg_set_val_str( userdata, feature->Feature, data );
4352         msi_free(data);
4353
4354         size = 0;
4355         if (feature->Feature_Parent)
4356             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4357         if (!absent)
4358         {
4359             size += sizeof(WCHAR);
4360             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4361                            (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4362         }
4363         else
4364         {
4365             size += 2*sizeof(WCHAR);
4366             data = msi_alloc(size);
4367             data[0] = 0x6;
4368             data[1] = 0;
4369             if (feature->Feature_Parent)
4370                 strcpyW( &data[1], feature->Feature_Parent );
4371             RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4372                        (LPBYTE)data,size);
4373             msi_free(data);
4374         }
4375
4376         /* the UI chunk */
4377         uirow = MSI_CreateRecord( 1 );
4378         MSI_RecordSetStringW( uirow, 1, feature->Feature );
4379         ui_actiondata( package, szPublishFeatures, uirow);
4380         msiobj_release( &uirow->hdr );
4381         /* FIXME: call ui_progress? */
4382     }
4383
4384 end:
4385     RegCloseKey(hkey);
4386     RegCloseKey(userdata);
4387     return rc;
4388 }
4389
4390 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4391 {
4392     UINT r;
4393     HKEY hkey;
4394
4395     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4396
4397     r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4398                                &hkey, FALSE);
4399     if (r == ERROR_SUCCESS)
4400     {
4401         RegDeleteValueW(hkey, feature->Feature);
4402         RegCloseKey(hkey);
4403     }
4404
4405     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4406                                        &hkey, FALSE);
4407     if (r == ERROR_SUCCESS)
4408     {
4409         RegDeleteValueW(hkey, feature->Feature);
4410         RegCloseKey(hkey);
4411     }
4412
4413     return ERROR_SUCCESS;
4414 }
4415
4416 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4417 {
4418     MSIFEATURE *feature;
4419
4420     if (!msi_check_unpublish(package))
4421         return ERROR_SUCCESS;
4422
4423     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4424     {
4425         msi_unpublish_feature(package, feature);
4426     }
4427
4428     return ERROR_SUCCESS;
4429 }
4430
4431 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4432 {
4433     LPWSTR prop, val, key;
4434     SYSTEMTIME systime;
4435     DWORD size, langid;
4436     WCHAR date[9];
4437     LPWSTR buffer;
4438
4439     static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4440     static const WCHAR szWindowsInstaller[] =
4441         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4442     static const WCHAR modpath_fmt[] =
4443         {'M','s','i','E','x','e','c','.','e','x','e',' ',
4444          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4445     static const WCHAR szModifyPath[] =
4446         {'M','o','d','i','f','y','P','a','t','h',0};
4447     static const WCHAR szUninstallString[] =
4448         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4449     static const WCHAR szEstimatedSize[] =
4450         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4451     static const WCHAR szProductLanguage[] =
4452         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4453     static const WCHAR szProductVersion[] =
4454         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4455     static const WCHAR szProductName[] =
4456         {'P','r','o','d','u','c','t','N','a','m','e',0};
4457     static const WCHAR szDisplayName[] =
4458         {'D','i','s','p','l','a','y','N','a','m','e',0};
4459     static const WCHAR szDisplayVersion[] =
4460         {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4461     static const WCHAR szManufacturer[] =
4462         {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4463
4464     static const LPCSTR propval[] = {
4465         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4466         "ARPCONTACT",             "Contact",
4467         "ARPCOMMENTS",            "Comments",
4468         "ProductName",            "DisplayName",
4469         "ProductVersion",         "DisplayVersion",
4470         "ARPHELPLINK",            "HelpLink",
4471         "ARPHELPTELEPHONE",       "HelpTelephone",
4472         "ARPINSTALLLOCATION",     "InstallLocation",
4473         "SourceDir",              "InstallSource",
4474         "Manufacturer",           "Publisher",
4475         "ARPREADME",              "Readme",
4476         "ARPSIZE",                "Size",
4477         "ARPURLINFOABOUT",        "URLInfoAbout",
4478         "ARPURLUPDATEINFO",       "URLUpdateInfo",
4479         NULL,
4480     };
4481     const LPCSTR *p = propval;
4482
4483     while (*p)
4484     {
4485         prop = strdupAtoW(*p++);
4486         key = strdupAtoW(*p++);
4487         val = msi_dup_property(package, prop);
4488         msi_reg_set_val_str(hkey, key, val);
4489         msi_free(val);
4490         msi_free(key);
4491         msi_free(prop);
4492     }
4493
4494     msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4495
4496     size = deformat_string(package, modpath_fmt, &buffer);
4497     RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4498     RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4499     msi_free(buffer);
4500
4501     /* FIXME: Write real Estimated Size when we have it */
4502     msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4503
4504     buffer = msi_dup_property(package, szProductName);
4505     msi_reg_set_val_str(hkey, szDisplayName, buffer);
4506     msi_free(buffer);
4507
4508     buffer = msi_dup_property(package, cszSourceDir);
4509     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4510     msi_free(buffer);
4511
4512     buffer = msi_dup_property(package, szManufacturer);
4513     msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4514     msi_free(buffer);
4515
4516     GetLocalTime(&systime);
4517     sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4518     msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4519
4520     langid = msi_get_property_int(package, szProductLanguage, 0);
4521     msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4522
4523     buffer = msi_dup_property(package, szProductVersion);
4524     msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4525     if (buffer)
4526     {
4527         DWORD verdword = msi_version_str_to_dword(buffer);
4528
4529         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4530         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4531         msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4532         msi_free(buffer);
4533     }
4534
4535     return ERROR_SUCCESS;
4536 }
4537
4538 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4539 {
4540     WCHAR squashed_pc[SQUISH_GUID_SIZE];
4541     LPWSTR upgrade_code;
4542     HKEY hkey, props;
4543     HKEY upgrade;
4544     UINT rc;
4545
4546     static const WCHAR szUpgradeCode[] = {
4547         'U','p','g','r','a','d','e','C','o','d','e',0};
4548
4549     /* FIXME: also need to publish if the product is in advertise mode */
4550     if (!msi_check_publish(package))
4551         return ERROR_SUCCESS;
4552
4553     rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4554     if (rc != ERROR_SUCCESS)
4555         return rc;
4556
4557     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4558                                  NULL, &props, TRUE);
4559     if (rc != ERROR_SUCCESS)
4560         goto done;
4561
4562     msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4563     msi_free( package->db->localfile );
4564     package->db->localfile = NULL;
4565
4566     rc = msi_publish_install_properties(package, hkey);
4567     if (rc != ERROR_SUCCESS)
4568         goto done;
4569
4570     rc = msi_publish_install_properties(package, props);
4571     if (rc != ERROR_SUCCESS)
4572         goto done;
4573
4574     upgrade_code = msi_dup_property(package, szUpgradeCode);
4575     if (upgrade_code)
4576     {
4577         MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4578         squash_guid(package->ProductCode, squashed_pc);
4579         msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4580         RegCloseKey(upgrade);
4581         msi_free(upgrade_code);
4582     }
4583
4584 done:
4585     RegCloseKey(hkey);
4586
4587     return ERROR_SUCCESS;
4588 }
4589
4590 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4591 {
4592     return execute_script(package,INSTALL_SCRIPT);
4593 }
4594
4595 static UINT msi_unpublish_product(MSIPACKAGE *package)
4596 {
4597     LPWSTR upgrade;
4598     LPWSTR remove = NULL;
4599     LPWSTR *features = NULL;
4600     BOOL full_uninstall = TRUE;
4601     MSIFEATURE *feature;
4602
4603     static const WCHAR szUpgradeCode[] =
4604         {'U','p','g','r','a','d','e','C','o','d','e',0};
4605
4606     remove = msi_dup_property(package, szRemove);
4607     if (!remove)
4608         return ERROR_SUCCESS;
4609
4610     features = msi_split_string(remove, ',');
4611     if (!features)
4612     {
4613         msi_free(remove);
4614         ERR("REMOVE feature list is empty!\n");
4615         return ERROR_FUNCTION_FAILED;
4616     }
4617
4618     if (!lstrcmpW(features[0], szAll))
4619         full_uninstall = TRUE;
4620     else
4621     {
4622         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4623         {
4624             if (feature->Action != INSTALLSTATE_ABSENT)
4625                 full_uninstall = FALSE;
4626         }
4627     }
4628
4629     if (!full_uninstall)
4630         goto done;
4631
4632     MSIREG_DeleteProductKey(package->ProductCode);
4633     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4634     MSIREG_DeleteUninstallKey(package->ProductCode);
4635
4636     if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4637     {
4638         MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4639         MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4640     }
4641     else
4642     {
4643         MSIREG_DeleteUserProductKey(package->ProductCode);
4644         MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4645     }
4646
4647     upgrade = msi_dup_property(package, szUpgradeCode);
4648     if (upgrade)
4649     {
4650         MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4651         msi_free(upgrade);
4652     }
4653
4654 done:
4655     msi_free(remove);
4656     msi_free(features);
4657     return ERROR_SUCCESS;
4658 }
4659
4660 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4661 {
4662     UINT rc;
4663
4664     rc = msi_unpublish_product(package);
4665     if (rc != ERROR_SUCCESS)
4666         return rc;
4667
4668     /* turn off scheduling */
4669     package->script->CurrentlyScripting= FALSE;
4670
4671     /* first do the same as an InstallExecute */
4672     rc = ACTION_InstallExecute(package);
4673     if (rc != ERROR_SUCCESS)
4674         return rc;
4675
4676     /* then handle Commit Actions */
4677     rc = execute_script(package,COMMIT_SCRIPT);
4678
4679     return rc;
4680 }
4681
4682 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4683 {
4684     static const WCHAR RunOnce[] = {
4685     'S','o','f','t','w','a','r','e','\\',
4686     'M','i','c','r','o','s','o','f','t','\\',
4687     'W','i','n','d','o','w','s','\\',
4688     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4689     'R','u','n','O','n','c','e',0};
4690     static const WCHAR InstallRunOnce[] = {
4691     'S','o','f','t','w','a','r','e','\\',
4692     'M','i','c','r','o','s','o','f','t','\\',
4693     'W','i','n','d','o','w','s','\\',
4694     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4695     'I','n','s','t','a','l','l','e','r','\\',
4696     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4697
4698     static const WCHAR msiexec_fmt[] = {
4699     '%','s',
4700     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4701     '\"','%','s','\"',0};
4702     static const WCHAR install_fmt[] = {
4703     '/','I',' ','\"','%','s','\"',' ',
4704     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4705     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4706     WCHAR buffer[256], sysdir[MAX_PATH];
4707     HKEY hkey;
4708     WCHAR squished_pc[100];
4709
4710     squash_guid(package->ProductCode,squished_pc);
4711
4712     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4713     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4714     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4715      squished_pc);
4716
4717     msi_reg_set_val_str( hkey, squished_pc, buffer );
4718     RegCloseKey(hkey);
4719
4720     TRACE("Reboot command %s\n",debugstr_w(buffer));
4721
4722     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4723     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4724
4725     msi_reg_set_val_str( hkey, squished_pc, buffer );
4726     RegCloseKey(hkey);
4727
4728     return ERROR_INSTALL_SUSPEND;
4729 }
4730
4731 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4732 {
4733     DWORD attrib;
4734     UINT rc;
4735
4736     /*
4737      * We are currently doing what should be done here in the top level Install
4738      * however for Administrative and uninstalls this step will be needed
4739      */
4740     if (!package->PackagePath)
4741         return ERROR_SUCCESS;
4742
4743     msi_set_sourcedir_props(package, TRUE);
4744
4745     attrib = GetFileAttributesW(package->db->path);
4746     if (attrib == INVALID_FILE_ATTRIBUTES)
4747     {
4748         LPWSTR prompt;
4749         LPWSTR msg;
4750         DWORD size = 0;
4751
4752         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4753                 package->Context, MSICODE_PRODUCT,
4754                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4755         if (rc == ERROR_MORE_DATA)
4756         {
4757             prompt = msi_alloc(size * sizeof(WCHAR));
4758             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4759                     package->Context, MSICODE_PRODUCT,
4760                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4761         }
4762         else
4763             prompt = strdupW(package->db->path);
4764
4765         msg = generate_error_string(package,1302,1,prompt);
4766         while(attrib == INVALID_FILE_ATTRIBUTES)
4767         {
4768             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4769             if (rc == IDCANCEL)
4770             {
4771                 rc = ERROR_INSTALL_USEREXIT;
4772                 break;
4773             }
4774             attrib = GetFileAttributesW(package->db->path);
4775         }
4776         msi_free(prompt);
4777         rc = ERROR_SUCCESS;
4778     }
4779     else
4780         return ERROR_SUCCESS;
4781
4782     return rc;
4783 }
4784
4785 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4786 {
4787     HKEY hkey=0;
4788     LPWSTR buffer;
4789     LPWSTR productid;
4790     UINT rc,i;
4791
4792     static const WCHAR szPropKeys[][80] = 
4793     {
4794         {'P','r','o','d','u','c','t','I','D',0},
4795         {'U','S','E','R','N','A','M','E',0},
4796         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4797         {0},
4798     };
4799
4800     static const WCHAR szRegKeys[][80] = 
4801     {
4802         {'P','r','o','d','u','c','t','I','D',0},
4803         {'R','e','g','O','w','n','e','r',0},
4804         {'R','e','g','C','o','m','p','a','n','y',0},
4805         {0},
4806     };
4807
4808     if (msi_check_unpublish(package))
4809     {
4810         MSIREG_DeleteUserDataProductKey(package->ProductCode);
4811         return ERROR_SUCCESS;
4812     }
4813
4814     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4815     if (!productid)
4816         return ERROR_SUCCESS;
4817
4818     rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4819                                  NULL, &hkey, TRUE);
4820     if (rc != ERROR_SUCCESS)
4821         goto end;
4822
4823     for( i = 0; szPropKeys[i][0]; i++ )
4824     {
4825         buffer = msi_dup_property( package, szPropKeys[i] );
4826         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4827         msi_free( buffer );
4828     }
4829
4830 end:
4831     msi_free(productid);
4832     RegCloseKey(hkey);
4833
4834     /* FIXME: call ui_actiondata */
4835
4836     return rc;
4837 }
4838
4839
4840 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4841 {
4842     UINT rc;
4843
4844     package->script->InWhatSequence |= SEQUENCE_EXEC;
4845     rc = ACTION_ProcessExecSequence(package,FALSE);
4846     return rc;
4847 }
4848
4849
4850 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4851 {
4852     MSIPACKAGE *package = param;
4853     LPCWSTR compgroupid, component, feature, qualifier, text;
4854     LPWSTR advertise = NULL, output = NULL;
4855     HKEY hkey = NULL;
4856     UINT rc;
4857     MSICOMPONENT *comp;
4858     MSIFEATURE *feat;
4859     DWORD sz;
4860     MSIRECORD *uirow;
4861
4862     feature = MSI_RecordGetString(rec, 5);
4863     feat = get_loaded_feature(package, feature);
4864     if (!feat)
4865         return ERROR_SUCCESS;
4866
4867     if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
4868         feat->ActionRequest != INSTALLSTATE_SOURCE &&
4869         feat->ActionRequest != INSTALLSTATE_ADVERTISED)
4870     {
4871         TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
4872         feat->Action = feat->Installed;
4873         return ERROR_SUCCESS;
4874     }
4875
4876     component = MSI_RecordGetString(rec, 3);
4877     comp = get_loaded_component(package, component);
4878     if (!comp)
4879         return ERROR_SUCCESS;
4880
4881     compgroupid = MSI_RecordGetString(rec,1);
4882     qualifier = MSI_RecordGetString(rec,2);
4883
4884     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4885     if (rc != ERROR_SUCCESS)
4886         goto end;
4887     
4888     text = MSI_RecordGetString(rec,4);
4889     advertise = create_component_advertise_string(package, comp, feature);
4890
4891     sz = strlenW(advertise);
4892
4893     if (text)
4894         sz += lstrlenW(text);
4895
4896     sz+=3;
4897     sz *= sizeof(WCHAR);
4898            
4899     output = msi_alloc_zero(sz);
4900     strcpyW(output,advertise);
4901     msi_free(advertise);
4902
4903     if (text)
4904         strcatW(output,text);
4905
4906     msi_reg_set_val_multi_str( hkey, qualifier, output );
4907     
4908 end:
4909     RegCloseKey(hkey);
4910     msi_free(output);
4911
4912     /* the UI chunk */
4913     uirow = MSI_CreateRecord( 2 );
4914     MSI_RecordSetStringW( uirow, 1, compgroupid );
4915     MSI_RecordSetStringW( uirow, 2, qualifier);
4916     ui_actiondata( package, szPublishComponents, uirow);
4917     msiobj_release( &uirow->hdr );
4918     /* FIXME: call ui_progress? */
4919
4920     return rc;
4921 }
4922
4923 /*
4924  * At present I am ignorning the advertised components part of this and only
4925  * focusing on the qualified component sets
4926  */
4927 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4928 {
4929     UINT rc;
4930     MSIQUERY * view;
4931     static const WCHAR ExecSeqQuery[] =
4932         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4933          '`','P','u','b','l','i','s','h',
4934          'C','o','m','p','o','n','e','n','t','`',0};
4935     
4936     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4937     if (rc != ERROR_SUCCESS)
4938         return ERROR_SUCCESS;
4939
4940     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4941     msiobj_release(&view->hdr);
4942
4943     return rc;
4944 }
4945
4946 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
4947 {
4948     static const WCHAR szInstallerComponents[] = {
4949         'S','o','f','t','w','a','r','e','\\',
4950         'M','i','c','r','o','s','o','f','t','\\',
4951         'I','n','s','t','a','l','l','e','r','\\',
4952         'C','o','m','p','o','n','e','n','t','s','\\',0};
4953
4954     MSIPACKAGE *package = param;
4955     LPCWSTR compgroupid, component, feature, qualifier;
4956     MSICOMPONENT *comp;
4957     MSIFEATURE *feat;
4958     MSIRECORD *uirow;
4959     WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
4960     LONG res;
4961
4962     feature = MSI_RecordGetString( rec, 5 );
4963     feat = get_loaded_feature( package, feature );
4964     if (!feat)
4965         return ERROR_SUCCESS;
4966
4967     if (feat->ActionRequest != INSTALLSTATE_ABSENT)
4968     {
4969         TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
4970         feat->Action = feat->Installed;
4971         return ERROR_SUCCESS;
4972     }
4973
4974     component = MSI_RecordGetString( rec, 3 );
4975     comp = get_loaded_component( package, component );
4976     if (!comp)
4977         return ERROR_SUCCESS;
4978
4979     compgroupid = MSI_RecordGetString( rec, 1 );
4980     qualifier = MSI_RecordGetString( rec, 2 );
4981
4982     squash_guid( compgroupid, squashed );
4983     strcpyW( keypath, szInstallerComponents );
4984     strcatW( keypath, squashed );
4985
4986     res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
4987     if (res != ERROR_SUCCESS)
4988     {
4989         WARN("Unable to delete component key %d\n", res);
4990     }
4991
4992     uirow = MSI_CreateRecord( 2 );
4993     MSI_RecordSetStringW( uirow, 1, compgroupid );
4994     MSI_RecordSetStringW( uirow, 2, qualifier );
4995     ui_actiondata( package, szUnpublishComponents, uirow );
4996     msiobj_release( &uirow->hdr );
4997
4998     return ERROR_SUCCESS;
4999 }
5000
5001 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5002 {
5003     UINT rc;
5004     MSIQUERY *view;
5005     static const WCHAR query[] =
5006         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5007          '`','P','u','b','l','i','s','h',
5008          'C','o','m','p','o','n','e','n','t','`',0};
5009
5010     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5011     if (rc != ERROR_SUCCESS)
5012         return ERROR_SUCCESS;
5013
5014     rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5015     msiobj_release( &view->hdr );
5016
5017     return rc;
5018 }
5019
5020 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5021 {
5022     MSIPACKAGE *package = param;
5023     MSIRECORD *row;
5024     MSIFILE *file;
5025     SC_HANDLE hscm, service = NULL;
5026     LPCWSTR comp, depends, pass;
5027     LPWSTR name = NULL, disp = NULL;
5028     LPCWSTR load_order, serv_name, key;
5029     DWORD serv_type, start_type;
5030     DWORD err_control;
5031
5032     static const WCHAR query[] =
5033         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5034          '`','C','o','m','p','o','n','e','n','t','`',' ',
5035          'W','H','E','R','E',' ',
5036          '`','C','o','m','p','o','n','e','n','t','`',' ',
5037          '=','\'','%','s','\'',0};
5038
5039     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5040     if (!hscm)
5041     {
5042         ERR("Failed to open the SC Manager!\n");
5043         goto done;
5044     }
5045
5046     start_type = MSI_RecordGetInteger(rec, 5);
5047     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5048         goto done;
5049
5050     depends = MSI_RecordGetString(rec, 8);
5051     if (depends && *depends)
5052         FIXME("Dependency list unhandled!\n");
5053
5054     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5055     deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5056     serv_type = MSI_RecordGetInteger(rec, 4);
5057     err_control = MSI_RecordGetInteger(rec, 6);
5058     load_order = MSI_RecordGetString(rec, 7);
5059     serv_name = MSI_RecordGetString(rec, 9);
5060     pass = MSI_RecordGetString(rec, 10);
5061     comp = MSI_RecordGetString(rec, 12);
5062
5063     /* fetch the service path */
5064     row = MSI_QueryGetRecord(package->db, query, comp);
5065     if (!row)
5066     {
5067         ERR("Control query failed!\n");
5068         goto done;
5069     }
5070
5071     key = MSI_RecordGetString(row, 6);
5072
5073     file = get_loaded_file(package, key);
5074     msiobj_release(&row->hdr);
5075     if (!file)
5076     {
5077         ERR("Failed to load the service file\n");
5078         goto done;
5079     }
5080
5081     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5082                              start_type, err_control, file->TargetPath,
5083                              load_order, NULL, NULL, serv_name, pass);
5084     if (!service)
5085     {
5086         if (GetLastError() != ERROR_SERVICE_EXISTS)
5087             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5088     }
5089
5090 done:
5091     CloseServiceHandle(service);
5092     CloseServiceHandle(hscm);
5093     msi_free(name);
5094     msi_free(disp);
5095
5096     return ERROR_SUCCESS;
5097 }
5098
5099 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5100 {
5101     UINT rc;
5102     MSIQUERY * view;
5103     static const WCHAR ExecSeqQuery[] =
5104         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5105          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5106     
5107     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5108     if (rc != ERROR_SUCCESS)
5109         return ERROR_SUCCESS;
5110
5111     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5112     msiobj_release(&view->hdr);
5113
5114     return rc;
5115 }
5116
5117 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5118 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5119 {
5120     LPCWSTR *vector, *temp_vector;
5121     LPWSTR p, q;
5122     DWORD sep_len;
5123
5124     static const WCHAR separator[] = {'[','~',']',0};
5125
5126     *numargs = 0;
5127     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5128
5129     if (!args)
5130         return NULL;
5131
5132     vector = msi_alloc(sizeof(LPWSTR));
5133     if (!vector)
5134         return NULL;
5135
5136     p = args;
5137     do
5138     {
5139         (*numargs)++;
5140         vector[*numargs - 1] = p;
5141
5142         if ((q = strstrW(p, separator)))
5143         {
5144             *q = '\0';
5145
5146             temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5147             if (!temp_vector)
5148             {
5149                 msi_free(vector);
5150                 return NULL;
5151             }
5152             vector = temp_vector;
5153
5154             p = q + sep_len;
5155         }
5156     } while (q);
5157
5158     return vector;
5159 }
5160
5161 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5162 {
5163     MSIPACKAGE *package = param;
5164     MSICOMPONENT *comp;
5165     SC_HANDLE scm = NULL, service = NULL;
5166     LPCWSTR component, *vector = NULL;
5167     LPWSTR name, args;
5168     DWORD event, numargs;
5169     UINT r = ERROR_FUNCTION_FAILED;
5170
5171     component = MSI_RecordGetString(rec, 6);
5172     comp = get_loaded_component(package, component);
5173     if (!comp)
5174         return ERROR_SUCCESS;
5175
5176     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5177     {
5178         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5179         comp->Action = comp->Installed;
5180         return ERROR_SUCCESS;
5181     }
5182     comp->Action = INSTALLSTATE_LOCAL;
5183
5184     deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5185     deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5186     event = MSI_RecordGetInteger(rec, 3);
5187
5188     if (!(event & msidbServiceControlEventStart))
5189     {
5190         r = ERROR_SUCCESS;
5191         goto done;
5192     }
5193
5194     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5195     if (!scm)
5196     {
5197         ERR("Failed to open the service control manager\n");
5198         goto done;
5199     }
5200
5201     service = OpenServiceW(scm, name, SERVICE_START);
5202     if (!service)
5203     {
5204         ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5205         goto done;
5206     }
5207
5208     vector = msi_service_args_to_vector(args, &numargs);
5209
5210     if (!StartServiceW(service, numargs, vector) &&
5211         GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5212     {
5213         ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5214         goto done;
5215     }
5216
5217     r = ERROR_SUCCESS;
5218
5219 done:
5220     CloseServiceHandle(service);
5221     CloseServiceHandle(scm);
5222
5223     msi_free(name);
5224     msi_free(args);
5225     msi_free(vector);
5226     return r;
5227 }
5228
5229 static UINT ACTION_StartServices( MSIPACKAGE *package )
5230 {
5231     UINT rc;
5232     MSIQUERY *view;
5233
5234     static const WCHAR query[] = {
5235         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5236         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5237
5238     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5239     if (rc != ERROR_SUCCESS)
5240         return ERROR_SUCCESS;
5241
5242     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5243     msiobj_release(&view->hdr);
5244
5245     return rc;
5246 }
5247
5248 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5249 {
5250     DWORD i, needed, count;
5251     ENUM_SERVICE_STATUSW *dependencies;
5252     SERVICE_STATUS ss;
5253     SC_HANDLE depserv;
5254
5255     if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5256                                0, &needed, &count))
5257         return TRUE;
5258
5259     if (GetLastError() != ERROR_MORE_DATA)
5260         return FALSE;
5261
5262     dependencies = msi_alloc(needed);
5263     if (!dependencies)
5264         return FALSE;
5265
5266     if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5267                                 needed, &needed, &count))
5268         goto error;
5269
5270     for (i = 0; i < count; i++)
5271     {
5272         depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5273                                SERVICE_STOP | SERVICE_QUERY_STATUS);
5274         if (!depserv)
5275             goto error;
5276
5277         if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5278             goto error;
5279     }
5280
5281     return TRUE;
5282
5283 error:
5284     msi_free(dependencies);
5285     return FALSE;
5286 }
5287
5288 static UINT stop_service( LPCWSTR name )
5289 {
5290     SC_HANDLE scm = NULL, service = NULL;
5291     SERVICE_STATUS status;
5292     SERVICE_STATUS_PROCESS ssp;
5293     DWORD needed;
5294
5295     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5296     if (!scm)
5297     {
5298         WARN("Failed to open the SCM: %d\n", GetLastError());
5299         goto done;
5300     }
5301
5302     service = OpenServiceW(scm, name,
5303                            SERVICE_STOP |
5304                            SERVICE_QUERY_STATUS |
5305                            SERVICE_ENUMERATE_DEPENDENTS);
5306     if (!service)
5307     {
5308         WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5309         goto done;
5310     }
5311
5312     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5313                               sizeof(SERVICE_STATUS_PROCESS), &needed))
5314     {
5315         WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5316         goto done;
5317     }
5318
5319     if (ssp.dwCurrentState == SERVICE_STOPPED)
5320         goto done;
5321
5322     stop_service_dependents(scm, service);
5323
5324     if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5325         WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5326
5327 done:
5328     CloseServiceHandle(service);
5329     CloseServiceHandle(scm);
5330
5331     return ERROR_SUCCESS;
5332 }
5333
5334 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5335 {
5336     MSIPACKAGE *package = param;
5337     MSICOMPONENT *comp;
5338     LPCWSTR component;
5339     LPWSTR name;
5340     DWORD event;
5341
5342     event = MSI_RecordGetInteger( rec, 3 );
5343     if (!(event & msidbServiceControlEventStop))
5344         return ERROR_SUCCESS;
5345
5346     component = MSI_RecordGetString( rec, 6 );
5347     comp = get_loaded_component( package, component );
5348     if (!comp)
5349         return ERROR_SUCCESS;
5350
5351     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5352     {
5353         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5354         comp->Action = comp->Installed;
5355         return ERROR_SUCCESS;
5356     }
5357     comp->Action = INSTALLSTATE_ABSENT;
5358
5359     deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5360     stop_service( name );
5361     msi_free( name );
5362
5363     return ERROR_SUCCESS;
5364 }
5365
5366 static UINT ACTION_StopServices( MSIPACKAGE *package )
5367 {
5368     UINT rc;
5369     MSIQUERY *view;
5370
5371     static const WCHAR query[] = {
5372         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5373         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5374
5375     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5376     if (rc != ERROR_SUCCESS)
5377         return ERROR_SUCCESS;
5378
5379     rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5380     msiobj_release(&view->hdr);
5381
5382     return rc;
5383 }
5384
5385 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5386 {
5387     MSIPACKAGE *package = param;
5388     MSICOMPONENT *comp;
5389     LPCWSTR component;
5390     LPWSTR name = NULL;
5391     DWORD event;
5392     SC_HANDLE scm = NULL, service = NULL;
5393
5394     event = MSI_RecordGetInteger( rec, 3 );
5395     if (!(event & msidbServiceControlEventDelete))
5396         return ERROR_SUCCESS;
5397
5398     component = MSI_RecordGetString(rec, 6);
5399     comp = get_loaded_component(package, component);
5400     if (!comp)
5401         return ERROR_SUCCESS;
5402
5403     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5404     {
5405         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5406         comp->Action = comp->Installed;
5407         return ERROR_SUCCESS;
5408     }
5409     comp->Action = INSTALLSTATE_ABSENT;
5410
5411     deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5412     stop_service( name );
5413
5414     scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5415     if (!scm)
5416     {
5417         WARN("Failed to open the SCM: %d\n", GetLastError());
5418         goto done;
5419     }
5420
5421     service = OpenServiceW( scm, name, DELETE );
5422     if (!service)
5423     {
5424         WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5425         goto done;
5426     }
5427
5428     if (!DeleteService( service ))
5429         WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5430
5431 done:
5432     CloseServiceHandle( service );
5433     CloseServiceHandle( scm );
5434     msi_free( name );
5435
5436     return ERROR_SUCCESS;
5437 }
5438
5439 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5440 {
5441     UINT rc;
5442     MSIQUERY *view;
5443
5444     static const WCHAR query[] = {
5445         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5446         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5447
5448     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5449     if (rc != ERROR_SUCCESS)
5450         return ERROR_SUCCESS;
5451
5452     rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5453     msiobj_release( &view->hdr );
5454
5455     return rc;
5456 }
5457
5458 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5459 {
5460     MSIFILE *file;
5461
5462     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5463     {
5464         if (!lstrcmpW(file->File, filename))
5465             return file;
5466     }
5467
5468     return NULL;
5469 }
5470
5471 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5472 {
5473     MSIPACKAGE *package = param;
5474     LPWSTR driver, driver_path, ptr;
5475     WCHAR outpath[MAX_PATH];
5476     MSIFILE *driver_file, *setup_file;
5477     LPCWSTR desc;
5478     DWORD len, usage;
5479     UINT r = ERROR_SUCCESS;
5480
5481     static const WCHAR driver_fmt[] = {
5482         'D','r','i','v','e','r','=','%','s',0};
5483     static const WCHAR setup_fmt[] = {
5484         'S','e','t','u','p','=','%','s',0};
5485     static const WCHAR usage_fmt[] = {
5486         'F','i','l','e','U','s','a','g','e','=','1',0};
5487
5488     desc = MSI_RecordGetString(rec, 3);
5489
5490     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5491     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5492
5493     if (!driver_file)
5494     {
5495         ERR("ODBC Driver entry not found!\n");
5496         return ERROR_FUNCTION_FAILED;
5497     }
5498
5499     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5500     if (setup_file)
5501         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5502     len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5503
5504     driver = msi_alloc(len * sizeof(WCHAR));
5505     if (!driver)
5506         return ERROR_OUTOFMEMORY;
5507
5508     ptr = driver;
5509     lstrcpyW(ptr, desc);
5510     ptr += lstrlenW(ptr) + 1;
5511
5512     len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5513     ptr += len + 1;
5514
5515     if (setup_file)
5516     {
5517         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5518         ptr += len + 1;
5519     }
5520
5521     lstrcpyW(ptr, usage_fmt);
5522     ptr += lstrlenW(ptr) + 1;
5523     *ptr = '\0';
5524
5525     driver_path = strdupW(driver_file->TargetPath);
5526     ptr = strrchrW(driver_path, '\\');
5527     if (ptr) *ptr = '\0';
5528
5529     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5530                              NULL, ODBC_INSTALL_COMPLETE, &usage))
5531     {
5532         ERR("Failed to install SQL driver!\n");
5533         r = ERROR_FUNCTION_FAILED;
5534     }
5535
5536     msi_free(driver);
5537     msi_free(driver_path);
5538
5539     return r;
5540 }
5541
5542 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5543 {
5544     MSIPACKAGE *package = param;
5545     LPWSTR translator, translator_path, ptr;
5546     WCHAR outpath[MAX_PATH];
5547     MSIFILE *translator_file, *setup_file;
5548     LPCWSTR desc;
5549     DWORD len, usage;
5550     UINT r = ERROR_SUCCESS;
5551
5552     static const WCHAR translator_fmt[] = {
5553         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5554     static const WCHAR setup_fmt[] = {
5555         'S','e','t','u','p','=','%','s',0};
5556
5557     desc = MSI_RecordGetString(rec, 3);
5558
5559     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5560     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5561
5562     if (!translator_file)
5563     {
5564         ERR("ODBC Translator entry not found!\n");
5565         return ERROR_FUNCTION_FAILED;
5566     }
5567
5568     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5569     if (setup_file)
5570         len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5571
5572     translator = msi_alloc(len * sizeof(WCHAR));
5573     if (!translator)
5574         return ERROR_OUTOFMEMORY;
5575
5576     ptr = translator;
5577     lstrcpyW(ptr, desc);
5578     ptr += lstrlenW(ptr) + 1;
5579
5580     len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5581     ptr += len + 1;
5582
5583     if (setup_file)
5584     {
5585         len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5586         ptr += len + 1;
5587     }
5588     *ptr = '\0';
5589
5590     translator_path = strdupW(translator_file->TargetPath);
5591     ptr = strrchrW(translator_path, '\\');
5592     if (ptr) *ptr = '\0';
5593
5594     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5595                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
5596     {
5597         ERR("Failed to install SQL translator!\n");
5598         r = ERROR_FUNCTION_FAILED;
5599     }
5600
5601     msi_free(translator);
5602     msi_free(translator_path);
5603
5604     return r;
5605 }
5606
5607 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5608 {
5609     LPWSTR attrs;
5610     LPCWSTR desc, driver;
5611     WORD request = ODBC_ADD_SYS_DSN;
5612     INT registration;
5613     DWORD len;
5614     UINT r = ERROR_SUCCESS;
5615
5616     static const WCHAR attrs_fmt[] = {
5617         'D','S','N','=','%','s',0 };
5618
5619     desc = MSI_RecordGetString(rec, 3);
5620     driver = MSI_RecordGetString(rec, 4);
5621     registration = MSI_RecordGetInteger(rec, 5);
5622
5623     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5624     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5625
5626     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5627     attrs = msi_alloc(len * sizeof(WCHAR));
5628     if (!attrs)
5629         return ERROR_OUTOFMEMORY;
5630
5631     len = sprintfW(attrs, attrs_fmt, desc);
5632     attrs[len + 1] = 0;
5633
5634     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5635     {
5636         ERR("Failed to install SQL data source!\n");
5637         r = ERROR_FUNCTION_FAILED;
5638     }
5639
5640     msi_free(attrs);
5641
5642     return r;
5643 }
5644
5645 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5646 {
5647     UINT rc;
5648     MSIQUERY *view;
5649
5650     static const WCHAR driver_query[] = {
5651         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5652         'O','D','B','C','D','r','i','v','e','r',0 };
5653
5654     static const WCHAR translator_query[] = {
5655         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5656         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5657
5658     static const WCHAR source_query[] = {
5659         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5660         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5661
5662     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5663     if (rc != ERROR_SUCCESS)
5664         return ERROR_SUCCESS;
5665
5666     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5667     msiobj_release(&view->hdr);
5668
5669     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5670     if (rc != ERROR_SUCCESS)
5671         return ERROR_SUCCESS;
5672
5673     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5674     msiobj_release(&view->hdr);
5675
5676     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5677     if (rc != ERROR_SUCCESS)
5678         return ERROR_SUCCESS;
5679
5680     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5681     msiobj_release(&view->hdr);
5682
5683     return rc;
5684 }
5685
5686 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5687 {
5688     DWORD usage;
5689     LPCWSTR desc;
5690
5691     desc = MSI_RecordGetString( rec, 3 );
5692     if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5693     {
5694         WARN("Failed to remove ODBC driver\n");
5695     }
5696     else if (!usage)
5697     {
5698         FIXME("Usage count reached 0\n");
5699     }
5700
5701     return ERROR_SUCCESS;
5702 }
5703
5704 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5705 {
5706     DWORD usage;
5707     LPCWSTR desc;
5708
5709     desc = MSI_RecordGetString( rec, 3 );
5710     if (!SQLRemoveTranslatorW( desc, &usage ))
5711     {
5712         WARN("Failed to remove ODBC translator\n");
5713     }
5714     else if (!usage)
5715     {
5716         FIXME("Usage count reached 0\n");
5717     }
5718
5719     return ERROR_SUCCESS;
5720 }
5721
5722 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5723 {
5724     LPWSTR attrs;
5725     LPCWSTR desc, driver;
5726     WORD request = ODBC_REMOVE_SYS_DSN;
5727     INT registration;
5728     DWORD len;
5729
5730     static const WCHAR attrs_fmt[] = {
5731         'D','S','N','=','%','s',0 };
5732
5733     desc = MSI_RecordGetString( rec, 3 );
5734     driver = MSI_RecordGetString( rec, 4 );
5735     registration = MSI_RecordGetInteger( rec, 5 );
5736
5737     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
5738     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
5739
5740     len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
5741     attrs = msi_alloc( len * sizeof(WCHAR) );
5742     if (!attrs)
5743         return ERROR_OUTOFMEMORY;
5744
5745     FIXME("Use ODBCSourceAttribute table\n");
5746
5747     len = sprintfW( attrs, attrs_fmt, desc );
5748     attrs[len + 1] = 0;
5749
5750     if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
5751     {
5752         WARN("Failed to remove ODBC data source\n");
5753     }
5754     msi_free( attrs );
5755
5756     return ERROR_SUCCESS;
5757 }
5758
5759 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5760 {
5761     UINT rc;
5762     MSIQUERY *view;
5763
5764     static const WCHAR driver_query[] = {
5765         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5766         'O','D','B','C','D','r','i','v','e','r',0 };
5767
5768     static const WCHAR translator_query[] = {
5769         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5770         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5771
5772     static const WCHAR source_query[] = {
5773         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5774         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5775
5776     rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
5777     if (rc != ERROR_SUCCESS)
5778         return ERROR_SUCCESS;
5779
5780     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
5781     msiobj_release( &view->hdr );
5782
5783     rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
5784     if (rc != ERROR_SUCCESS)
5785         return ERROR_SUCCESS;
5786
5787     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
5788     msiobj_release( &view->hdr );
5789
5790     rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
5791     if (rc != ERROR_SUCCESS)
5792         return ERROR_SUCCESS;
5793
5794     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
5795     msiobj_release( &view->hdr );
5796
5797     return rc;
5798 }
5799
5800 #define ENV_ACT_SETALWAYS   0x1
5801 #define ENV_ACT_SETABSENT   0x2
5802 #define ENV_ACT_REMOVE      0x4
5803 #define ENV_ACT_REMOVEMATCH 0x8
5804
5805 #define ENV_MOD_MACHINE     0x20000000
5806 #define ENV_MOD_APPEND      0x40000000
5807 #define ENV_MOD_PREFIX      0x80000000
5808 #define ENV_MOD_MASK        0xC0000000
5809
5810 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5811
5812 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5813 {
5814     LPCWSTR cptr = *name;
5815
5816     static const WCHAR prefix[] = {'[','~',']',0};
5817     static const int prefix_len = 3;
5818
5819     *flags = 0;
5820     while (*cptr)
5821     {
5822         if (*cptr == '=')
5823             *flags |= ENV_ACT_SETALWAYS;
5824         else if (*cptr == '+')
5825             *flags |= ENV_ACT_SETABSENT;
5826         else if (*cptr == '-')
5827             *flags |= ENV_ACT_REMOVE;
5828         else if (*cptr == '!')
5829             *flags |= ENV_ACT_REMOVEMATCH;
5830         else if (*cptr == '*')
5831             *flags |= ENV_MOD_MACHINE;
5832         else
5833             break;
5834
5835         cptr++;
5836         (*name)++;
5837     }
5838
5839     if (!*cptr)
5840     {
5841         ERR("Missing environment variable\n");
5842         return ERROR_FUNCTION_FAILED;
5843     }
5844
5845     if (*value)
5846     {
5847         LPCWSTR ptr = *value;
5848         if (!strncmpW(ptr, prefix, prefix_len))
5849         {
5850             if (ptr[prefix_len] == szSemiColon[0])
5851             {
5852                 *flags |= ENV_MOD_APPEND;
5853                 *value += lstrlenW(prefix);
5854             }
5855             else
5856             {
5857                 *value = NULL;
5858             }
5859         }
5860         else if (lstrlenW(*value) >= prefix_len)
5861         {
5862             ptr += lstrlenW(ptr) - prefix_len;
5863             if (!lstrcmpW(ptr, prefix))
5864             {
5865                 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5866                 {
5867                     *flags |= ENV_MOD_PREFIX;
5868                     /* the "[~]" will be removed by deformat_string */;
5869                 }
5870                 else
5871                 {
5872                     *value = NULL;
5873                 }
5874             }
5875         }
5876     }
5877
5878     if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5879         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5880         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5881         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5882     {
5883         ERR("Invalid flags: %08x\n", *flags);
5884         return ERROR_FUNCTION_FAILED;
5885     }
5886
5887     if (!*flags)
5888         *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5889
5890     return ERROR_SUCCESS;
5891 }
5892
5893 static UINT open_env_key( DWORD flags, HKEY *key )
5894 {
5895     static const WCHAR user_env[] =
5896         {'E','n','v','i','r','o','n','m','e','n','t',0};
5897     static const WCHAR machine_env[] =
5898         {'S','y','s','t','e','m','\\',
5899          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5900          'C','o','n','t','r','o','l','\\',
5901          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5902          'E','n','v','i','r','o','n','m','e','n','t',0};
5903     const WCHAR *env;
5904     HKEY root;
5905     LONG res;
5906
5907     if (flags & ENV_MOD_MACHINE)
5908     {
5909         env = machine_env;
5910         root = HKEY_LOCAL_MACHINE;
5911     }
5912     else
5913     {
5914         env = user_env;
5915         root = HKEY_CURRENT_USER;
5916     }
5917
5918     res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
5919     if (res != ERROR_SUCCESS)
5920     {
5921         WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
5922         return ERROR_FUNCTION_FAILED;
5923     }
5924
5925     return ERROR_SUCCESS;
5926 }
5927
5928 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5929 {
5930     MSIPACKAGE *package = param;
5931     LPCWSTR name, value, component;
5932     LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
5933     DWORD flags, type, size;
5934     UINT res;
5935     HKEY env = NULL;
5936     MSICOMPONENT *comp;
5937     MSIRECORD *uirow;
5938     int action = 0;
5939
5940     component = MSI_RecordGetString(rec, 4);
5941     comp = get_loaded_component(package, component);
5942     if (!comp)
5943         return ERROR_SUCCESS;
5944
5945     if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5946     {
5947         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5948         comp->Action = comp->Installed;
5949         return ERROR_SUCCESS;
5950     }
5951     comp->Action = INSTALLSTATE_LOCAL;
5952
5953     name = MSI_RecordGetString(rec, 2);
5954     value = MSI_RecordGetString(rec, 3);
5955
5956     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5957
5958     res = env_parse_flags(&name, &value, &flags);
5959     if (res != ERROR_SUCCESS || !value)
5960        goto done;
5961
5962     if (value && !deformat_string(package, value, &deformatted))
5963     {
5964         res = ERROR_OUTOFMEMORY;
5965         goto done;
5966     }
5967
5968     value = deformatted;
5969
5970     res = open_env_key( flags, &env );
5971     if (res != ERROR_SUCCESS)
5972         goto done;
5973
5974     if (flags & ENV_MOD_MACHINE)
5975         action |= 0x20000000;
5976
5977     size = 0;
5978     type = REG_SZ;
5979     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5980     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5981         (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5982         goto done;
5983
5984     if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5985     {
5986         action = 0x2;
5987
5988         /* Nothing to do. */
5989         if (!value)
5990         {
5991             res = ERROR_SUCCESS;
5992             goto done;
5993         }
5994
5995         /* If we are appending but the string was empty, strip ; */
5996         if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5997
5998         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5999         newval = strdupW(value);
6000         if (!newval)
6001         {
6002             res = ERROR_OUTOFMEMORY;
6003             goto done;
6004         }
6005     }
6006     else
6007     {
6008         action = 0x1;
6009
6010         /* Contrary to MSDN, +-variable to [~];path works */
6011         if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6012         {
6013             res = ERROR_SUCCESS;
6014             goto done;
6015         }
6016
6017         data = msi_alloc(size);
6018         if (!data)
6019         {
6020             RegCloseKey(env);
6021             return ERROR_OUTOFMEMORY;
6022         }
6023
6024         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6025         if (res != ERROR_SUCCESS)
6026             goto done;
6027
6028         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
6029         {
6030             action = 0x4;
6031             res = RegDeleteValueW(env, name);
6032             if (res != ERROR_SUCCESS)
6033                 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6034             goto done;
6035         }
6036
6037         size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6038         if (flags & ENV_MOD_MASK)
6039         {
6040             DWORD mod_size;
6041             int multiplier = 0;
6042             if (flags & ENV_MOD_APPEND) multiplier++;
6043             if (flags & ENV_MOD_PREFIX) multiplier++;
6044             mod_size = lstrlenW(value) * multiplier;
6045             size += mod_size * sizeof(WCHAR);
6046         }
6047
6048         newval = msi_alloc(size);
6049         ptr = newval;
6050         if (!newval)
6051         {
6052             res = ERROR_OUTOFMEMORY;
6053             goto done;
6054         }
6055
6056         if (flags & ENV_MOD_PREFIX)
6057         {
6058             lstrcpyW(newval, value);
6059             ptr = newval + lstrlenW(value);
6060             action |= 0x80000000;
6061         }
6062
6063         lstrcpyW(ptr, data);
6064
6065         if (flags & ENV_MOD_APPEND)
6066         {
6067             lstrcatW(newval, value);
6068             action |= 0x40000000;
6069         }
6070     }
6071     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6072     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6073     if (res)
6074     {
6075         WARN("Failed to set %s to %s (%d)\n",  debugstr_w(name), debugstr_w(newval), res);
6076     }
6077
6078 done:
6079     uirow = MSI_CreateRecord( 3 );
6080     MSI_RecordSetStringW( uirow, 1, name );
6081     MSI_RecordSetStringW( uirow, 2, newval );
6082     MSI_RecordSetInteger( uirow, 3, action );
6083     ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6084     msiobj_release( &uirow->hdr );
6085
6086     if (env) RegCloseKey(env);
6087     msi_free(deformatted);
6088     msi_free(data);
6089     msi_free(newval);
6090     return res;
6091 }
6092
6093 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6094 {
6095     UINT rc;
6096     MSIQUERY * view;
6097     static const WCHAR ExecSeqQuery[] =
6098         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6099          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6100     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6101     if (rc != ERROR_SUCCESS)
6102         return ERROR_SUCCESS;
6103
6104     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6105     msiobj_release(&view->hdr);
6106
6107     return rc;
6108 }
6109
6110 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6111 {
6112     MSIPACKAGE *package = param;
6113     LPCWSTR name, value, component;
6114     LPWSTR deformatted = NULL;
6115     DWORD flags;
6116     HKEY env;
6117     MSICOMPONENT *comp;
6118     MSIRECORD *uirow;
6119     int action = 0;
6120     LONG res;
6121     UINT r;
6122
6123     component = MSI_RecordGetString( rec, 4 );
6124     comp = get_loaded_component( package, component );
6125     if (!comp)
6126         return ERROR_SUCCESS;
6127
6128     if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6129     {
6130         TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6131         comp->Action = comp->Installed;
6132         return ERROR_SUCCESS;
6133     }
6134     comp->Action = INSTALLSTATE_ABSENT;
6135
6136     name = MSI_RecordGetString( rec, 2 );
6137     value = MSI_RecordGetString( rec, 3 );
6138
6139     TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6140
6141     r = env_parse_flags( &name, &value, &flags );
6142     if (r != ERROR_SUCCESS)
6143        return r;
6144
6145     if (!(flags & ENV_ACT_REMOVE))
6146     {
6147         TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6148         return ERROR_SUCCESS;
6149     }
6150
6151     if (value && !deformat_string( package, value, &deformatted ))
6152         return ERROR_OUTOFMEMORY;
6153
6154     value = deformatted;
6155
6156     r = open_env_key( flags, &env );
6157     if (r != ERROR_SUCCESS)
6158     {
6159         r = ERROR_SUCCESS;
6160         goto done;
6161     }
6162
6163     if (flags & ENV_MOD_MACHINE)
6164         action |= 0x20000000;
6165
6166     TRACE("Removing %s\n", debugstr_w(name));
6167
6168     res = RegDeleteValueW( env, name );
6169     if (res != ERROR_SUCCESS)
6170     {
6171         WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6172         r = ERROR_SUCCESS;
6173     }
6174
6175 done:
6176     uirow = MSI_CreateRecord( 3 );
6177     MSI_RecordSetStringW( uirow, 1, name );
6178     MSI_RecordSetStringW( uirow, 2, value );
6179     MSI_RecordSetInteger( uirow, 3, action );
6180     ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6181     msiobj_release( &uirow->hdr );
6182
6183     if (env) RegCloseKey( env );
6184     msi_free( deformatted );
6185     return r;
6186 }
6187
6188 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6189 {
6190     UINT rc;
6191     MSIQUERY *view;
6192     static const WCHAR query[] =
6193         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6194          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6195
6196     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6197     if (rc != ERROR_SUCCESS)
6198         return ERROR_SUCCESS;
6199
6200     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6201     msiobj_release( &view->hdr );
6202
6203     return rc;
6204 }
6205
6206 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
6207
6208 typedef struct
6209 {
6210     struct list entry;
6211     LPWSTR sourcename;
6212     LPWSTR destname;
6213     LPWSTR source;
6214     LPWSTR dest;
6215 } FILE_LIST;
6216
6217 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
6218 {
6219     BOOL ret;
6220
6221     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
6222         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
6223     {
6224         WARN("Source or dest is directory, not moving\n");
6225         return FALSE;
6226     }
6227
6228     if (options == msidbMoveFileOptionsMove)
6229     {
6230         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
6231         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
6232         if (!ret)
6233         {
6234             WARN("MoveFile failed: %d\n", GetLastError());
6235             return FALSE;
6236         }
6237     }
6238     else
6239     {
6240         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
6241         ret = CopyFileW(source, dest, FALSE);
6242         if (!ret)
6243         {
6244             WARN("CopyFile failed: %d\n", GetLastError());
6245             return FALSE;
6246         }
6247     }
6248
6249     return TRUE;
6250 }
6251
6252 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
6253 {
6254     LPWSTR path, ptr;
6255     DWORD dirlen, pathlen;
6256
6257     ptr = strrchrW(wildcard, '\\');
6258     dirlen = ptr - wildcard + 1;
6259
6260     pathlen = dirlen + lstrlenW(filename) + 1;
6261     path = msi_alloc(pathlen * sizeof(WCHAR));
6262
6263     lstrcpynW(path, wildcard, dirlen + 1);
6264     lstrcatW(path, filename);
6265
6266     return path;
6267 }
6268
6269 static void free_file_entry(FILE_LIST *file)
6270 {
6271     msi_free(file->source);
6272     msi_free(file->dest);
6273     msi_free(file);
6274 }
6275
6276 static void free_list(FILE_LIST *list)
6277 {
6278     while (!list_empty(&list->entry))
6279     {
6280         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
6281
6282         list_remove(&file->entry);
6283         free_file_entry(file);
6284     }
6285 }
6286
6287 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
6288 {
6289     FILE_LIST *new, *file;
6290     LPWSTR ptr, filename;
6291     DWORD size;
6292
6293     new = msi_alloc_zero(sizeof(FILE_LIST));
6294     if (!new)
6295         return FALSE;
6296
6297     new->source = strdupW(source);
6298     ptr = strrchrW(dest, '\\') + 1;
6299     filename = strrchrW(new->source, '\\') + 1;
6300
6301     new->sourcename = filename;
6302
6303     if (*ptr)
6304         new->destname = ptr;
6305     else
6306         new->destname = new->sourcename;
6307
6308     size = (ptr - dest) + lstrlenW(filename) + 1;
6309     new->dest = msi_alloc(size * sizeof(WCHAR));
6310     if (!new->dest)
6311     {
6312         free_file_entry(new);
6313         return FALSE;
6314     }
6315
6316     lstrcpynW(new->dest, dest, ptr - dest + 1);
6317     lstrcatW(new->dest, filename);
6318
6319     if (list_empty(&files->entry))
6320     {
6321         list_add_head(&files->entry, &new->entry);
6322         return TRUE;
6323     }
6324
6325     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
6326     {
6327         if (lstrcmpW(source, file->source) < 0)
6328         {
6329             list_add_before(&file->entry, &new->entry);
6330             return TRUE;
6331         }
6332     }
6333
6334     list_add_after(&file->entry, &new->entry);
6335     return TRUE;
6336 }
6337
6338 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
6339 {
6340     WIN32_FIND_DATAW wfd;
6341     HANDLE hfile;
6342     LPWSTR path;
6343     BOOL res;
6344     FILE_LIST files, *file;
6345     DWORD size;
6346
6347     hfile = FindFirstFileW(source, &wfd);
6348     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
6349
6350     list_init(&files.entry);
6351
6352     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
6353     {
6354         if (is_dot_dir(wfd.cFileName)) continue;
6355
6356         path = wildcard_to_file(source, wfd.cFileName);
6357         if (!path)
6358         {
6359             res = FALSE;
6360             goto done;
6361         }
6362
6363         add_wildcard(&files, path, dest);
6364         msi_free(path);
6365     }
6366
6367     /* no files match the wildcard */
6368     if (list_empty(&files.entry))
6369         goto done;
6370
6371     /* only the first wildcard match gets renamed to dest */
6372     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
6373     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
6374     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
6375     if (!file->dest)
6376     {
6377         res = FALSE;
6378         goto done;
6379     }
6380
6381     /* file->dest may be shorter after the reallocation, so add a NULL
6382      * terminator.  This is needed for the call to strrchrW, as there will no
6383      * longer be a NULL terminator within the bounds of the allocation in this case.
6384      */
6385     file->dest[size - 1] = '\0';
6386     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
6387
6388     while (!list_empty(&files.entry))
6389     {
6390         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
6391
6392         msi_move_file(file->source, file->dest, options);
6393
6394         list_remove(&file->entry);
6395         free_file_entry(file);
6396     }
6397
6398     res = TRUE;
6399
6400 done:
6401     free_list(&files);
6402     FindClose(hfile);
6403     return res;
6404 }
6405
6406 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
6407 {
6408     MSIPACKAGE *package = param;
6409     MSICOMPONENT *comp;
6410     LPCWSTR sourcename, component;
6411     LPWSTR destname = NULL;
6412     LPWSTR sourcedir = NULL, destdir = NULL;
6413     LPWSTR source = NULL, dest = NULL;
6414     int options;
6415     DWORD size;
6416     BOOL ret, wildcards;
6417
6418     component = MSI_RecordGetString(rec, 2);
6419     comp = get_loaded_component(package, component);
6420     if (!comp)
6421         return ERROR_SUCCESS;
6422
6423     if (comp->ActionRequest != INSTALLSTATE_LOCAL && comp->ActionRequest != INSTALLSTATE_SOURCE)
6424     {
6425         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6426         comp->Action = comp->Installed;
6427         return ERROR_SUCCESS;
6428     }
6429     comp->Action = comp->ActionRequest;
6430
6431     sourcename = MSI_RecordGetString(rec, 3);
6432     options = MSI_RecordGetInteger(rec, 7);
6433
6434     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
6435     if (!sourcedir)
6436         goto done;
6437
6438     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
6439     if (!destdir)
6440         goto done;
6441
6442     if (!sourcename)
6443     {
6444         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
6445             goto done;
6446
6447         source = strdupW(sourcedir);
6448         if (!source)
6449             goto done;
6450     }
6451     else
6452     {
6453         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
6454         source = msi_alloc(size * sizeof(WCHAR));
6455         if (!source)
6456             goto done;
6457
6458         lstrcpyW(source, sourcedir);
6459         if (source[lstrlenW(source) - 1] != '\\')
6460             lstrcatW(source, szBackSlash);
6461         lstrcatW(source, sourcename);
6462     }
6463
6464     wildcards = strchrW(source, '*') || strchrW(source, '?');
6465
6466     if (MSI_RecordIsNull(rec, 4))
6467     {
6468         if (!wildcards)
6469         {
6470             destname = strdupW(sourcename);
6471             if (!destname)
6472                 goto done;
6473         }
6474     }
6475     else
6476     {
6477         destname = strdupW(MSI_RecordGetString(rec, 4));
6478         if (destname)
6479             reduce_to_longfilename(destname);
6480     }
6481
6482     size = 0;
6483     if (destname)
6484         size = lstrlenW(destname);
6485
6486     size += lstrlenW(destdir) + 2;
6487     dest = msi_alloc(size * sizeof(WCHAR));
6488     if (!dest)
6489         goto done;
6490
6491     lstrcpyW(dest, destdir);
6492     if (dest[lstrlenW(dest) - 1] != '\\')
6493         lstrcatW(dest, szBackSlash);
6494
6495     if (destname)
6496         lstrcatW(dest, destname);
6497
6498     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
6499     {
6500         ret = CreateDirectoryW(destdir, NULL);
6501         if (!ret)
6502         {
6503             WARN("CreateDirectory failed: %d\n", GetLastError());
6504             return ERROR_SUCCESS;
6505         }
6506     }
6507
6508     if (!wildcards)
6509         msi_move_file(source, dest, options);
6510     else
6511         move_files_wildcard(source, dest, options);
6512
6513 done:
6514     msi_free(sourcedir);
6515     msi_free(destdir);
6516     msi_free(destname);
6517     msi_free(source);
6518     msi_free(dest);
6519
6520     return ERROR_SUCCESS;
6521 }
6522
6523 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
6524 {
6525     UINT rc;
6526     MSIQUERY *view;
6527
6528     static const WCHAR ExecSeqQuery[] =
6529         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6530          '`','M','o','v','e','F','i','l','e','`',0};
6531
6532     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6533     if (rc != ERROR_SUCCESS)
6534         return ERROR_SUCCESS;
6535
6536     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
6537     msiobj_release(&view->hdr);
6538
6539     return rc;
6540 }
6541
6542 typedef struct tagMSIASSEMBLY
6543 {
6544     struct list entry;
6545     MSICOMPONENT *component;
6546     MSIFEATURE *feature;
6547     MSIFILE *file;
6548     LPWSTR manifest;
6549     LPWSTR application;
6550     DWORD attributes;
6551     BOOL installed;
6552 } MSIASSEMBLY;
6553
6554 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6555                                               DWORD dwReserved);
6556 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6557                                           LPVOID pvReserved, HMODULE *phModDll);
6558
6559 static BOOL init_functionpointers(void)
6560 {
6561     HRESULT hr;
6562     HMODULE hfusion;
6563     HMODULE hmscoree;
6564
6565     static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6566
6567     hmscoree = LoadLibraryA("mscoree.dll");
6568     if (!hmscoree)
6569     {
6570         WARN("mscoree.dll not available\n");
6571         return FALSE;
6572     }
6573
6574     pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6575     if (!pLoadLibraryShim)
6576     {
6577         WARN("LoadLibraryShim not available\n");
6578         FreeLibrary(hmscoree);
6579         return FALSE;
6580     }
6581
6582     hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6583     if (FAILED(hr))
6584     {
6585         WARN("fusion.dll not available\n");
6586         FreeLibrary(hmscoree);
6587         return FALSE;
6588     }
6589
6590     pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6591
6592     FreeLibrary(hmscoree);
6593     return TRUE;
6594 }
6595
6596 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6597                              LPWSTR path)
6598 {
6599     IAssemblyCache *cache;
6600     HRESULT hr;
6601     UINT r = ERROR_FUNCTION_FAILED;
6602
6603     TRACE("installing assembly: %s\n", debugstr_w(path));
6604
6605     if (assembly->feature)
6606         msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6607
6608     if (assembly->manifest)
6609         FIXME("Manifest unhandled\n");
6610
6611     if (assembly->application)
6612     {
6613         FIXME("Assembly should be privately installed\n");
6614         return ERROR_SUCCESS;
6615     }
6616
6617     if (assembly->attributes == msidbAssemblyAttributesWin32)
6618     {
6619         FIXME("Win32 assemblies not handled\n");
6620         return ERROR_SUCCESS;
6621     }
6622
6623     hr = pCreateAssemblyCache(&cache, 0);
6624     if (FAILED(hr))
6625         goto done;
6626
6627     hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6628     if (FAILED(hr))
6629         ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6630
6631     r = ERROR_SUCCESS;
6632
6633 done:
6634     IAssemblyCache_Release(cache);
6635     return r;
6636 }
6637
6638 typedef struct tagASSEMBLY_LIST
6639 {
6640     MSIPACKAGE *package;
6641     IAssemblyCache *cache;
6642     struct list *assemblies;
6643 } ASSEMBLY_LIST;
6644
6645 typedef struct tagASSEMBLY_NAME
6646 {
6647     LPWSTR name;
6648     LPWSTR version;
6649     LPWSTR culture;
6650     LPWSTR pubkeytoken;
6651 } ASSEMBLY_NAME;
6652
6653 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6654 {
6655     ASSEMBLY_NAME *asmname = param;
6656     LPCWSTR name = MSI_RecordGetString(rec, 2);
6657     LPWSTR val = msi_dup_record_field(rec, 3);
6658
6659     static const WCHAR Name[] = {'N','a','m','e',0};
6660     static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6661     static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6662     static const WCHAR PublicKeyToken[] = {
6663         'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6664
6665     if (!strcmpiW(name, Name))
6666         asmname->name = val;
6667     else if (!strcmpiW(name, Version))
6668         asmname->version = val;
6669     else if (!strcmpiW(name, Culture))
6670         asmname->culture = val;
6671     else if (!strcmpiW(name, PublicKeyToken))
6672         asmname->pubkeytoken = val;
6673     else
6674         msi_free(val);
6675
6676     return ERROR_SUCCESS;
6677 }
6678
6679 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6680 {
6681     if (!*str)
6682     {
6683         *size = lstrlenW(append) + 1;
6684         *str = msi_alloc((*size) * sizeof(WCHAR));
6685         lstrcpyW(*str, append);
6686         return;
6687     }
6688
6689     (*size) += lstrlenW(append);
6690     *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6691     lstrcatW(*str, append);
6692 }
6693
6694 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
6695                                      MSICOMPONENT *comp)
6696 {
6697     ASSEMBLY_INFO asminfo;
6698     ASSEMBLY_NAME name;
6699     MSIQUERY *view;
6700     LPWSTR disp;
6701     DWORD size;
6702     BOOL found;
6703     UINT r;
6704
6705     static const WCHAR separator[] = {',',' ',0};
6706     static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6707     static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6708     static const WCHAR PublicKeyToken[] = {
6709         'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6710     static const WCHAR query[] = {
6711         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6712         '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6713         'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6714         '=','\'','%','s','\'',0};
6715
6716     disp = NULL;
6717     found = FALSE;
6718     ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
6719     ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
6720
6721     r = MSI_OpenQuery(db, &view, query, comp->Component);
6722     if (r != ERROR_SUCCESS)
6723         return ERROR_SUCCESS;
6724
6725     MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
6726     msiobj_release(&view->hdr);
6727
6728     if (!name.name)
6729     {
6730         ERR("No assembly name specified!\n");
6731         goto done;
6732     }
6733
6734     append_str(&disp, &size, name.name);
6735
6736     if (name.version)
6737     {
6738         append_str(&disp, &size, separator);
6739         append_str(&disp, &size, Version);
6740         append_str(&disp, &size, name.version);
6741     }
6742
6743     if (name.culture)
6744     {
6745         append_str(&disp, &size, separator);
6746         append_str(&disp, &size, Culture);
6747         append_str(&disp, &size, name.culture);
6748     }
6749
6750     if (name.pubkeytoken)
6751     {
6752         append_str(&disp, &size, separator);
6753         append_str(&disp, &size, PublicKeyToken);
6754         append_str(&disp, &size, name.pubkeytoken);
6755     }
6756
6757     asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6758     IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
6759                                      disp, &asminfo);
6760     found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6761
6762 done:
6763     msi_free(disp);
6764     msi_free(name.name);
6765     msi_free(name.version);
6766     msi_free(name.culture);
6767     msi_free(name.pubkeytoken);
6768
6769     return found;
6770 }
6771
6772 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6773 {
6774     ASSEMBLY_LIST *list = param;
6775     MSIASSEMBLY *assembly;
6776     LPCWSTR component;
6777
6778     assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6779     if (!assembly)
6780         return ERROR_OUTOFMEMORY;
6781
6782     component = MSI_RecordGetString(rec, 1);
6783     assembly->component = get_loaded_component(list->package, component);
6784     if (!assembly->component)
6785         return ERROR_SUCCESS;
6786
6787     if (assembly->component->ActionRequest != INSTALLSTATE_LOCAL &&
6788         assembly->component->ActionRequest != INSTALLSTATE_SOURCE)
6789     {
6790         TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6791         assembly->component->Action = assembly->component->Installed;
6792         return ERROR_SUCCESS;
6793     }
6794     assembly->component->Action = assembly->component->ActionRequest;
6795
6796     assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6797     assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6798
6799     if (!assembly->file)
6800     {
6801         ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6802         return ERROR_FUNCTION_FAILED;
6803     }
6804
6805     assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6806     assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6807     assembly->attributes = MSI_RecordGetInteger(rec, 5);
6808
6809     if (assembly->application)
6810     {
6811         WCHAR version[24];
6812         DWORD size = sizeof(version)/sizeof(WCHAR);
6813
6814         /* FIXME: we should probably check the manifest file here */
6815
6816         if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6817             (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6818         {
6819             assembly->installed = TRUE;
6820         }
6821     }
6822     else
6823         assembly->installed = check_assembly_installed(list->package->db,
6824                                                        list->cache,
6825                                                        assembly->component);
6826
6827     list_add_head(list->assemblies, &assembly->entry);
6828     return ERROR_SUCCESS;
6829 }
6830
6831 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6832 {
6833     IAssemblyCache *cache = NULL;
6834     ASSEMBLY_LIST list;
6835     MSIQUERY *view;
6836     HRESULT hr;
6837     UINT r;
6838
6839     static const WCHAR query[] =
6840         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6841          '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6842
6843     r = MSI_DatabaseOpenViewW(package->db, query, &view);
6844     if (r != ERROR_SUCCESS)
6845         return ERROR_SUCCESS;
6846
6847     hr = pCreateAssemblyCache(&cache, 0);
6848     if (FAILED(hr))
6849         return ERROR_FUNCTION_FAILED;
6850
6851     list.package = package;
6852     list.cache = cache;
6853     list.assemblies = assemblies;
6854
6855     r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6856     msiobj_release(&view->hdr);
6857
6858     IAssemblyCache_Release(cache);
6859
6860     return r;
6861 }
6862
6863 static void free_assemblies(struct list *assemblies)
6864 {
6865     struct list *item, *cursor;
6866
6867     LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6868     {
6869         MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6870
6871         list_remove(&assembly->entry);
6872         msi_free(assembly->application);
6873         msi_free(assembly->manifest);
6874         msi_free(assembly);
6875     }
6876 }
6877
6878 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6879 {
6880     MSIASSEMBLY *assembly;
6881
6882     LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6883     {
6884         if (!lstrcmpW(assembly->file->File, file))
6885         {
6886             *out = assembly;
6887             return TRUE;
6888         }
6889     }
6890
6891     return FALSE;
6892 }
6893
6894 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6895                                LPWSTR *path, DWORD *attrs, PVOID user)
6896 {
6897     MSIASSEMBLY *assembly;
6898     WCHAR temppath[MAX_PATH];
6899     struct list *assemblies = user;
6900     UINT r;
6901
6902     if (!find_assembly(assemblies, file, &assembly))
6903         return FALSE;
6904
6905     GetTempPathW(MAX_PATH, temppath);
6906     PathAddBackslashW(temppath);
6907     lstrcatW(temppath, assembly->file->FileName);
6908
6909     if (action == MSICABEXTRACT_BEGINEXTRACT)
6910     {
6911         if (assembly->installed)
6912             return FALSE;
6913
6914         *path = strdupW(temppath);
6915         *attrs = assembly->file->Attributes;
6916     }
6917     else if (action == MSICABEXTRACT_FILEEXTRACTED)
6918     {
6919         assembly->installed = TRUE;
6920
6921         r = install_assembly(package, assembly, temppath);
6922         if (r != ERROR_SUCCESS)
6923             ERR("Failed to install assembly\n");
6924     }
6925
6926     return TRUE;
6927 }
6928
6929 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6930 {
6931     UINT r;
6932     struct list assemblies = LIST_INIT(assemblies);
6933     MSIASSEMBLY *assembly;
6934     MSIMEDIAINFO *mi;
6935
6936     if (!init_functionpointers() || !pCreateAssemblyCache)
6937         return ERROR_FUNCTION_FAILED;
6938
6939     r = load_assemblies(package, &assemblies);
6940     if (r != ERROR_SUCCESS)
6941         goto done;
6942
6943     if (list_empty(&assemblies))
6944         goto done;
6945
6946     mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6947     if (!mi)
6948     {
6949         r = ERROR_OUTOFMEMORY;
6950         goto done;
6951     }
6952
6953     LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6954     {
6955         if (assembly->installed && !mi->is_continuous)
6956             continue;
6957
6958         if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6959             (assembly->file->IsCompressed && !mi->is_extracted))
6960         {
6961             MSICABDATA data;
6962
6963             r = ready_media(package, assembly->file, mi);
6964             if (r != ERROR_SUCCESS)
6965             {
6966                 ERR("Failed to ready media\n");
6967                 break;
6968             }
6969
6970             data.mi = mi;
6971             data.package = package;
6972             data.cb = installassembly_cb;
6973             data.user = &assemblies;
6974
6975             if (assembly->file->IsCompressed &&
6976                 !msi_cabextract(package, mi, &data))
6977             {
6978                 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6979                 r = ERROR_FUNCTION_FAILED;
6980                 break;
6981             }
6982         }
6983
6984         if (!assembly->file->IsCompressed)
6985         {
6986             LPWSTR source = resolve_file_source(package, assembly->file);
6987
6988             r = install_assembly(package, assembly, source);
6989             if (r != ERROR_SUCCESS)
6990                 ERR("Failed to install assembly\n");
6991
6992             msi_free(source);
6993         }
6994
6995         /* FIXME: write Installer assembly reg values */
6996     }
6997
6998 done:
6999     free_assemblies(&assemblies);
7000     return r;
7001 }
7002
7003 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7004 {
7005     LPWSTR key, template, id;
7006     UINT r = ERROR_SUCCESS;
7007
7008     id = msi_dup_property( package, szProductID );
7009     if (id)
7010     {
7011         msi_free( id );
7012         return ERROR_SUCCESS;
7013     }
7014     template = msi_dup_property( package, szPIDTemplate );
7015     key = msi_dup_property( package, szPIDKEY );
7016
7017     if (key && template)
7018     {
7019         FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7020         r = MSI_SetPropertyW( package, szProductID, key );
7021     }
7022     msi_free( template );
7023     msi_free( key );
7024     return r;
7025 }
7026
7027 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7028 {
7029     TRACE("\n");
7030     package->need_reboot = 1;
7031     return ERROR_SUCCESS;
7032 }
7033
7034 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7035 {
7036     static const WCHAR szAvailableFreeReg[] =
7037         {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7038
7039     TRACE("%p %d kilobytes\n", package, msi_get_property_int( package, szAvailableFreeReg, 0 ));
7040     return ERROR_SUCCESS;
7041 }
7042
7043 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7044 {
7045     FIXME("%p\n", package);
7046     return ERROR_SUCCESS;
7047 }
7048
7049 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7050 {
7051     FIXME("%p\n", package);
7052     return ERROR_SUCCESS;
7053 }
7054
7055 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7056                                            LPCSTR action, LPCWSTR table )
7057 {
7058     static const WCHAR query[] = {
7059         'S','E','L','E','C','T',' ','*',' ',
7060         'F','R','O','M',' ','`','%','s','`',0 };
7061     MSIQUERY *view = NULL;
7062     DWORD count = 0;
7063     UINT r;
7064     
7065     r = MSI_OpenQuery( package->db, &view, query, table );
7066     if (r == ERROR_SUCCESS)
7067     {
7068         r = MSI_IterateRecords(view, &count, NULL, package);
7069         msiobj_release(&view->hdr);
7070     }
7071
7072     if (count)
7073         FIXME("%s -> %u ignored %s table values\n",
7074               action, count, debugstr_w(table));
7075
7076     return ERROR_SUCCESS;
7077 }
7078
7079 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7080 {
7081     static const WCHAR table[] = { 'P','a','t','c','h',0 };
7082     return msi_unimplemented_action_stub( package, "PatchFiles", table );
7083 }
7084
7085 static UINT ACTION_BindImage( MSIPACKAGE *package )
7086 {
7087     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7088     return msi_unimplemented_action_stub( package, "BindImage", table );
7089 }
7090
7091 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7092 {
7093     static const WCHAR table[] = {
7094         'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7095     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7096 }
7097
7098 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7099 {
7100     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7101     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
7102 }
7103
7104 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
7105 {
7106     static const WCHAR table[] = {
7107         'M','s','i','A','s','s','e','m','b','l','y',0 };
7108     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
7109 }
7110
7111 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7112 {
7113     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7114     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7115 }
7116
7117 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7118 {
7119     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7120     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7121 }
7122
7123 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7124 {
7125     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7126     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7127 }
7128
7129 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7130 {
7131     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7132     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7133 }
7134
7135 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7136 {
7137     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
7138     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
7139 }
7140
7141 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7142 {
7143     static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
7144     return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
7145 }
7146
7147 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
7148 {
7149     static const WCHAR table[] = { 'A','p','p','I','d',0 };
7150     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
7151 }
7152
7153 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
7154 {
7155     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
7156     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
7157 }
7158
7159 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
7160 {
7161     static const WCHAR table[] = { 'M','I','M','E',0 };
7162     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
7163 }
7164
7165 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
7166 {
7167     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
7168     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
7169 }
7170
7171 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7172
7173 static const struct
7174 {
7175     const WCHAR *action;
7176     UINT (*handler)(MSIPACKAGE *);
7177 }
7178 StandardActions[] =
7179 {
7180     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7181     { szAppSearch, ACTION_AppSearch },
7182     { szBindImage, ACTION_BindImage },
7183     { szCCPSearch, ACTION_CCPSearch },
7184     { szCostFinalize, ACTION_CostFinalize },
7185     { szCostInitialize, ACTION_CostInitialize },
7186     { szCreateFolders, ACTION_CreateFolders },
7187     { szCreateShortcuts, ACTION_CreateShortcuts },
7188     { szDeleteServices, ACTION_DeleteServices },
7189     { szDisableRollback, ACTION_DisableRollback },
7190     { szDuplicateFiles, ACTION_DuplicateFiles },
7191     { szExecuteAction, ACTION_ExecuteAction },
7192     { szFileCost, ACTION_FileCost },
7193     { szFindRelatedProducts, ACTION_FindRelatedProducts },
7194     { szForceReboot, ACTION_ForceReboot },
7195     { szInstallAdminPackage, ACTION_InstallAdminPackage },
7196     { szInstallExecute, ACTION_InstallExecute },
7197     { szInstallExecuteAgain, ACTION_InstallExecute },
7198     { szInstallFiles, ACTION_InstallFiles},
7199     { szInstallFinalize, ACTION_InstallFinalize },
7200     { szInstallInitialize, ACTION_InstallInitialize },
7201     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7202     { szInstallValidate, ACTION_InstallValidate },
7203     { szIsolateComponents, ACTION_IsolateComponents },
7204     { szLaunchConditions, ACTION_LaunchConditions },
7205     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7206     { szMoveFiles, ACTION_MoveFiles },
7207     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7208     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7209     { szInstallODBC, ACTION_InstallODBC },
7210     { szInstallServices, ACTION_InstallServices },
7211     { szPatchFiles, ACTION_PatchFiles },
7212     { szProcessComponents, ACTION_ProcessComponents },
7213     { szPublishComponents, ACTION_PublishComponents },
7214     { szPublishFeatures, ACTION_PublishFeatures },
7215     { szPublishProduct, ACTION_PublishProduct },
7216     { szRegisterClassInfo, ACTION_RegisterClassInfo },
7217     { szRegisterComPlus, ACTION_RegisterComPlus},
7218     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7219     { szRegisterFonts, ACTION_RegisterFonts },
7220     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7221     { szRegisterProduct, ACTION_RegisterProduct },
7222     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7223     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7224     { szRegisterUser, ACTION_RegisterUser },
7225     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7226     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7227     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7228     { szRemoveFiles, ACTION_RemoveFiles },
7229     { szRemoveFolders, ACTION_RemoveFolders },
7230     { szRemoveIniValues, ACTION_RemoveIniValues },
7231     { szRemoveODBC, ACTION_RemoveODBC },
7232     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7233     { szRemoveShortcuts, ACTION_RemoveShortcuts },
7234     { szResolveSource, ACTION_ResolveSource },
7235     { szRMCCPSearch, ACTION_RMCCPSearch },
7236     { szScheduleReboot, ACTION_ScheduleReboot },
7237     { szSelfRegModules, ACTION_SelfRegModules },
7238     { szSelfUnregModules, ACTION_SelfUnregModules },
7239     { szSetODBCFolders, ACTION_SetODBCFolders },
7240     { szStartServices, ACTION_StartServices },
7241     { szStopServices, ACTION_StopServices },
7242     { szUnpublishComponents, ACTION_UnpublishComponents },
7243     { szUnpublishFeatures, ACTION_UnpublishFeatures },
7244     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7245     { szUnregisterComPlus, ACTION_UnregisterComPlus },
7246     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7247     { szUnregisterFonts, ACTION_UnregisterFonts },
7248     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7249     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7250     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7251     { szValidateProductID, ACTION_ValidateProductID },
7252     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7253     { szWriteIniValues, ACTION_WriteIniValues },
7254     { szWriteRegistryValues, ACTION_WriteRegistryValues },
7255     { NULL, NULL },
7256 };
7257
7258 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
7259                                         UINT* rc, BOOL force )
7260 {
7261     BOOL ret = FALSE;
7262     BOOL run = force;
7263     int i;
7264
7265     if (!run && !package->script->CurrentlyScripting)
7266         run = TRUE;
7267
7268     if (!run)
7269     {
7270         if (strcmpW(action,szInstallFinalize) == 0 ||
7271             strcmpW(action,szInstallExecute) == 0 ||
7272             strcmpW(action,szInstallExecuteAgain) == 0)
7273                 run = TRUE;
7274     }
7275
7276     i = 0;
7277     while (StandardActions[i].action != NULL)
7278     {
7279         if (strcmpW(StandardActions[i].action, action)==0)
7280         {
7281             if (!run)
7282             {
7283                 ui_actioninfo(package, action, TRUE, 0);
7284                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
7285                 ui_actioninfo(package, action, FALSE, *rc);
7286             }
7287             else
7288             {
7289                 ui_actionstart(package, action);
7290                 if (StandardActions[i].handler)
7291                 {
7292                     *rc = StandardActions[i].handler(package);
7293                 }
7294                 else
7295                 {
7296                     FIXME("unhandled standard action %s\n",debugstr_w(action));
7297                     *rc = ERROR_SUCCESS;
7298                 }
7299             }
7300             ret = TRUE;
7301             break;
7302         }
7303         i++;
7304     }
7305     return ret;
7306 }
7307
7308 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
7309 {
7310     UINT rc = ERROR_SUCCESS;
7311     BOOL handled;
7312
7313     TRACE("Performing action (%s)\n", debugstr_w(action));
7314
7315     handled = ACTION_HandleStandardAction(package, action, &rc, force);
7316
7317     if (!handled)
7318         handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
7319
7320     if (!handled)
7321     {
7322         WARN("unhandled msi action %s\n", debugstr_w(action));
7323         rc = ERROR_FUNCTION_NOT_CALLED;
7324     }
7325
7326     return rc;
7327 }
7328
7329 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7330 {
7331     UINT rc = ERROR_SUCCESS;
7332     BOOL handled = FALSE;
7333
7334     TRACE("Performing action (%s)\n", debugstr_w(action));
7335
7336     handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
7337
7338     if (!handled)
7339         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7340
7341     if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7342         handled = TRUE;
7343
7344     if (!handled)
7345     {
7346         WARN("unhandled msi action %s\n", debugstr_w(action));
7347         rc = ERROR_FUNCTION_NOT_CALLED;
7348     }
7349
7350     return rc;
7351 }
7352
7353 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7354 {
7355     UINT rc = ERROR_SUCCESS;
7356     MSIRECORD *row;
7357
7358     static const WCHAR ExecSeqQuery[] =
7359         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7360          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7361          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7362          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7363     static const WCHAR UISeqQuery[] =
7364         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7365      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7366      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7367          ' ', '=',' ','%','i',0};
7368
7369     if (needs_ui_sequence(package))
7370         row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7371     else
7372         row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7373
7374     if (row)
7375     {
7376         LPCWSTR action, cond;
7377
7378         TRACE("Running the actions\n");
7379
7380         /* check conditions */
7381         cond = MSI_RecordGetString(row, 2);
7382
7383         /* this is a hack to skip errors in the condition code */
7384         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7385         {
7386             msiobj_release(&row->hdr);
7387             return ERROR_SUCCESS;
7388         }
7389
7390         action = MSI_RecordGetString(row, 1);
7391         if (!action)
7392         {
7393             ERR("failed to fetch action\n");
7394             msiobj_release(&row->hdr);
7395             return ERROR_FUNCTION_FAILED;
7396         }
7397
7398         if (needs_ui_sequence(package))
7399             rc = ACTION_PerformUIAction(package, action, -1);
7400         else
7401             rc = ACTION_PerformAction(package, action, -1, FALSE);
7402
7403         msiobj_release(&row->hdr);
7404     }
7405
7406     return rc;
7407 }
7408
7409 /****************************************************
7410  * TOP level entry points
7411  *****************************************************/
7412
7413 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7414                          LPCWSTR szCommandLine )
7415 {
7416     UINT rc;
7417     BOOL ui_exists;
7418
7419     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7420     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7421
7422     MSI_SetPropertyW(package, szAction, szInstall);
7423
7424     package->script->InWhatSequence = SEQUENCE_INSTALL;
7425
7426     if (szPackagePath)
7427     {
7428         LPWSTR p, dir;
7429         LPCWSTR file;
7430
7431         dir = strdupW(szPackagePath);
7432         p = strrchrW(dir, '\\');
7433         if (p)
7434         {
7435             *(++p) = 0;
7436             file = szPackagePath + (p - dir);
7437         }
7438         else
7439         {
7440             msi_free(dir);
7441             dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7442             GetCurrentDirectoryW(MAX_PATH, dir);
7443             lstrcatW(dir, szBackSlash);
7444             file = szPackagePath;
7445         }
7446
7447         msi_free( package->PackagePath );
7448         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7449         if (!package->PackagePath)
7450         {
7451             msi_free(dir);
7452             return ERROR_OUTOFMEMORY;
7453         }
7454
7455         lstrcpyW(package->PackagePath, dir);
7456         lstrcatW(package->PackagePath, file);
7457         msi_free(dir);
7458
7459         msi_set_sourcedir_props(package, FALSE);
7460     }
7461
7462     msi_parse_command_line( package, szCommandLine, FALSE );
7463
7464     msi_apply_transforms( package );
7465     msi_apply_patches( package );
7466
7467     if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
7468     {
7469         TRACE("setting reinstall property\n");
7470         MSI_SetPropertyW( package, szReinstall, szAll );
7471     }
7472
7473     /* properties may have been added by a transform */
7474     msi_clone_properties( package );
7475     msi_set_context( package );
7476
7477     if (needs_ui_sequence( package))
7478     {
7479         package->script->InWhatSequence |= SEQUENCE_UI;
7480         rc = ACTION_ProcessUISequence(package);
7481         ui_exists = ui_sequence_exists(package);
7482         if (rc == ERROR_SUCCESS || !ui_exists)
7483         {
7484             package->script->InWhatSequence |= SEQUENCE_EXEC;
7485             rc = ACTION_ProcessExecSequence(package, ui_exists);
7486         }
7487     }
7488     else
7489         rc = ACTION_ProcessExecSequence(package, FALSE);
7490
7491     package->script->CurrentlyScripting = FALSE;
7492
7493     /* process the ending type action */
7494     if (rc == ERROR_SUCCESS)
7495         ACTION_PerformActionSequence(package, -1);
7496     else if (rc == ERROR_INSTALL_USEREXIT)
7497         ACTION_PerformActionSequence(package, -2);
7498     else if (rc == ERROR_INSTALL_SUSPEND)
7499         ACTION_PerformActionSequence(package, -4);
7500     else  /* failed */
7501         ACTION_PerformActionSequence(package, -3);
7502
7503     /* finish up running custom actions */
7504     ACTION_FinishCustomActions(package);
7505
7506     if (rc == ERROR_SUCCESS && package->need_reboot)
7507         return ERROR_SUCCESS_REBOOT_REQUIRED;
7508
7509     return rc;
7510 }