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