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