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