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