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