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