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