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