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