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