msi: Only unpublish the features if the entire product is being uninstalled.
[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 /*
22  * Pages I need
23  *
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
25
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
27  */
28
29 #include <stdarg.h>
30
31 #define COBJMACROS
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "winsvc.h"
38 #include "odbcinst.h"
39 #include "wine/debug.h"
40 #include "msidefs.h"
41 #include "msipriv.h"
42 #include "winuser.h"
43 #include "shlobj.h"
44 #include "wine/unicode.h"
45 #include "winver.h"
46
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
49
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
51
52 /*
53  * Prototypes
54  */
55 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
56 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
57 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
58
59 /*
60  * consts and values used
61  */
62 static const WCHAR c_colon[] = {'C',':','\\',0};
63
64 static const WCHAR szCreateFolders[] =
65     {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize[] =
67     {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles[] =
69     {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles[] =
71     {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues[] =
73     {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74             'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize[] =
76     {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost[] = 
78     {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize[] = 
80     {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate[] = 
82     {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions[] = 
84     {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents[] = 
86     {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries[] = 
88     {'R','e','g','i','s','t','e','r','T','y','p','e',
89             'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo[] = 
91     {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo[] = 
93     {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts[] = 
95     {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct[] = 
97     {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues[] = 
99     {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules[] = 
101     {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures[] = 
103     {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct[] = 
105     {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute[] = 
107     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain[] = 
109     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110             'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize[] = 
112     {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot[] = 
114     {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource[] =
116     {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch[] = 
118     {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace[] = 
120     {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121             'S','p','a','c','e',0};
122 static const WCHAR szBindImage[] = 
123     {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch[] = 
125     {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices[] = 
127     {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback[] = 
129     {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction[] = 
131     {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts[] = 
133     {'F','i','n','d','R','e','l','a','t','e','d',
134             'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage[] = 
136     {'I','n','s','t','a','l','l','A','d','m','i','n',
137             'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile[] = 
139     {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
140             'F','i','l','e',0};
141 static const WCHAR szIsolateComponents[] = 
142     {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates[] = 
144     {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145             'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles[] = 
147     {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies[] = 
149     {'M','s','i','P','u','b','l','i','s','h',
150             'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies[] = 
152     {'M','s','i','U','n','p','u','b','l','i','s','h',
153             'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC[] = 
155     {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices[] = 
157     {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles[] = 
159     {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents[] = 
161     {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus[] =
163     {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo[] =
165     {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
166             'I','n','f','o',0};
167 static const WCHAR szRegisterFonts[] =
168     {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo[] =
170     {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser[] =
172     {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles[] =
174     {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175             'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings[] =
177     {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178             'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts[] =
180     {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181             'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles[] =
183     {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders[] =
185     {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues[] =
187     {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC[] =
189     {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues[] =
191     {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192             'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts[] =
194     {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch[] =
196     {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot[] =
198     {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules[] =
200     {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders[] =
202     {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices[] =
204     {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices[] =
206     {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents[] =
208     {'U','n','p','u','b','l','i','s','h',
209             'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures[] =
211     {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo[] =
213     {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
214             'I','n','f','o',0};
215 static const WCHAR szUnregisterComPlus[] =
216     {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo[] =
218     {'U','n','r','e','g','i','s','t','e','r',
219             'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts[] =
221     {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo[] =
223     {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo[] =
225     {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
226             'I','n','f','o',0};
227 static const WCHAR szUnregisterTypeLibraries[] =
228     {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229             'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID[] =
231     {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings[] =
233     {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234             'S','t','r','i','n','g','s',0};
235
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
238
239 struct _actions {
240     LPCWSTR action;
241     STANDARDACTIONHANDLER handler;
242 };
243
244 static const struct _actions StandardActions[];
245
246
247 /********************************************************
248  * helper functions
249  ********************************************************/
250
251 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
252 {
253     static const WCHAR Query_t[] = 
254         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255          '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256          'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=', 
257          ' ','\'','%','s','\'',0};
258     MSIRECORD * row;
259
260     row = MSI_QueryGetRecord( package->db, Query_t, action );
261     if (!row)
262         return;
263     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
264     msiobj_release(&row->hdr);
265 }
266
267 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, 
268                           UINT rc)
269 {
270     MSIRECORD * row;
271     static const WCHAR template_s[]=
272         {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273          '%','s', '.',0};
274     static const WCHAR template_e[]=
275         {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276          '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
277          '%','i','.',0};
278     static const WCHAR format[] = 
279         {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
280     WCHAR message[1024];
281     WCHAR timet[0x100];
282
283     GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
284     if (start)
285         sprintfW(message,template_s,timet,action);
286     else
287         sprintfW(message,template_e,timet,action,rc);
288     
289     row = MSI_CreateRecord(1);
290     MSI_RecordSetStringW(row,1,message);
291  
292     MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
293     msiobj_release(&row->hdr);
294 }
295
296 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
297 {
298     LPCWSTR ptr,ptr2;
299     BOOL quote;
300     DWORD len;
301     LPWSTR prop = NULL, val = NULL;
302
303     if (!szCommandLine)
304         return ERROR_SUCCESS;
305
306     ptr = szCommandLine;
307        
308     while (*ptr)
309     {
310         if (*ptr==' ')
311         {
312             ptr++;
313             continue;
314         }
315
316         TRACE("Looking at %s\n",debugstr_w(ptr));
317
318         ptr2 = strchrW(ptr,'=');
319         if (!ptr2)
320         {
321             ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
322             break;
323         }
324  
325         quote = FALSE;
326
327         len = ptr2-ptr;
328         prop = msi_alloc((len+1)*sizeof(WCHAR));
329         memcpy(prop,ptr,len*sizeof(WCHAR));
330         prop[len]=0;
331         ptr2++;
332        
333         len = 0; 
334         ptr = ptr2; 
335         while (*ptr && (quote || (!quote && *ptr!=' ')))
336         {
337             if (*ptr == '"')
338                 quote = !quote;
339             ptr++;
340             len++;
341         }
342        
343         if (*ptr2=='"')
344         {
345             ptr2++;
346             len -= 2;
347         }
348         val = msi_alloc((len+1)*sizeof(WCHAR));
349         memcpy(val,ptr2,len*sizeof(WCHAR));
350         val[len] = 0;
351
352         if (lstrlenW(prop) > 0)
353         {
354             TRACE("Found commandline property (%s) = (%s)\n", 
355                    debugstr_w(prop), debugstr_w(val));
356             MSI_SetPropertyW(package,prop,val);
357         }
358         msi_free(val);
359         msi_free(prop);
360     }
361
362     return ERROR_SUCCESS;
363 }
364
365
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
367 {
368     LPCWSTR pc;
369     LPWSTR p, *ret = NULL;
370     UINT count = 0;
371
372     if (!str)
373         return ret;
374
375     /* count the number of substrings */
376     for ( pc = str, count = 0; pc; count++ )
377     {
378         pc = strchrW( pc, sep );
379         if (pc)
380             pc++;
381     }
382
383     /* allocate space for an array of substring pointers and the substrings */
384     ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385                      (lstrlenW(str)+1) * sizeof(WCHAR) );
386     if (!ret)
387         return ret;
388
389     /* copy the string and set the pointers */
390     p = (LPWSTR) &ret[count+1];
391     lstrcpyW( p, str );
392     for( count = 0; (ret[count] = p); count++ )
393     {
394         p = strchrW( p, sep );
395         if (p)
396             *p++ = 0;
397     }
398
399     return ret;
400 }
401
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
403 {
404     WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405     LPWSTR prod_code, patch_product;
406     UINT ret;
407
408     prod_code = msi_dup_property( package, szProductCode );
409     patch_product = msi_get_suminfo_product( patch );
410
411     TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
412
413     if ( strstrW( patch_product, prod_code ) )
414         ret = ERROR_SUCCESS;
415     else
416         ret = ERROR_FUNCTION_FAILED;
417
418     msi_free( patch_product );
419     msi_free( prod_code );
420
421     return ret;
422 }
423
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425                                  MSIDATABASE *patch_db, LPCWSTR name )
426 {
427     UINT ret = ERROR_FUNCTION_FAILED;
428     IStorage *stg = NULL;
429     HRESULT r;
430
431     TRACE("%p %s\n", package, debugstr_w(name) );
432
433     if (*name++ != ':')
434     {
435         ERR("expected a colon in %s\n", debugstr_w(name));
436         return ERROR_FUNCTION_FAILED;
437     }
438
439     r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
440     if (SUCCEEDED(r))
441     {
442         ret = msi_check_transform_applicable( package, stg );
443         if (ret == ERROR_SUCCESS)
444             msi_table_apply_transform( package->db, stg );
445         else
446             TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447         IStorage_Release( stg );
448     }
449     else
450         ERR("failed to open substorage %s\n", debugstr_w(name));
451
452     return ERROR_SUCCESS;
453 }
454
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
456 {
457     static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458     LPWSTR guid_list, *guids, product_code;
459     UINT i, ret = ERROR_FUNCTION_FAILED;
460
461     product_code = msi_dup_property( package, szProdCode );
462     if (!product_code)
463     {
464         /* FIXME: the property ProductCode should be written into the DB somewhere */
465         ERR("no product code to check\n");
466         return ERROR_SUCCESS;
467     }
468
469     guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470     guids = msi_split_string( guid_list, ';' );
471     for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
472     {
473         if (!lstrcmpW( guids[i], product_code ))
474             ret = ERROR_SUCCESS;
475     }
476     msi_free( guids );
477     msi_free( guid_list );
478     msi_free( product_code );
479
480     return ret;
481 }
482
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
484 {
485     MSISUMMARYINFO *si;
486     LPWSTR str, *substorage;
487     UINT i, r = ERROR_SUCCESS;
488
489     si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490     if (!si)
491         return ERROR_FUNCTION_FAILED;
492
493     msi_check_patch_applicable( package, si );
494
495     /* enumerate the substorage */
496     str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497     substorage = msi_split_string( str, ';' );
498     for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499         r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500     msi_free( substorage );
501     msi_free( str );
502
503     /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
504
505     msiobj_release( &si->hdr );
506
507     return r;
508 }
509
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
511 {
512     MSIDATABASE *patch_db = NULL;
513     UINT r;
514
515     TRACE("%p %s\n", package, debugstr_w( file ) );
516
517     /* FIXME:
518      *  We probably want to make sure we only open a patch collection here.
519      *  Patch collections (.msp) and databases (.msi) have different GUIDs
520      *  but currently MSI_OpenDatabaseW will accept both.
521      */
522     r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523     if ( r != ERROR_SUCCESS )
524     {
525         ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526         return r;
527     }
528
529     msi_parse_patch_summary( package, patch_db );
530
531     /*
532      * There might be a CAB file in the patch package,
533      * so append it to the list of storage to search for streams.
534      */
535     append_storage_to_db( package->db, patch_db->storage );
536
537     msiobj_release( &patch_db->hdr );
538
539     return ERROR_SUCCESS;
540 }
541
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
544 {
545     static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546     LPWSTR patch_list, *patches;
547     UINT i, r = ERROR_SUCCESS;
548
549     patch_list = msi_dup_property( package, szPatch );
550
551     TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
552
553     patches = msi_split_string( patch_list, ';' );
554     for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555         r = msi_apply_patch_package( package, patches[i] );
556
557     msi_free( patches );
558     msi_free( patch_list );
559
560     return r;
561 }
562
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
564 {
565     static const WCHAR szTransforms[] = {
566         'T','R','A','N','S','F','O','R','M','S',0 };
567     LPWSTR xform_list, *xforms;
568     UINT i, r = ERROR_SUCCESS;
569
570     xform_list = msi_dup_property( package, szTransforms );
571     xforms = msi_split_string( xform_list, ';' );
572
573     for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
574     {
575         if (xforms[i][0] == ':')
576             r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
577         else
578             r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
579     }
580
581     msi_free( xforms );
582     msi_free( xform_list );
583
584     return r;
585 }
586
587 BOOL ui_sequence_exists( MSIPACKAGE *package )
588 {
589     MSIQUERY *view;
590     UINT rc;
591
592     static const WCHAR ExecSeqQuery [] =
593         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594          '`','I','n','s','t','a','l','l',
595          'U','I','S','e','q','u','e','n','c','e','`',
596          ' ','W','H','E','R','E',' ',
597          '`','S','e','q','u','e','n','c','e','`',' ',
598          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599          '`','S','e','q','u','e','n','c','e','`',0};
600
601     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602     if (rc == ERROR_SUCCESS)
603     {
604         msiobj_release(&view->hdr);
605         return TRUE;
606     }
607
608     return FALSE;
609 }
610
611 /****************************************************
612  * TOP level entry points 
613  *****************************************************/
614
615 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
616                          LPCWSTR szCommandLine )
617 {
618     UINT rc;
619     BOOL ui = FALSE, ui_exists;
620     static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
621     static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
622     static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
623
624     MSI_SetPropertyW(package, szAction, szInstall);
625
626     package->script = msi_alloc_zero(sizeof(MSISCRIPT));
627
628     package->script->InWhatSequence = SEQUENCE_INSTALL;
629
630     if (szPackagePath)   
631     {
632         LPWSTR p, check, dir;
633         LPCWSTR file;
634
635         dir = strdupW(szPackagePath);
636         p = strrchrW(dir, '\\');
637         if (p)
638         {
639             *(++p) = 0;
640             file = szPackagePath + (p - dir);
641         }
642         else
643         {
644             msi_free(dir);
645             dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
646             GetCurrentDirectoryW(MAX_PATH, dir);
647             lstrcatW(dir, cszbs);
648             file = szPackagePath;
649         }
650
651         msi_free( package->PackagePath );
652         package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
653         if (!package->PackagePath)
654         {
655             msi_free(dir);
656             return ERROR_OUTOFMEMORY;
657         }
658
659         lstrcpyW(package->PackagePath, dir);
660         lstrcatW(package->PackagePath, file);
661
662         check = msi_dup_property( package, cszSourceDir );
663         if (!check)
664             MSI_SetPropertyW(package, cszSourceDir, dir);
665         msi_free(check);
666
667         check = msi_dup_property( package, cszSOURCEDIR );
668         if (!check)
669             MSI_SetPropertyW(package, cszSOURCEDIR, dir);
670
671         msi_free(dir);
672         msi_free(check);
673     }
674
675     msi_parse_command_line( package, szCommandLine );
676
677     msi_apply_transforms( package );
678     msi_apply_patches( package );
679
680     /* properties may have been added by a transform */
681     msi_clone_properties( package );
682
683     if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
684     {
685         package->script->InWhatSequence |= SEQUENCE_UI;
686         rc = ACTION_ProcessUISequence(package);
687         ui = TRUE;
688         ui_exists = ui_sequence_exists(package);
689         if (rc == ERROR_SUCCESS || !ui_exists)
690         {
691             package->script->InWhatSequence |= SEQUENCE_EXEC;
692             rc = ACTION_ProcessExecSequence(package,ui_exists);
693         }
694     }
695     else
696         rc = ACTION_ProcessExecSequence(package,FALSE);
697     
698     if (rc == -1)
699     {
700         /* install was halted but should be considered a success */
701         rc = ERROR_SUCCESS;
702     }
703
704     package->script->CurrentlyScripting= FALSE;
705
706     /* process the ending type action */
707     if (rc == ERROR_SUCCESS)
708         ACTION_PerformActionSequence(package,-1,ui);
709     else if (rc == ERROR_INSTALL_USEREXIT) 
710         ACTION_PerformActionSequence(package,-2,ui);
711     else if (rc == ERROR_INSTALL_SUSPEND) 
712         ACTION_PerformActionSequence(package,-4,ui);
713     else  /* failed */
714         ACTION_PerformActionSequence(package,-3,ui);
715
716     /* finish up running custom actions */
717     ACTION_FinishCustomActions(package);
718     
719     return rc;
720 }
721
722 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
723 {
724     UINT rc = ERROR_SUCCESS;
725     MSIRECORD * row = 0;
726     static const WCHAR ExecSeqQuery[] =
727         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
728          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
729          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
730          '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
731
732     static const WCHAR UISeqQuery[] =
733         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
734      '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
735      '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
736          ' ', '=',' ','%','i',0};
737
738     if (UI)
739         row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
740     else
741         row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
742
743     if (row)
744     {
745         LPCWSTR action, cond;
746
747         TRACE("Running the actions\n"); 
748
749         /* check conditions */
750         cond = MSI_RecordGetString(row,2);
751
752         /* this is a hack to skip errors in the condition code */
753         if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
754             goto end;
755
756         action = MSI_RecordGetString(row,1);
757         if (!action)
758         {
759             ERR("failed to fetch action\n");
760             rc = ERROR_FUNCTION_FAILED;
761             goto end;
762         }
763
764         if (UI)
765             rc = ACTION_PerformUIAction(package,action,-1);
766         else
767             rc = ACTION_PerformAction(package,action,-1,FALSE);
768 end:
769         msiobj_release(&row->hdr);
770     }
771     else
772         rc = ERROR_SUCCESS;
773
774     return rc;
775 }
776
777 typedef struct {
778     MSIPACKAGE* package;
779     BOOL UI;
780 } iterate_action_param;
781
782 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
783 {
784     iterate_action_param *iap= (iterate_action_param*)param;
785     UINT rc;
786     LPCWSTR cond, action;
787
788     action = MSI_RecordGetString(row,1);
789     if (!action)
790     {
791         ERR("Error is retrieving action name\n");
792         return ERROR_FUNCTION_FAILED;
793     }
794
795     /* check conditions */
796     cond = MSI_RecordGetString(row,2);
797
798     /* this is a hack to skip errors in the condition code */
799     if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
800     {
801         TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
802         return ERROR_SUCCESS;
803     }
804
805     if (iap->UI)
806         rc = ACTION_PerformUIAction(iap->package,action,-1);
807     else
808         rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
809
810     msi_dialog_check_messages( NULL );
811
812     if (iap->package->CurrentInstallState != ERROR_SUCCESS )
813         rc = iap->package->CurrentInstallState;
814
815     if (rc == ERROR_FUNCTION_NOT_CALLED)
816         rc = ERROR_SUCCESS;
817
818     if (rc != ERROR_SUCCESS)
819         ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
820
821     return rc;
822 }
823
824 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
825 {
826     MSIQUERY * view;
827     UINT r;
828     static const WCHAR query[] =
829         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
830          '`','%','s','`',
831          ' ','W','H','E','R','E',' ', 
832          '`','S','e','q','u','e','n','c','e','`',' ',
833          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
834          '`','S','e','q','u','e','n','c','e','`',0};
835     iterate_action_param iap;
836
837     /*
838      * FIXME: probably should be checking UILevel in the
839      *       ACTION_PerformUIAction/ACTION_PerformAction
840      *       rather than saving the UI level here. Those
841      *       two functions can be merged too.
842      */
843     iap.package = package;
844     iap.UI = TRUE;
845
846     TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
847
848     r = MSI_OpenQuery( package->db, &view, query, szTable );
849     if (r == ERROR_SUCCESS)
850     {
851         r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
852         msiobj_release(&view->hdr);
853     }
854
855     return r;
856 }
857
858 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
859 {
860     MSIQUERY * view;
861     UINT rc;
862     static const WCHAR ExecSeqQuery[] =
863         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
864          '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
865          'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
866          '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
867          'O','R','D','E','R',' ', 'B','Y',' ',
868          '`','S','e','q','u','e','n','c','e','`',0 };
869     MSIRECORD * row = 0;
870     static const WCHAR IVQuery[] =
871         {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
872          ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
873          'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
874          'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
875          ' ','\'', 'I','n','s','t','a','l','l',
876          'V','a','l','i','d','a','t','e','\'', 0};
877     INT seq = 0;
878     iterate_action_param iap;
879
880     iap.package = package;
881     iap.UI = FALSE;
882
883     if (package->script->ExecuteSequenceRun)
884     {
885         TRACE("Execute Sequence already Run\n");
886         return ERROR_SUCCESS;
887     }
888
889     package->script->ExecuteSequenceRun = TRUE;
890
891     /* get the sequence number */
892     if (UIran)
893     {
894         row = MSI_QueryGetRecord(package->db, IVQuery);
895         if( !row )
896             return ERROR_FUNCTION_FAILED;
897         seq = MSI_RecordGetInteger(row,1);
898         msiobj_release(&row->hdr);
899     }
900
901     rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
902     if (rc == ERROR_SUCCESS)
903     {
904         TRACE("Running the actions\n");
905
906         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
907         msiobj_release(&view->hdr);
908     }
909
910     return rc;
911 }
912
913 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
914 {
915     MSIQUERY * view;
916     UINT rc;
917     static const WCHAR ExecSeqQuery [] =
918         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
919          '`','I','n','s','t','a','l','l',
920          'U','I','S','e','q','u','e','n','c','e','`',
921          ' ','W','H','E','R','E',' ', 
922          '`','S','e','q','u','e','n','c','e','`',' ',
923          '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
924          '`','S','e','q','u','e','n','c','e','`',0};
925     iterate_action_param iap;
926
927     iap.package = package;
928     iap.UI = TRUE;
929
930     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
931     
932     if (rc == ERROR_SUCCESS)
933     {
934         TRACE("Running the actions\n"); 
935
936         rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
937         msiobj_release(&view->hdr);
938     }
939
940     return rc;
941 }
942
943 /********************************************************
944  * ACTION helper functions and functions that perform the actions
945  *******************************************************/
946 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, 
947                                         UINT* rc, BOOL force )
948 {
949     BOOL ret = FALSE; 
950     BOOL run = force;
951     int i;
952
953     if (!run && !package->script->CurrentlyScripting)
954         run = TRUE;
955    
956     if (!run)
957     {
958         if (strcmpW(action,szInstallFinalize) == 0 ||
959             strcmpW(action,szInstallExecute) == 0 ||
960             strcmpW(action,szInstallExecuteAgain) == 0) 
961                 run = TRUE;
962     }
963     
964     i = 0;
965     while (StandardActions[i].action != NULL)
966     {
967         if (strcmpW(StandardActions[i].action, action)==0)
968         {
969             if (!run)
970             {
971                 ui_actioninfo(package, action, TRUE, 0);
972                 *rc = schedule_action(package,INSTALL_SCRIPT,action);
973                 ui_actioninfo(package, action, FALSE, *rc);
974             }
975             else
976             {
977                 ui_actionstart(package, action);
978                 if (StandardActions[i].handler)
979                 {
980                     *rc = StandardActions[i].handler(package);
981                 }
982                 else
983                 {
984                     FIXME("unhandled standard action %s\n",debugstr_w(action));
985                     *rc = ERROR_SUCCESS;
986                 }
987             }
988             ret = TRUE;
989             break;
990         }
991         i++;
992     }
993     return ret;
994 }
995
996 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
997                                        UINT* rc, UINT script, BOOL force )
998 {
999     BOOL ret=FALSE;
1000     UINT arc;
1001
1002     arc = ACTION_CustomAction(package, action, script, force);
1003
1004     if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1005     {
1006         *rc = arc;
1007         ret = TRUE;
1008     }
1009     return ret;
1010 }
1011
1012 /* 
1013  * A lot of actions are really important even if they don't do anything
1014  * explicit... Lots of properties are set at the beginning of the installation
1015  * CostFinalize does a bunch of work to translate the directories and such
1016  * 
1017  * But until I get write access to the database that is hard, so I am going to
1018  * hack it to see if I can get something to run.
1019  */
1020 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1021 {
1022     UINT rc = ERROR_SUCCESS; 
1023     BOOL handled;
1024
1025     TRACE("Performing action (%s)\n",debugstr_w(action));
1026
1027     handled = ACTION_HandleStandardAction(package, action, &rc, force);
1028
1029     if (!handled)
1030         handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1031
1032     if (!handled)
1033     {
1034         FIXME("unhandled msi action %s\n",debugstr_w(action));
1035         rc = ERROR_FUNCTION_NOT_CALLED;
1036     }
1037
1038     return rc;
1039 }
1040
1041 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1042 {
1043     UINT rc = ERROR_SUCCESS;
1044     BOOL handled = FALSE;
1045
1046     TRACE("Performing action (%s)\n",debugstr_w(action));
1047
1048     handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1049
1050     if (!handled)
1051         handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1052
1053     if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1054         handled = TRUE;
1055
1056     if (!handled)
1057     {
1058         FIXME("unhandled msi action %s\n",debugstr_w(action));
1059         rc = ERROR_FUNCTION_NOT_CALLED;
1060     }
1061
1062     return rc;
1063 }
1064
1065
1066 /*
1067  * Actual Action Handlers
1068  */
1069
1070 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1071 {
1072     MSIPACKAGE *package = (MSIPACKAGE*)param;
1073     LPCWSTR dir;
1074     LPWSTR full_path;
1075     MSIRECORD *uirow;
1076     MSIFOLDER *folder;
1077
1078     dir = MSI_RecordGetString(row,1);
1079     if (!dir)
1080     {
1081         ERR("Unable to get folder id\n");
1082         return ERROR_SUCCESS;
1083     }
1084
1085     full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1086     if (!full_path)
1087     {
1088         ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1089         return ERROR_SUCCESS;
1090     }
1091
1092     TRACE("Folder is %s\n",debugstr_w(full_path));
1093
1094     /* UI stuff */
1095     uirow = MSI_CreateRecord(1);
1096     MSI_RecordSetStringW(uirow,1,full_path);
1097     ui_actiondata(package,szCreateFolders,uirow);
1098     msiobj_release( &uirow->hdr );
1099
1100     if (folder->State == 0)
1101         create_full_pathW(full_path);
1102
1103     folder->State = 3;
1104
1105     msi_free(full_path);
1106     return ERROR_SUCCESS;
1107 }
1108
1109 /* FIXME: probably should merge this with the above function */
1110 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1111 {
1112     UINT rc = ERROR_SUCCESS;
1113     MSIFOLDER *folder;
1114     LPWSTR install_path;
1115
1116     install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1117     if (!install_path)
1118         return ERROR_FUNCTION_FAILED; 
1119
1120     /* create the path */
1121     if (folder->State == 0)
1122     {
1123         create_full_pathW(install_path);
1124         folder->State = 2;
1125     }
1126     msi_free(install_path);
1127
1128     return rc;
1129 }
1130
1131 UINT msi_create_component_directories( MSIPACKAGE *package )
1132 {
1133     MSICOMPONENT *comp;
1134
1135     /* create all the folders required by the components are going to install */
1136     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1137     {
1138         if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1139             continue;
1140         msi_create_directory( package, comp->Directory );
1141     }
1142
1143     return ERROR_SUCCESS;
1144 }
1145
1146 /*
1147  * Also we cannot enable/disable components either, so for now I am just going 
1148  * to do all the directories for all the components.
1149  */
1150 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1151 {
1152     static const WCHAR ExecSeqQuery[] =
1153         {'S','E','L','E','C','T',' ',
1154          '`','D','i','r','e','c','t','o','r','y','_','`',
1155          ' ','F','R','O','M',' ',
1156          '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1157     UINT rc;
1158     MSIQUERY *view;
1159
1160     /* create all the empty folders specified in the CreateFolder table */
1161     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1162     if (rc != ERROR_SUCCESS)
1163         return ERROR_SUCCESS;
1164
1165     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1166     msiobj_release(&view->hdr);
1167
1168     msi_create_component_directories( package );
1169
1170     return rc;
1171 }
1172
1173 static UINT load_component( MSIRECORD *row, LPVOID param )
1174 {
1175     MSIPACKAGE *package = param;
1176     MSICOMPONENT *comp;
1177
1178     comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1179     if (!comp)
1180         return ERROR_FUNCTION_FAILED;
1181
1182     list_add_tail( &package->components, &comp->entry );
1183
1184     /* fill in the data */
1185     comp->Component = msi_dup_record_field( row, 1 );
1186
1187     TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1188
1189     comp->ComponentId = msi_dup_record_field( row, 2 );
1190     comp->Directory = msi_dup_record_field( row, 3 );
1191     comp->Attributes = MSI_RecordGetInteger(row,4);
1192     comp->Condition = msi_dup_record_field( row, 5 );
1193     comp->KeyPath = msi_dup_record_field( row, 6 );
1194
1195     comp->Installed = INSTALLSTATE_UNKNOWN;
1196     msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1197
1198     return ERROR_SUCCESS;
1199 }
1200
1201 static UINT load_all_components( MSIPACKAGE *package )
1202 {
1203     static const WCHAR query[] = {
1204         'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ', 
1205          '`','C','o','m','p','o','n','e','n','t','`',0 };
1206     MSIQUERY *view;
1207     UINT r;
1208
1209     if (!list_empty(&package->components))
1210         return ERROR_SUCCESS;
1211
1212     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1213     if (r != ERROR_SUCCESS)
1214         return r;
1215
1216     r = MSI_IterateRecords(view, NULL, load_component, package);
1217     msiobj_release(&view->hdr);
1218     return r;
1219 }
1220
1221 typedef struct {
1222     MSIPACKAGE *package;
1223     MSIFEATURE *feature;
1224 } _ilfs;
1225
1226 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1227 {
1228     ComponentList *cl;
1229
1230     cl = msi_alloc( sizeof (*cl) );
1231     if ( !cl )
1232         return ERROR_NOT_ENOUGH_MEMORY;
1233     cl->component = comp;
1234     list_add_tail( &feature->Components, &cl->entry );
1235
1236     return ERROR_SUCCESS;
1237 }
1238
1239 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1240 {
1241     FeatureList *fl;
1242
1243     fl = msi_alloc( sizeof(*fl) );
1244     if ( !fl )
1245         return ERROR_NOT_ENOUGH_MEMORY;
1246     fl->feature = child;
1247     list_add_tail( &parent->Children, &fl->entry );
1248
1249     return ERROR_SUCCESS;
1250 }
1251
1252 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1253 {
1254     _ilfs* ilfs= (_ilfs*)param;
1255     LPCWSTR component;
1256     MSICOMPONENT *comp;
1257
1258     component = MSI_RecordGetString(row,1);
1259
1260     /* check to see if the component is already loaded */
1261     comp = get_loaded_component( ilfs->package, component );
1262     if (!comp)
1263     {
1264         ERR("unknown component %s\n", debugstr_w(component));
1265         return ERROR_FUNCTION_FAILED;
1266     }
1267
1268     add_feature_component( ilfs->feature, comp );
1269     comp->Enabled = TRUE;
1270
1271     return ERROR_SUCCESS;
1272 }
1273
1274 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1275 {
1276     MSIFEATURE *feature;
1277
1278     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1279     {
1280         if ( !lstrcmpW( feature->Feature, name ) )
1281             return feature;
1282     }
1283
1284     return NULL;
1285 }
1286
1287 static UINT load_feature(MSIRECORD * row, LPVOID param)
1288 {
1289     MSIPACKAGE* package = (MSIPACKAGE*)param;
1290     MSIFEATURE* feature;
1291     static const WCHAR Query1[] = 
1292         {'S','E','L','E','C','T',' ',
1293          '`','C','o','m','p','o','n','e','n','t','_','`',
1294          ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1295          'C','o','m','p','o','n','e','n','t','s','`',' ',
1296          'W','H','E','R','E',' ',
1297          '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1298     MSIQUERY * view;
1299     UINT    rc;
1300     _ilfs ilfs;
1301
1302     /* fill in the data */
1303
1304     feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1305     if (!feature)
1306         return ERROR_NOT_ENOUGH_MEMORY;
1307
1308     list_init( &feature->Children );
1309     list_init( &feature->Components );
1310     
1311     feature->Feature = msi_dup_record_field( row, 1 );
1312
1313     TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1314
1315     feature->Feature_Parent = msi_dup_record_field( row, 2 );
1316     feature->Title = msi_dup_record_field( row, 3 );
1317     feature->Description = msi_dup_record_field( row, 4 );
1318
1319     if (!MSI_RecordIsNull(row,5))
1320         feature->Display = MSI_RecordGetInteger(row,5);
1321   
1322     feature->Level= MSI_RecordGetInteger(row,6);
1323     feature->Directory = msi_dup_record_field( row, 7 );
1324     feature->Attributes = MSI_RecordGetInteger(row,8);
1325
1326     feature->Installed = INSTALLSTATE_UNKNOWN;
1327     msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1328
1329     list_add_tail( &package->features, &feature->entry );
1330
1331     /* load feature components */
1332
1333     rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1334     if (rc != ERROR_SUCCESS)
1335         return ERROR_SUCCESS;
1336
1337     ilfs.package = package;
1338     ilfs.feature = feature;
1339
1340     MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1341     msiobj_release(&view->hdr);
1342
1343     return ERROR_SUCCESS;
1344 }
1345
1346 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1347 {
1348     MSIPACKAGE* package = (MSIPACKAGE*)param;
1349     MSIFEATURE *parent, *child;
1350
1351     child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1352     if (!child)
1353         return ERROR_FUNCTION_FAILED;
1354
1355     if (!child->Feature_Parent)
1356         return ERROR_SUCCESS;
1357
1358     parent = find_feature_by_name( package, child->Feature_Parent );
1359     if (!parent)
1360         return ERROR_FUNCTION_FAILED;
1361
1362     add_feature_child( parent, child );
1363     return ERROR_SUCCESS;
1364 }
1365
1366 static UINT load_all_features( MSIPACKAGE *package )
1367 {
1368     static const WCHAR query[] = {
1369         'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1370         '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1371         ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1372     MSIQUERY *view;
1373     UINT r;
1374
1375     if (!list_empty(&package->features))
1376         return ERROR_SUCCESS;
1377  
1378     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1379     if (r != ERROR_SUCCESS)
1380         return r;
1381
1382     r = MSI_IterateRecords( view, NULL, load_feature, package );
1383     if (r != ERROR_SUCCESS)
1384         return r;
1385
1386     r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1387     msiobj_release( &view->hdr );
1388
1389     return r;
1390 }
1391
1392 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1393 {
1394     if (!p)
1395         return p;
1396     p = strchrW(p, ch);
1397     if (!p)
1398         return p;
1399     *p = 0;
1400     return p+1;
1401 }
1402
1403 static UINT load_file(MSIRECORD *row, LPVOID param)
1404 {
1405     MSIPACKAGE* package = (MSIPACKAGE*)param;
1406     LPCWSTR component;
1407     MSIFILE *file;
1408
1409     /* fill in the data */
1410
1411     file = msi_alloc_zero( sizeof (MSIFILE) );
1412     if (!file)
1413         return ERROR_NOT_ENOUGH_MEMORY;
1414  
1415     file->File = msi_dup_record_field( row, 1 );
1416
1417     component = MSI_RecordGetString( row, 2 );
1418     file->Component = get_loaded_component( package, component );
1419
1420     if (!file->Component)
1421         ERR("Unfound Component %s\n",debugstr_w(component));
1422
1423     file->FileName = msi_dup_record_field( row, 3 );
1424     reduce_to_longfilename( file->FileName );
1425
1426     file->ShortName = msi_dup_record_field( row, 3 );
1427     file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1428     
1429     file->FileSize = MSI_RecordGetInteger( row, 4 );
1430     file->Version = msi_dup_record_field( row, 5 );
1431     file->Language = msi_dup_record_field( row, 6 );
1432     file->Attributes = MSI_RecordGetInteger( row, 7 );
1433     file->Sequence = MSI_RecordGetInteger( row, 8 );
1434
1435     file->state = msifs_invalid;
1436
1437     /* if the compressed bits are not set in the file attributes,
1438      * then read the information from the package word count property
1439      */
1440     if (file->Attributes & msidbFileAttributesCompressed)
1441     {
1442         file->IsCompressed = TRUE;
1443     }
1444     else if (file->Attributes & msidbFileAttributesNoncompressed)
1445     {
1446         file->IsCompressed = FALSE;
1447     }
1448     else
1449     {
1450         file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1451     }
1452
1453     TRACE("File Loaded (%s)\n",debugstr_w(file->File));  
1454
1455     list_add_tail( &package->files, &file->entry );
1456  
1457     return ERROR_SUCCESS;
1458 }
1459
1460 static UINT load_all_files(MSIPACKAGE *package)
1461 {
1462     MSIQUERY * view;
1463     UINT rc;
1464     static const WCHAR Query[] =
1465         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1466          '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1467          '`','S','e','q','u','e','n','c','e','`', 0};
1468
1469     if (!list_empty(&package->files))
1470         return ERROR_SUCCESS;
1471
1472     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1473     if (rc != ERROR_SUCCESS)
1474         return ERROR_SUCCESS;
1475
1476     rc = MSI_IterateRecords(view, NULL, load_file, package);
1477     msiobj_release(&view->hdr);
1478
1479     return ERROR_SUCCESS;
1480 }
1481
1482 static UINT load_folder( MSIRECORD *row, LPVOID param )
1483 {
1484     MSIPACKAGE *package = param;
1485     static const WCHAR szDot[] = { '.',0 };
1486     static WCHAR szEmpty[] = { 0 };
1487     LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1488     MSIFOLDER *folder;
1489
1490     folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1491     if (!folder)
1492         return ERROR_NOT_ENOUGH_MEMORY;
1493
1494     folder->Directory = msi_dup_record_field( row, 1 );
1495
1496     TRACE("%s\n", debugstr_w(folder->Directory));
1497
1498     p = msi_dup_record_field(row, 3);
1499
1500     /* split src and target dir */
1501     tgt_short = p;
1502     src_short = folder_split_path( p, ':' );
1503
1504     /* split the long and short paths */
1505     tgt_long = folder_split_path( tgt_short, '|' );
1506     src_long = folder_split_path( src_short, '|' );
1507
1508     /* check for no-op dirs */
1509     if (!lstrcmpW(szDot, tgt_short))
1510         tgt_short = szEmpty;
1511     if (!lstrcmpW(szDot, src_short))
1512         src_short = szEmpty;
1513
1514     if (!tgt_long)
1515         tgt_long = tgt_short;
1516
1517     if (!src_short) {
1518         src_short = tgt_short;
1519         src_long = tgt_long;
1520     }
1521
1522     if (!src_long)
1523         src_long = src_short;
1524
1525     /* FIXME: use the target short path too */
1526     folder->TargetDefault = strdupW(tgt_long);
1527     folder->SourceShortPath = strdupW(src_short);
1528     folder->SourceLongPath = strdupW(src_long);
1529     msi_free(p);
1530
1531     TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1532     TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1533     TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1534
1535     folder->Parent = msi_dup_record_field( row, 2 );
1536
1537     folder->Property = msi_dup_property( package, folder->Directory );
1538
1539     list_add_tail( &package->folders, &folder->entry );
1540
1541     TRACE("returning %p\n", folder);
1542
1543     return ERROR_SUCCESS;
1544 }
1545
1546 static UINT load_all_folders( MSIPACKAGE *package )
1547 {
1548     static const WCHAR query[] = {
1549         'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1550          '`','D','i','r','e','c','t','o','r','y','`',0 };
1551     MSIQUERY *view;
1552     UINT r;
1553
1554     if (!list_empty(&package->folders))
1555         return ERROR_SUCCESS;
1556
1557     r = MSI_DatabaseOpenViewW( package->db, query, &view );
1558     if (r != ERROR_SUCCESS)
1559         return r;
1560
1561     r = MSI_IterateRecords(view, NULL, load_folder, package);
1562     msiobj_release(&view->hdr);
1563     return r;
1564 }
1565
1566 /*
1567  * I am not doing any of the costing functionality yet.
1568  * Mostly looking at doing the Component and Feature loading
1569  *
1570  * The native MSI does A LOT of modification to tables here. Mostly adding
1571  * a lot of temporary columns to the Feature and Component tables.
1572  *
1573  *    note: Native msi also tracks the short filename. But I am only going to
1574  *          track the long ones.  Also looking at this directory table
1575  *          it appears that the directory table does not get the parents
1576  *          resolved base on property only based on their entries in the
1577  *          directory table.
1578  */
1579 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1580 {
1581     static const WCHAR szCosting[] =
1582         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1583     static const WCHAR szZero[] = { '0', 0 };
1584
1585     MSI_SetPropertyW(package, szCosting, szZero);
1586     MSI_SetPropertyW(package, cszRootDrive, c_colon);
1587
1588     load_all_components( package );
1589     load_all_features( package );
1590     load_all_files( package );
1591     load_all_folders( package );
1592
1593     return ERROR_SUCCESS;
1594 }
1595
1596 static UINT execute_script(MSIPACKAGE *package, UINT script )
1597 {
1598     int i;
1599     UINT rc = ERROR_SUCCESS;
1600
1601     TRACE("Executing Script %i\n",script);
1602
1603     if (!package->script)
1604     {
1605         ERR("no script!\n");
1606         return ERROR_FUNCTION_FAILED;
1607     }
1608
1609     for (i = 0; i < package->script->ActionCount[script]; i++)
1610     {
1611         LPWSTR action;
1612         action = package->script->Actions[script][i];
1613         ui_actionstart(package, action);
1614         TRACE("Executing Action (%s)\n",debugstr_w(action));
1615         rc = ACTION_PerformAction(package, action, script, TRUE);
1616         if (rc != ERROR_SUCCESS)
1617             break;
1618     }
1619     msi_free_action_script(package, script);
1620     return rc;
1621 }
1622
1623 static UINT ACTION_FileCost(MSIPACKAGE *package)
1624 {
1625     return ERROR_SUCCESS;
1626 }
1627
1628 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1629 {
1630     MSICOMPONENT *comp;
1631
1632     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1633     {
1634         INSTALLSTATE res;
1635
1636         if (!comp->ComponentId)
1637             continue;
1638
1639         res = MsiGetComponentPathW( package->ProductCode,
1640                                     comp->ComponentId, NULL, NULL);
1641         if (res < 0)
1642             res = INSTALLSTATE_ABSENT;
1643         comp->Installed = res;
1644     }
1645 }
1646
1647 /* scan for and update current install states */
1648 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1649 {
1650     MSICOMPONENT *comp;
1651     MSIFEATURE *feature;
1652
1653     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1654     {
1655         ComponentList *cl;
1656         INSTALLSTATE res = INSTALLSTATE_ABSENT;
1657
1658         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1659         {
1660             comp= cl->component;
1661
1662             if (!comp->ComponentId)
1663             {
1664                 res = INSTALLSTATE_ABSENT;
1665                 break;
1666             }
1667
1668             if (res == INSTALLSTATE_ABSENT)
1669                 res = comp->Installed;
1670             else
1671             {
1672                 if (res == comp->Installed)
1673                     continue;
1674
1675                 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1676                     res != INSTALLSTATE_SOURCE)
1677                 {
1678                     res = INSTALLSTATE_INCOMPLETE;
1679                 }
1680             }
1681         }
1682         feature->Installed = res;
1683     }
1684 }
1685
1686 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property, 
1687                                     INSTALLSTATE state)
1688 {
1689     static const WCHAR all[]={'A','L','L',0};
1690     LPWSTR override;
1691     MSIFEATURE *feature;
1692
1693     override = msi_dup_property( package, property );
1694     if (!override)
1695         return FALSE;
1696
1697     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1698     {
1699         if (strcmpiW(override,all)==0)
1700             msi_feature_set_state( feature, state );
1701         else
1702         {
1703             LPWSTR ptr = override;
1704             LPWSTR ptr2 = strchrW(override,',');
1705
1706             while (ptr)
1707             {
1708                 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1709                     || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1710                 {
1711                     msi_feature_set_state( feature, state );
1712                     break;
1713                 }
1714                 if (ptr2)
1715                 {
1716                     ptr=ptr2+1;
1717                     ptr2 = strchrW(ptr,',');
1718                 }
1719                 else
1720                     break;
1721             }
1722         }
1723     }
1724     msi_free(override);
1725
1726     return TRUE;
1727 }
1728
1729 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1730 {
1731     int install_level;
1732     static const WCHAR szlevel[] =
1733         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1734     static const WCHAR szAddLocal[] =
1735         {'A','D','D','L','O','C','A','L',0};
1736     static const WCHAR szRemove[] =
1737         {'R','E','M','O','V','E',0};
1738     static const WCHAR szReinstall[] =
1739         {'R','E','I','N','S','T','A','L','L',0};
1740     BOOL override = FALSE;
1741     MSICOMPONENT* component;
1742     MSIFEATURE *feature;
1743
1744
1745     /* I do not know if this is where it should happen.. but */
1746
1747     TRACE("Checking Install Level\n");
1748
1749     install_level = msi_get_property_int( package, szlevel, 1 );
1750
1751     /* ok here is the _real_ rub
1752      * all these activation/deactivation things happen in order and things
1753      * later on the list override things earlier on the list.
1754      * 1) INSTALLLEVEL processing
1755      * 2) ADDLOCAL
1756      * 3) REMOVE
1757      * 4) ADDSOURCE
1758      * 5) ADDDEFAULT
1759      * 6) REINSTALL
1760      * 7) COMPADDLOCAL
1761      * 8) COMPADDSOURCE
1762      * 9) FILEADDLOCAL
1763      * 10) FILEADDSOURCE
1764      * 11) FILEADDDEFAULT
1765      * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1766      * ignored for all the features. seems strange, especially since it is not
1767      * documented anywhere, but it is how it works.
1768      *
1769      * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1770      * REMOVE are the big ones, since we don't handle administrative installs
1771      * yet anyway.
1772      */
1773     override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1774     override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1775     override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1776
1777     if (!override)
1778     {
1779         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1780         {
1781             BOOL feature_state = ((feature->Level > 0) &&
1782                              (feature->Level <= install_level));
1783
1784             if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1785             {
1786                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1787                     msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1788                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1789                     msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1790                 else
1791                     msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1792             }
1793         }
1794
1795         /* disable child features of unselected parent features */
1796         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1797         {
1798             FeatureList *fl;
1799
1800             if (feature->Level > 0 && feature->Level <= install_level)
1801                 continue;
1802
1803             LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1804                 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1805         }
1806     }
1807     else
1808     {
1809         /* set the Preselected Property */
1810         static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1811         static const WCHAR szOne[] = { '1', 0 };
1812
1813         MSI_SetPropertyW(package,szPreselected,szOne);
1814     }
1815
1816     /*
1817      * now we want to enable or disable components base on feature
1818      */
1819
1820     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1821     {
1822         ComponentList *cl;
1823
1824         TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1825               debugstr_w(feature->Feature), feature->Installed, feature->Action);
1826
1827         /* features with components that have compressed files are made local */
1828         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1829         {
1830             if (cl->component->Enabled &&
1831                 cl->component->ForceLocalState &&
1832                 feature->Action == INSTALLSTATE_SOURCE)
1833             {
1834                 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1835                 break;
1836             }
1837         }
1838
1839         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1840         {
1841             component = cl->component;
1842
1843             if (!component->Enabled)
1844                 continue;
1845
1846             switch (feature->Action)
1847             {
1848             case INSTALLSTATE_ABSENT:
1849                 component->anyAbsent = 1;
1850                 break;
1851             case INSTALLSTATE_ADVERTISED:
1852                 component->hasAdvertiseFeature = 1;
1853                 break;
1854             case INSTALLSTATE_SOURCE:
1855                 component->hasSourceFeature = 1;
1856                 break;
1857             case INSTALLSTATE_LOCAL:
1858                 component->hasLocalFeature = 1;
1859                 break;
1860             case INSTALLSTATE_DEFAULT:
1861                 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1862                     component->hasAdvertiseFeature = 1;
1863                 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1864                     component->hasSourceFeature = 1;
1865                 else
1866                     component->hasLocalFeature = 1;
1867                 break;
1868             default:
1869                 break;
1870             }
1871         }
1872     }
1873
1874     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1875     {
1876         /* if the component isn't enabled, leave it alone */
1877         if (!component->Enabled)
1878             continue;
1879
1880         /* check if it's local or source */
1881         if (!(component->Attributes & msidbComponentAttributesOptional) &&
1882              (component->hasLocalFeature || component->hasSourceFeature))
1883         {
1884             if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1885                  !component->ForceLocalState)
1886                 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1887             else
1888                 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1889             continue;
1890         }
1891
1892         /* if any feature is local, the component must be local too */
1893         if (component->hasLocalFeature)
1894         {
1895             msi_component_set_state( component, INSTALLSTATE_LOCAL );
1896             continue;
1897         }
1898
1899         if (component->hasSourceFeature)
1900         {
1901             msi_component_set_state( component, INSTALLSTATE_SOURCE );
1902             continue;
1903         }
1904
1905         if (component->hasAdvertiseFeature)
1906         {
1907             msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1908             continue;
1909         }
1910
1911         TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1912         if (component->anyAbsent)
1913             msi_component_set_state(component, INSTALLSTATE_ABSENT);
1914     }
1915
1916     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1917     {
1918         if (component->Action == INSTALLSTATE_DEFAULT)
1919         {
1920             TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1921             msi_component_set_state( component, INSTALLSTATE_LOCAL );
1922         }
1923
1924         TRACE("Result: Component %s (Installed %i, Action %i)\n",
1925             debugstr_w(component->Component), component->Installed, component->Action);
1926     }
1927
1928
1929     return ERROR_SUCCESS;
1930 }
1931
1932 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1933 {
1934     MSIPACKAGE *package = (MSIPACKAGE*)param;
1935     LPCWSTR name;
1936     LPWSTR path;
1937     MSIFOLDER *f;
1938
1939     name = MSI_RecordGetString(row,1);
1940
1941     f = get_loaded_folder(package, name);
1942     if (!f) return ERROR_SUCCESS;
1943
1944     /* reset the ResolvedTarget */
1945     msi_free(f->ResolvedTarget);
1946     f->ResolvedTarget = NULL;
1947
1948     /* This helper function now does ALL the work */
1949     TRACE("Dir %s ...\n",debugstr_w(name));
1950     path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1951     TRACE("resolves to %s\n",debugstr_w(path));
1952     msi_free(path);
1953
1954     return ERROR_SUCCESS;
1955 }
1956
1957 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1958 {
1959     MSIPACKAGE *package = (MSIPACKAGE*)param;
1960     LPCWSTR name;
1961     MSIFEATURE *feature;
1962
1963     name = MSI_RecordGetString( row, 1 );
1964
1965     feature = get_loaded_feature( package, name );
1966     if (!feature)
1967         ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1968     else
1969     {
1970         LPCWSTR Condition;
1971         Condition = MSI_RecordGetString(row,3);
1972
1973         if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1974         {
1975             int level = MSI_RecordGetInteger(row,2);
1976             TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1977             feature->Level = level;
1978         }
1979     }
1980     return ERROR_SUCCESS;
1981 }
1982
1983 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1984 {
1985     static const WCHAR name_fmt[] =
1986         {'%','u','.','%','u','.','%','u','.','%','u',0};
1987     static WCHAR name[] = {'\\',0};
1988     VS_FIXEDFILEINFO *lpVer;
1989     WCHAR filever[0x100];
1990     LPVOID version;
1991     DWORD versize;
1992     DWORD handle;
1993     UINT sz;
1994
1995     TRACE("%s\n", debugstr_w(filename));
1996
1997     versize = GetFileVersionInfoSizeW( filename, &handle );
1998     if (!versize)
1999         return NULL;
2000
2001     version = msi_alloc( versize );
2002     GetFileVersionInfoW( filename, 0, versize, version );
2003
2004     if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2005     {
2006         msi_free( version );
2007         return NULL;
2008     }
2009
2010     sprintfW( filever, name_fmt,
2011         HIWORD(lpVer->dwFileVersionMS),
2012         LOWORD(lpVer->dwFileVersionMS),
2013         HIWORD(lpVer->dwFileVersionLS),
2014         LOWORD(lpVer->dwFileVersionLS));
2015
2016     msi_free( version );
2017
2018     return strdupW( filever );
2019 }
2020
2021 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2022 {
2023     LPWSTR file_version;
2024     MSIFILE *file;
2025
2026     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2027     {
2028         MSICOMPONENT* comp = file->Component;
2029         LPWSTR p;
2030
2031         if (!comp)
2032             continue;
2033
2034         if (file->IsCompressed)
2035             comp->ForceLocalState = TRUE;
2036
2037         /* calculate target */
2038         p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2039
2040         msi_free(file->TargetPath);
2041
2042         TRACE("file %s is named %s\n",
2043                debugstr_w(file->File), debugstr_w(file->FileName));
2044
2045         file->TargetPath = build_directory_name(2, p, file->FileName);
2046
2047         msi_free(p);
2048
2049         TRACE("file %s resolves to %s\n",
2050                debugstr_w(file->File), debugstr_w(file->TargetPath));
2051
2052         /* don't check files of components that aren't installed */
2053         if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2054             comp->Installed == INSTALLSTATE_ABSENT)
2055         {
2056             file->state = msifs_missing;  /* assume files are missing */
2057             continue;
2058         }
2059
2060         if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2061         {
2062             file->state = msifs_missing;
2063             comp->Cost += file->FileSize;
2064             comp->Installed = INSTALLSTATE_INCOMPLETE;
2065             continue;
2066         }
2067
2068         if (file->Version &&
2069             (file_version = msi_get_disk_file_version( file->TargetPath )))
2070         {
2071             TRACE("new %s old %s\n", debugstr_w(file->Version),
2072                   debugstr_w(file_version));
2073             /* FIXME: seems like a bad way to compare version numbers */
2074             if (lstrcmpiW(file_version, file->Version)<0)
2075             {
2076                 file->state = msifs_overwrite;
2077                 comp->Cost += file->FileSize;
2078                 comp->Installed = INSTALLSTATE_INCOMPLETE;
2079             }
2080             else
2081                 file->state = msifs_present;
2082             msi_free( file_version );
2083         }
2084         else
2085             file->state = msifs_present;
2086     }
2087
2088     return ERROR_SUCCESS;
2089 }
2090
2091 /*
2092  * A lot is done in this function aside from just the costing.
2093  * The costing needs to be implemented at some point but for now I am going
2094  * to focus on the directory building
2095  *
2096  */
2097 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2098 {
2099     static const WCHAR ExecSeqQuery[] =
2100         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2101          '`','D','i','r','e','c','t','o','r','y','`',0};
2102     static const WCHAR ConditionQuery[] =
2103         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2104          '`','C','o','n','d','i','t','i','o','n','`',0};
2105     static const WCHAR szCosting[] =
2106         {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2107     static const WCHAR szlevel[] =
2108         {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2109     static const WCHAR szOne[] = { '1', 0 };
2110     MSICOMPONENT *comp;
2111     UINT rc;
2112     MSIQUERY * view;
2113     LPWSTR level;
2114
2115     if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2116         return ERROR_SUCCESS;
2117
2118     TRACE("Building Directory properties\n");
2119
2120     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2121     if (rc == ERROR_SUCCESS)
2122     {
2123         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2124                         package);
2125         msiobj_release(&view->hdr);
2126     }
2127
2128     /* read components states from the registry */
2129     ACTION_GetComponentInstallStates(package);
2130
2131     TRACE("File calculations\n");
2132     msi_check_file_install_states( package );
2133
2134     TRACE("Evaluating Condition Table\n");
2135
2136     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2137     if (rc == ERROR_SUCCESS)
2138     {
2139         rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2140                     package);
2141         msiobj_release(&view->hdr);
2142     }
2143
2144     TRACE("Enabling or Disabling Components\n");
2145     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2146     {
2147         if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2148         {
2149             TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2150             comp->Enabled = FALSE;
2151         }
2152     }
2153
2154     MSI_SetPropertyW(package,szCosting,szOne);
2155     /* set default run level if not set */
2156     level = msi_dup_property( package, szlevel );
2157     if (!level)
2158         MSI_SetPropertyW(package,szlevel, szOne);
2159     msi_free(level);
2160
2161     ACTION_UpdateFeatureInstallStates(package);
2162
2163     return MSI_SetFeatureStates(package);
2164 }
2165
2166 /* OK this value is "interpreted" and then formatted based on the 
2167    first few characters */
2168 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
2169                          DWORD *size)
2170 {
2171     LPSTR data = NULL;
2172     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2173     {
2174         if (value[1]=='x')
2175         {
2176             LPWSTR ptr;
2177             CHAR byte[5];
2178             LPWSTR deformated = NULL;
2179             int count;
2180
2181             deformat_string(package, &value[2], &deformated);
2182
2183             /* binary value type */
2184             ptr = deformated;
2185             *type = REG_BINARY;
2186             if (strlenW(ptr)%2)
2187                 *size = (strlenW(ptr)/2)+1;
2188             else
2189                 *size = strlenW(ptr)/2;
2190
2191             data = msi_alloc(*size);
2192
2193             byte[0] = '0'; 
2194             byte[1] = 'x'; 
2195             byte[4] = 0; 
2196             count = 0;
2197             /* if uneven pad with a zero in front */
2198             if (strlenW(ptr)%2)
2199             {
2200                 byte[2]= '0';
2201                 byte[3]= *ptr;
2202                 ptr++;
2203                 data[count] = (BYTE)strtol(byte,NULL,0);
2204                 count ++;
2205                 TRACE("Uneven byte count\n");
2206             }
2207             while (*ptr)
2208             {
2209                 byte[2]= *ptr;
2210                 ptr++;
2211                 byte[3]= *ptr;
2212                 ptr++;
2213                 data[count] = (BYTE)strtol(byte,NULL,0);
2214                 count ++;
2215             }
2216             msi_free(deformated);
2217
2218             TRACE("Data %i bytes(%i)\n",*size,count);
2219         }
2220         else
2221         {
2222             LPWSTR deformated;
2223             LPWSTR p;
2224             DWORD d = 0;
2225             deformat_string(package, &value[1], &deformated);
2226
2227             *type=REG_DWORD; 
2228             *size = sizeof(DWORD);
2229             data = msi_alloc(*size);
2230             p = deformated;
2231             if (*p == '-')
2232                 p++;
2233             while (*p)
2234             {
2235                 if ( (*p < '0') || (*p > '9') )
2236                     break;
2237                 d *= 10;
2238                 d += (*p - '0');
2239                 p++;
2240             }
2241             if (deformated[0] == '-')
2242                 d = -d;
2243             *(LPDWORD)data = d;
2244             TRACE("DWORD %i\n",*(LPDWORD)data);
2245
2246             msi_free(deformated);
2247         }
2248     }
2249     else
2250     {
2251         static const WCHAR szMulti[] = {'[','~',']',0};
2252         LPCWSTR ptr;
2253         *type=REG_SZ;
2254
2255         if (value[0]=='#')
2256         {
2257             if (value[1]=='%')
2258             {
2259                 ptr = &value[2];
2260                 *type=REG_EXPAND_SZ;
2261             }
2262             else
2263                 ptr = &value[1];
2264          }
2265          else
2266             ptr=value;
2267
2268         if (strstrW(value,szMulti))
2269             *type = REG_MULTI_SZ;
2270
2271         *size = deformat_string(package, ptr,(LPWSTR*)&data);
2272     }
2273     return data;
2274 }
2275
2276 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2277 {
2278     MSIPACKAGE *package = (MSIPACKAGE*)param;
2279     static const WCHAR szHCR[] = 
2280         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2281          'R','O','O','T','\\',0};
2282     static const WCHAR szHCU[] =
2283         {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2284          'U','S','E','R','\\',0};
2285     static const WCHAR szHLM[] =
2286         {'H','K','E','Y','_','L','O','C','A','L','_',
2287          'M','A','C','H','I','N','E','\\',0};
2288     static const WCHAR szHU[] =
2289         {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2290
2291     LPSTR value_data = NULL;
2292     HKEY  root_key, hkey;
2293     DWORD type,size;
2294     LPWSTR  deformated;
2295     LPCWSTR szRoot, component, name, key, value;
2296     MSICOMPONENT *comp;
2297     MSIRECORD * uirow;
2298     LPWSTR uikey;
2299     INT   root;
2300     BOOL check_first = FALSE;
2301     UINT rc;
2302
2303     ui_progress(package,2,0,0,0);
2304
2305     value = NULL;
2306     key = NULL;
2307     uikey = NULL;
2308     name = NULL;
2309
2310     component = MSI_RecordGetString(row, 6);
2311     comp = get_loaded_component(package,component);
2312     if (!comp)
2313         return ERROR_SUCCESS;
2314
2315     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2316     {
2317         TRACE("Skipping write due to disabled component %s\n",
2318                         debugstr_w(component));
2319
2320         comp->Action = comp->Installed;
2321
2322         return ERROR_SUCCESS;
2323     }
2324
2325     comp->Action = INSTALLSTATE_LOCAL;
2326
2327     name = MSI_RecordGetString(row, 4);
2328     if( MSI_RecordIsNull(row,5) && name )
2329     {
2330         /* null values can have special meanings */
2331         if (name[0]=='-' && name[1] == 0)
2332                 return ERROR_SUCCESS;
2333         else if ((name[0]=='+' && name[1] == 0) || 
2334                  (name[0] == '*' && name[1] == 0))
2335                 name = NULL;
2336         check_first = TRUE;
2337     }
2338
2339     root = MSI_RecordGetInteger(row,2);
2340     key = MSI_RecordGetString(row, 3);
2341
2342     /* get the root key */
2343     switch (root)
2344     {
2345         case -1: 
2346             {
2347                 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2348                 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2349                 if (all_users && all_users[0] == '1')
2350                 {
2351                     root_key = HKEY_LOCAL_MACHINE;
2352                     szRoot = szHLM;
2353                 }
2354                 else
2355                 {
2356                     root_key = HKEY_CURRENT_USER;
2357                     szRoot = szHCU;
2358                 }
2359                 msi_free(all_users);
2360             }
2361                  break;
2362         case 0:  root_key = HKEY_CLASSES_ROOT; 
2363                  szRoot = szHCR;
2364                  break;
2365         case 1:  root_key = HKEY_CURRENT_USER;
2366                  szRoot = szHCU;
2367                  break;
2368         case 2:  root_key = HKEY_LOCAL_MACHINE;
2369                  szRoot = szHLM;
2370                  break;
2371         case 3:  root_key = HKEY_USERS; 
2372                  szRoot = szHU;
2373                  break;
2374         default:
2375                  ERR("Unknown root %i\n",root);
2376                  root_key=NULL;
2377                  szRoot = NULL;
2378                  break;
2379     }
2380     if (!root_key)
2381         return ERROR_SUCCESS;
2382
2383     deformat_string(package, key , &deformated);
2384     size = strlenW(deformated) + strlenW(szRoot) + 1;
2385     uikey = msi_alloc(size*sizeof(WCHAR));
2386     strcpyW(uikey,szRoot);
2387     strcatW(uikey,deformated);
2388
2389     if (RegCreateKeyW( root_key, deformated, &hkey))
2390     {
2391         ERR("Could not create key %s\n",debugstr_w(deformated));
2392         msi_free(deformated);
2393         msi_free(uikey);
2394         return ERROR_SUCCESS;
2395     }
2396     msi_free(deformated);
2397
2398     value = MSI_RecordGetString(row,5);
2399     if (value)
2400         value_data = parse_value(package, value, &type, &size); 
2401     else
2402     {
2403         static const WCHAR szEmpty[] = {0};
2404         value_data = (LPSTR)strdupW(szEmpty);
2405         size = 0;
2406         type = REG_SZ;
2407     }
2408
2409     deformat_string(package, name, &deformated);
2410
2411     /* get the double nulls to terminate SZ_MULTI */
2412     if (type == REG_MULTI_SZ)
2413         size +=sizeof(WCHAR);
2414
2415     if (!check_first)
2416     {
2417         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2418                         debugstr_w(uikey));
2419         RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2420     }
2421     else
2422     {
2423         DWORD sz = 0;
2424         rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2425         if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2426         {
2427             TRACE("value %s of %s checked already exists\n",
2428                             debugstr_w(deformated), debugstr_w(uikey));
2429         }
2430         else
2431         {
2432             TRACE("Checked and setting value %s of %s\n",
2433                             debugstr_w(deformated), debugstr_w(uikey));
2434             if (deformated || size)
2435                 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2436         }
2437     }
2438     RegCloseKey(hkey);
2439
2440     uirow = MSI_CreateRecord(3);
2441     MSI_RecordSetStringW(uirow,2,deformated);
2442     MSI_RecordSetStringW(uirow,1,uikey);
2443
2444     if (type == REG_SZ)
2445         MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2446     else
2447         MSI_RecordSetStringW(uirow,3,value);
2448
2449     ui_actiondata(package,szWriteRegistryValues,uirow);
2450     msiobj_release( &uirow->hdr );
2451
2452     msi_free(value_data);
2453     msi_free(deformated);
2454     msi_free(uikey);
2455
2456     return ERROR_SUCCESS;
2457 }
2458
2459 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2460 {
2461     UINT rc;
2462     MSIQUERY * view;
2463     static const WCHAR ExecSeqQuery[] =
2464         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2465          '`','R','e','g','i','s','t','r','y','`',0 };
2466
2467     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2468     if (rc != ERROR_SUCCESS)
2469         return ERROR_SUCCESS;
2470
2471     /* increment progress bar each time action data is sent */
2472     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2473
2474     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2475
2476     msiobj_release(&view->hdr);
2477     return rc;
2478 }
2479
2480 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2481 {
2482     package->script->CurrentlyScripting = TRUE;
2483
2484     return ERROR_SUCCESS;
2485 }
2486
2487
2488 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2489 {
2490     MSICOMPONENT *comp;
2491     DWORD progress = 0;
2492     DWORD total = 0;
2493     static const WCHAR q1[]=
2494         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2495          '`','R','e','g','i','s','t','r','y','`',0};
2496     UINT rc;
2497     MSIQUERY * view;
2498     MSIFEATURE *feature;
2499     MSIFILE *file;
2500
2501     TRACE("InstallValidate\n");
2502
2503     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2504     if (rc == ERROR_SUCCESS)
2505     {
2506         MSI_IterateRecords( view, &progress, NULL, package );
2507         msiobj_release( &view->hdr );
2508         total += progress * REG_PROGRESS_VALUE;
2509     }
2510
2511     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2512         total += COMPONENT_PROGRESS_VALUE;
2513
2514     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2515         total += file->FileSize;
2516
2517     ui_progress(package,0,total,0,0);
2518
2519     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2520     {
2521         TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2522             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2523             feature->ActionRequest);
2524     }
2525     
2526     return ERROR_SUCCESS;
2527 }
2528
2529 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2530 {
2531     MSIPACKAGE* package = (MSIPACKAGE*)param;
2532     LPCWSTR cond = NULL; 
2533     LPCWSTR message = NULL;
2534     UINT r;
2535
2536     static const WCHAR title[]=
2537         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2538
2539     cond = MSI_RecordGetString(row,1);
2540
2541     r = MSI_EvaluateConditionW(package,cond);
2542     if (r == MSICONDITION_FALSE)
2543     {
2544         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2545         {
2546             LPWSTR deformated;
2547             message = MSI_RecordGetString(row,2);
2548             deformat_string(package,message,&deformated);
2549             MessageBoxW(NULL,deformated,title,MB_OK);
2550             msi_free(deformated);
2551         }
2552
2553         return ERROR_INSTALL_FAILURE;
2554     }
2555
2556     return ERROR_SUCCESS;
2557 }
2558
2559 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2560 {
2561     UINT rc;
2562     MSIQUERY * view = NULL;
2563     static const WCHAR ExecSeqQuery[] =
2564         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2565          '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2566
2567     TRACE("Checking launch conditions\n");
2568
2569     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2570     if (rc != ERROR_SUCCESS)
2571         return ERROR_SUCCESS;
2572
2573     rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2574     msiobj_release(&view->hdr);
2575
2576     return rc;
2577 }
2578
2579 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2580 {
2581
2582     if (!cmp->KeyPath)
2583         return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2584
2585     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2586     {
2587         MSIRECORD * row = 0;
2588         UINT root,len;
2589         LPWSTR deformated,buffer,deformated_name;
2590         LPCWSTR key,name;
2591         static const WCHAR ExecSeqQuery[] =
2592             {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2593              '`','R','e','g','i','s','t','r','y','`',' ',
2594              'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2595              ' ','=',' ' ,'\'','%','s','\'',0 };
2596         static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2597         static const WCHAR fmt2[]=
2598             {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2599
2600         row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2601         if (!row)
2602             return NULL;
2603
2604         root = MSI_RecordGetInteger(row,2);
2605         key = MSI_RecordGetString(row, 3);
2606         name = MSI_RecordGetString(row, 4);
2607         deformat_string(package, key , &deformated);
2608         deformat_string(package, name, &deformated_name);
2609
2610         len = strlenW(deformated) + 6;
2611         if (deformated_name)
2612             len+=strlenW(deformated_name);
2613
2614         buffer = msi_alloc( len *sizeof(WCHAR));
2615
2616         if (deformated_name)
2617             sprintfW(buffer,fmt2,root,deformated,deformated_name);
2618         else
2619             sprintfW(buffer,fmt,root,deformated);
2620
2621         msi_free(deformated);
2622         msi_free(deformated_name);
2623         msiobj_release(&row->hdr);
2624
2625         return buffer;
2626     }
2627     else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2628     {
2629         FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2630         return NULL;
2631     }
2632     else
2633     {
2634         MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2635
2636         if (file)
2637             return strdupW( file->TargetPath );
2638     }
2639     return NULL;
2640 }
2641
2642 static HKEY openSharedDLLsKey(void)
2643 {
2644     HKEY hkey=0;
2645     static const WCHAR path[] =
2646         {'S','o','f','t','w','a','r','e','\\',
2647          'M','i','c','r','o','s','o','f','t','\\',
2648          'W','i','n','d','o','w','s','\\',
2649          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2650          'S','h','a','r','e','d','D','L','L','s',0};
2651
2652     RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2653     return hkey;
2654 }
2655
2656 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2657 {
2658     HKEY hkey;
2659     DWORD count=0;
2660     DWORD type;
2661     DWORD sz = sizeof(count);
2662     DWORD rc;
2663     
2664     hkey = openSharedDLLsKey();
2665     rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2666     if (rc != ERROR_SUCCESS)
2667         count = 0;
2668     RegCloseKey(hkey);
2669     return count;
2670 }
2671
2672 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2673 {
2674     HKEY hkey;
2675
2676     hkey = openSharedDLLsKey();
2677     if (count > 0)
2678         msi_reg_set_val_dword( hkey, path, count );
2679     else
2680         RegDeleteValueW(hkey,path);
2681     RegCloseKey(hkey);
2682     return count;
2683 }
2684
2685 /*
2686  * Return TRUE if the count should be written out and FALSE if not
2687  */
2688 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2689 {
2690     MSIFEATURE *feature;
2691     INT count = 0;
2692     BOOL write = FALSE;
2693
2694     /* only refcount DLLs */
2695     if (comp->KeyPath == NULL || 
2696         comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
2697         comp->Attributes & msidbComponentAttributesODBCDataSource)
2698         write = FALSE;
2699     else
2700     {
2701         count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2702         write = (count > 0);
2703
2704         if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2705             write = TRUE;
2706     }
2707
2708     /* increment counts */
2709     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2710     {
2711         ComponentList *cl;
2712
2713         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2714             continue;
2715
2716         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2717         {
2718             if ( cl->component == comp )
2719                 count++;
2720         }
2721     }
2722
2723     /* decrement counts */
2724     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2725     {
2726         ComponentList *cl;
2727
2728         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2729             continue;
2730
2731         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2732         {
2733             if ( cl->component == comp )
2734                 count--;
2735         }
2736     }
2737
2738     /* ref count all the files in the component */
2739     if (write)
2740     {
2741         MSIFILE *file;
2742
2743         LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2744         {
2745             if (file->Component == comp)
2746                 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2747         }
2748     }
2749     
2750     /* add a count for permenent */
2751     if (comp->Attributes & msidbComponentAttributesPermanent)
2752         count ++;
2753     
2754     comp->RefCount = count;
2755
2756     if (write)
2757         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2758 }
2759
2760 /*
2761  * Ok further analysis makes me think that this work is
2762  * actually done in the PublishComponents and PublishFeatures
2763  * step, and not here.  It appears like the keypath and all that is
2764  * resolved in this step, however actually written in the Publish steps.
2765  * But we will leave it here for now because it is unclear
2766  */
2767 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2768 {
2769     WCHAR squished_pc[GUID_SIZE];
2770     WCHAR squished_cc[GUID_SIZE];
2771     UINT rc;
2772     MSICOMPONENT *comp;
2773     HKEY hkey=0,hkey2=0;
2774
2775     TRACE("\n");
2776
2777     /* writes the Component and Features values to the registry */
2778
2779     rc = MSIREG_OpenComponents(&hkey);
2780     if (rc != ERROR_SUCCESS)
2781         return rc;
2782
2783     squash_guid(package->ProductCode,squished_pc);
2784     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2785
2786     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2787     {
2788         MSIRECORD * uirow;
2789
2790         ui_progress(package,2,0,0,0);
2791         if (!comp->ComponentId)
2792             continue;
2793
2794         squash_guid(comp->ComponentId,squished_cc);
2795
2796         msi_free(comp->FullKeypath);
2797         comp->FullKeypath = resolve_keypath( package, comp );
2798
2799         /* do the refcounting */
2800         ACTION_RefCountComponent( package, comp );
2801
2802         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2803                             debugstr_w(comp->Component),
2804                             debugstr_w(squished_cc),
2805                             debugstr_w(comp->FullKeypath),
2806                             comp->RefCount);
2807         /*
2808          * Write the keypath out if the component is to be registered
2809          * and delete the key if the component is to be deregistered
2810          */
2811         if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2812         {
2813             rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2814             if (rc != ERROR_SUCCESS)
2815                 continue;
2816
2817             if (!comp->FullKeypath)
2818                 continue;
2819
2820             msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2821
2822             if (comp->Attributes & msidbComponentAttributesPermanent)
2823             {
2824                 static const WCHAR szPermKey[] =
2825                     { '0','0','0','0','0','0','0','0','0','0','0','0',
2826                       '0','0','0','0','0','0','0','0','0','0','0','0',
2827                       '0','0','0','0','0','0','0','0',0 };
2828
2829                 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2830             }
2831
2832             RegCloseKey(hkey2);
2833
2834             rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2835             if (rc != ERROR_SUCCESS)
2836                 continue;
2837
2838             msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2839             RegCloseKey(hkey2);
2840         }
2841         else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2842         {
2843             DWORD res;
2844
2845             rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2846             if (rc != ERROR_SUCCESS)
2847                 continue;
2848
2849             RegDeleteValueW(hkey2,squished_pc);
2850
2851             /* if the key is empty delete it */
2852             res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2853             RegCloseKey(hkey2);
2854             if (res == ERROR_NO_MORE_ITEMS)
2855                 RegDeleteKeyW(hkey,squished_cc);
2856
2857             MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2858         }
2859
2860         /* UI stuff */
2861         uirow = MSI_CreateRecord(3);
2862         MSI_RecordSetStringW(uirow,1,package->ProductCode);
2863         MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2864         MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2865         ui_actiondata(package,szProcessComponents,uirow);
2866         msiobj_release( &uirow->hdr );
2867     }
2868     RegCloseKey(hkey);
2869     return rc;
2870 }
2871
2872 typedef struct {
2873     CLSID       clsid;
2874     LPWSTR      source;
2875
2876     LPWSTR      path;
2877     ITypeLib    *ptLib;
2878 } typelib_struct;
2879
2880 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
2881                                        LPWSTR lpszName, LONG_PTR lParam)
2882 {
2883     TLIBATTR *attr;
2884     typelib_struct *tl_struct = (typelib_struct*) lParam;
2885     static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2886     int sz; 
2887     HRESULT res;
2888
2889     if (!IS_INTRESOURCE(lpszName))
2890     {
2891         ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2892         return TRUE;
2893     }
2894
2895     sz = strlenW(tl_struct->source)+4;
2896     sz *= sizeof(WCHAR);
2897
2898     if ((INT_PTR)lpszName == 1)
2899         tl_struct->path = strdupW(tl_struct->source);
2900     else
2901     {
2902         tl_struct->path = msi_alloc(sz);
2903         sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2904     }
2905
2906     TRACE("trying %s\n", debugstr_w(tl_struct->path));
2907     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2908     if (!SUCCEEDED(res))
2909     {
2910         msi_free(tl_struct->path);
2911         tl_struct->path = NULL;
2912
2913         return TRUE;
2914     }
2915
2916     ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2917     if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2918     {
2919         ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2920         return FALSE;
2921     }
2922
2923     msi_free(tl_struct->path);
2924     tl_struct->path = NULL;
2925
2926     ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2927     ITypeLib_Release(tl_struct->ptLib);
2928
2929     return TRUE;
2930 }
2931
2932 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2933 {
2934     MSIPACKAGE* package = (MSIPACKAGE*)param;
2935     LPCWSTR component;
2936     MSICOMPONENT *comp;
2937     MSIFILE *file;
2938     typelib_struct tl_struct;
2939     HMODULE module;
2940     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2941
2942     component = MSI_RecordGetString(row,3);
2943     comp = get_loaded_component(package,component);
2944     if (!comp)
2945         return ERROR_SUCCESS;
2946
2947     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2948     {
2949         TRACE("Skipping typelib reg due to disabled component\n");
2950
2951         comp->Action = comp->Installed;
2952
2953         return ERROR_SUCCESS;
2954     }
2955
2956     comp->Action = INSTALLSTATE_LOCAL;
2957
2958     file = get_loaded_file( package, comp->KeyPath ); 
2959     if (!file)
2960         return ERROR_SUCCESS;
2961
2962     module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2963     if (module)
2964     {
2965         LPCWSTR guid;
2966         guid = MSI_RecordGetString(row,1);
2967         CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2968         tl_struct.source = strdupW( file->TargetPath );
2969         tl_struct.path = NULL;
2970
2971         EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2972                         (LONG_PTR)&tl_struct);
2973
2974         if (tl_struct.path)
2975         {
2976             LPWSTR help = NULL;
2977             LPCWSTR helpid;
2978             HRESULT res;
2979
2980             helpid = MSI_RecordGetString(row,6);
2981
2982             if (helpid)
2983                 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2984             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2985             msi_free(help);
2986
2987             if (!SUCCEEDED(res))
2988                 ERR("Failed to register type library %s\n",
2989                         debugstr_w(tl_struct.path));
2990             else
2991             {
2992                 ui_actiondata(package,szRegisterTypeLibraries,row);
2993
2994                 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2995             }
2996
2997             ITypeLib_Release(tl_struct.ptLib);
2998             msi_free(tl_struct.path);
2999         }
3000         else
3001             ERR("Failed to load type library %s\n",
3002                     debugstr_w(tl_struct.source));
3003
3004         FreeLibrary(module);
3005         msi_free(tl_struct.source);
3006     }
3007     else
3008         ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3009
3010     return ERROR_SUCCESS;
3011 }
3012
3013 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3014 {
3015     /* 
3016      * OK this is a bit confusing.. I am given a _Component key and I believe
3017      * that the file that is being registered as a type library is the "key file
3018      * of that component" which I interpret to mean "The file in the KeyPath of
3019      * that component".
3020      */
3021     UINT rc;
3022     MSIQUERY * view;
3023     static const WCHAR Query[] =
3024         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3025          '`','T','y','p','e','L','i','b','`',0};
3026
3027     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3028     if (rc != ERROR_SUCCESS)
3029         return ERROR_SUCCESS;
3030
3031     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3032     msiobj_release(&view->hdr);
3033     return rc;
3034 }
3035
3036 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3037 {
3038     MSIPACKAGE *package = (MSIPACKAGE*)param;
3039     LPWSTR target_file, target_folder, filename;
3040     LPCWSTR buffer, extension;
3041     MSICOMPONENT *comp;
3042     static const WCHAR szlnk[]={'.','l','n','k',0};
3043     IShellLinkW *sl = NULL;
3044     IPersistFile *pf = NULL;
3045     HRESULT res;
3046
3047     buffer = MSI_RecordGetString(row,4);
3048     comp = get_loaded_component(package,buffer);
3049     if (!comp)
3050         return ERROR_SUCCESS;
3051
3052     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3053     {
3054         TRACE("Skipping shortcut creation due to disabled component\n");
3055
3056         comp->Action = comp->Installed;
3057
3058         return ERROR_SUCCESS;
3059     }
3060
3061     comp->Action = INSTALLSTATE_LOCAL;
3062
3063     ui_actiondata(package,szCreateShortcuts,row);
3064
3065     res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3066                     &IID_IShellLinkW, (LPVOID *) &sl );
3067
3068     if (FAILED( res ))
3069     {
3070         ERR("CLSID_ShellLink not available\n");
3071         goto err;
3072     }
3073
3074     res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3075     if (FAILED( res ))
3076     {
3077         ERR("QueryInterface(IID_IPersistFile) failed\n");
3078         goto err;
3079     }
3080
3081     buffer = MSI_RecordGetString(row,2);
3082     target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3083
3084     /* may be needed because of a bug somehwere else */
3085     create_full_pathW(target_folder);
3086
3087     filename = msi_dup_record_field( row, 3 );
3088     reduce_to_longfilename(filename);
3089
3090     extension = strchrW(filename,'.');
3091     if (!extension || strcmpiW(extension,szlnk))
3092     {
3093         int len = strlenW(filename);
3094         filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3095         memcpy(filename + len, szlnk, sizeof(szlnk));
3096     }
3097     target_file = build_directory_name(2, target_folder, filename);
3098     msi_free(target_folder);
3099     msi_free(filename);
3100
3101     buffer = MSI_RecordGetString(row,5);
3102     if (strchrW(buffer,'['))
3103     {
3104         LPWSTR deformated;
3105         deformat_string(package,buffer,&deformated);
3106         IShellLinkW_SetPath(sl,deformated);
3107         msi_free(deformated);
3108     }
3109     else
3110     {
3111         FIXME("poorly handled shortcut format, advertised shortcut\n");
3112         IShellLinkW_SetPath(sl,comp->FullKeypath);
3113     }
3114
3115     if (!MSI_RecordIsNull(row,6))
3116     {
3117         LPWSTR deformated;
3118         buffer = MSI_RecordGetString(row,6);
3119         deformat_string(package,buffer,&deformated);
3120         IShellLinkW_SetArguments(sl,deformated);
3121         msi_free(deformated);
3122     }
3123
3124     if (!MSI_RecordIsNull(row,7))
3125     {
3126         buffer = MSI_RecordGetString(row,7);
3127         IShellLinkW_SetDescription(sl,buffer);
3128     }
3129
3130     if (!MSI_RecordIsNull(row,8))
3131         IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3132
3133     if (!MSI_RecordIsNull(row,9))
3134     {
3135         LPWSTR Path;
3136         INT index; 
3137
3138         buffer = MSI_RecordGetString(row,9);
3139
3140         Path = build_icon_path(package,buffer);
3141         index = MSI_RecordGetInteger(row,10);
3142
3143         /* no value means 0 */
3144         if (index == MSI_NULL_INTEGER)
3145             index = 0;
3146
3147         IShellLinkW_SetIconLocation(sl,Path,index);
3148         msi_free(Path);
3149     }
3150
3151     if (!MSI_RecordIsNull(row,11))
3152         IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3153
3154     if (!MSI_RecordIsNull(row,12))
3155     {
3156         LPWSTR Path;
3157         buffer = MSI_RecordGetString(row,12);
3158         Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3159         if (Path)
3160             IShellLinkW_SetWorkingDirectory(sl,Path);
3161         msi_free(Path);
3162     }
3163
3164     TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3165     IPersistFile_Save(pf,target_file,FALSE);
3166
3167     msi_free(target_file);    
3168
3169 err:
3170     if (pf)
3171         IPersistFile_Release( pf );
3172     if (sl)
3173         IShellLinkW_Release( sl );
3174
3175     return ERROR_SUCCESS;
3176 }
3177
3178 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3179 {
3180     UINT rc;
3181     HRESULT res;
3182     MSIQUERY * view;
3183     static const WCHAR Query[] =
3184         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3185          '`','S','h','o','r','t','c','u','t','`',0};
3186
3187     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3188     if (rc != ERROR_SUCCESS)
3189         return ERROR_SUCCESS;
3190
3191     res = CoInitialize( NULL );
3192     if (FAILED (res))
3193     {
3194         ERR("CoInitialize failed\n");
3195         return ERROR_FUNCTION_FAILED;
3196     }
3197
3198     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3199     msiobj_release(&view->hdr);
3200
3201     CoUninitialize();
3202
3203     return rc;
3204 }
3205
3206 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3207 {
3208     MSIPACKAGE* package = (MSIPACKAGE*)param;
3209     HANDLE the_file;
3210     LPWSTR FilePath;
3211     LPCWSTR FileName;
3212     CHAR buffer[1024];
3213     DWORD sz;
3214     UINT rc;
3215     MSIRECORD *uirow;
3216
3217     FileName = MSI_RecordGetString(row,1);
3218     if (!FileName)
3219     {
3220         ERR("Unable to get FileName\n");
3221         return ERROR_SUCCESS;
3222     }
3223
3224     FilePath = build_icon_path(package,FileName);
3225
3226     TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3227
3228     the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3229                         FILE_ATTRIBUTE_NORMAL, NULL);
3230
3231     if (the_file == INVALID_HANDLE_VALUE)
3232     {
3233         ERR("Unable to create file %s\n",debugstr_w(FilePath));
3234         msi_free(FilePath);
3235         return ERROR_SUCCESS;
3236     }
3237
3238     do 
3239     {
3240         DWORD write;
3241         sz = 1024;
3242         rc = MSI_RecordReadStream(row,2,buffer,&sz);
3243         if (rc != ERROR_SUCCESS)
3244         {
3245             ERR("Failed to get stream\n");
3246             CloseHandle(the_file);  
3247             DeleteFileW(FilePath);
3248             break;
3249         }
3250         WriteFile(the_file,buffer,sz,&write,NULL);
3251     } while (sz == 1024);
3252
3253     msi_free(FilePath);
3254
3255     CloseHandle(the_file);
3256
3257     uirow = MSI_CreateRecord(1);
3258     MSI_RecordSetStringW(uirow,1,FileName);
3259     ui_actiondata(package,szPublishProduct,uirow);
3260     msiobj_release( &uirow->hdr );
3261
3262     return ERROR_SUCCESS;
3263 }
3264
3265 static BOOL msi_check_publish(MSIPACKAGE *package)
3266 {
3267     MSIFEATURE *feature;
3268
3269     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3270     {
3271         if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3272             return TRUE;
3273     }
3274
3275     return FALSE;
3276 }
3277
3278 /*
3279  * 99% of the work done here is only done for 
3280  * advertised installs. However this is where the
3281  * Icon table is processed and written out
3282  * so that is what I am going to do here.
3283  */
3284 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3285 {
3286     UINT rc;
3287     MSIQUERY * view;
3288     MSISOURCELISTINFO *info;
3289     MSIMEDIADISK *disk;
3290     static const WCHAR Query[]=
3291         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3292          '`','I','c','o','n','`',0};
3293     /* for registry stuff */
3294     HKEY hkey=0;
3295     HKEY hukey=0;
3296     HKEY hudkey=0, props=0;
3297     static const WCHAR szProductLanguage[] =
3298         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3299     static const WCHAR szARPProductIcon[] =
3300         {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3301     static const WCHAR szProductVersion[] =
3302         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3303     DWORD langid;
3304     LPWSTR buffer;
3305     DWORD size;
3306     MSIHANDLE hDb, hSumInfo;
3307
3308     /* FIXME: also need to publish if the product is in advertise mode */
3309     if (!msi_check_publish(package))
3310         return ERROR_SUCCESS;
3311
3312     /* write out icon files */
3313
3314     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3315     if (rc == ERROR_SUCCESS)
3316     {
3317         MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3318         msiobj_release(&view->hdr);
3319     }
3320
3321     /* ok there is a lot more done here but i need to figure out what */
3322
3323     rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3324     if (rc != ERROR_SUCCESS)
3325         goto end;
3326
3327     rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3328     if (rc != ERROR_SUCCESS)
3329         goto end;
3330
3331     rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3332     if (rc != ERROR_SUCCESS)
3333         goto end;
3334
3335     rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3336     if (rc != ERROR_SUCCESS)
3337         goto end;
3338
3339     buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3340     msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3341     msi_free(buffer);
3342
3343     langid = msi_get_property_int( package, szProductLanguage, 0 );
3344     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3345
3346     buffer = msi_dup_property( package, szARPProductIcon );
3347     if (buffer)
3348     {
3349         LPWSTR path = build_icon_path(package,buffer);
3350         msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3351         msi_free( path );
3352     }
3353     msi_free(buffer);
3354
3355     buffer = msi_dup_property( package, szProductVersion );
3356     if (buffer)
3357     {
3358         DWORD verdword = msi_version_str_to_dword(buffer);
3359         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3360     }
3361     msi_free(buffer);
3362     
3363     /* FIXME: Need to write more keys to the user registry */
3364   
3365     hDb= alloc_msihandle( &package->db->hdr );
3366     if (!hDb) {
3367         rc = ERROR_NOT_ENOUGH_MEMORY;
3368         goto end;
3369     }
3370     rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo); 
3371     MsiCloseHandle(hDb);
3372     if (rc == ERROR_SUCCESS)
3373     {
3374         WCHAR guidbuffer[0x200];
3375         size = 0x200;
3376         rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3377                                         guidbuffer, &size);
3378         if (rc == ERROR_SUCCESS)
3379         {
3380             WCHAR squashed[GUID_SIZE];
3381             /* for now we only care about the first guid */
3382             LPWSTR ptr = strchrW(guidbuffer,';');
3383             if (ptr) *ptr = 0;
3384             squash_guid(guidbuffer,squashed);
3385             msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3386         }
3387         else
3388         {
3389             ERR("Unable to query Revision_Number...\n");
3390             rc = ERROR_SUCCESS;
3391         }
3392         MsiCloseHandle(hSumInfo);
3393     }
3394     else
3395     {
3396         ERR("Unable to open Summary Information\n");
3397         rc = ERROR_SUCCESS;
3398     }
3399
3400     /* publish the SourceList info */
3401     LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3402     {
3403         MsiSourceListSetInfoW(package->ProductCode, NULL,
3404                               info->context, info->options,
3405                               info->property, info->value);
3406     }
3407
3408     LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3409     {
3410         MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3411                                    disk->context, disk->options,
3412                                    disk->disk_id, disk->volume_label, disk->disk_prompt);
3413     }
3414
3415 end:
3416     RegCloseKey(hkey);
3417     RegCloseKey(hukey);
3418     RegCloseKey(hudkey);
3419     RegCloseKey(props);
3420
3421     return rc;
3422 }
3423
3424 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3425 {
3426     MSIPACKAGE *package = (MSIPACKAGE*)param;
3427     LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3428     LPWSTR deformated_section, deformated_key, deformated_value;
3429     LPWSTR folder, fullname = NULL;
3430     MSIRECORD * uirow;
3431     INT action;
3432     MSICOMPONENT *comp;
3433     static const WCHAR szWindowsFolder[] =
3434           {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3435
3436     component = MSI_RecordGetString(row, 8);
3437     comp = get_loaded_component(package,component);
3438
3439     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3440     {
3441         TRACE("Skipping ini file due to disabled component %s\n",
3442                         debugstr_w(component));
3443
3444         comp->Action = comp->Installed;
3445
3446         return ERROR_SUCCESS;
3447     }
3448
3449     comp->Action = INSTALLSTATE_LOCAL;
3450
3451     identifier = MSI_RecordGetString(row,1); 
3452     filename = MSI_RecordGetString(row,2);
3453     dirproperty = MSI_RecordGetString(row,3);
3454     section = MSI_RecordGetString(row,4);
3455     key = MSI_RecordGetString(row,5);
3456     value = MSI_RecordGetString(row,6);
3457     action = MSI_RecordGetInteger(row,7);
3458
3459     deformat_string(package,section,&deformated_section);
3460     deformat_string(package,key,&deformated_key);
3461     deformat_string(package,value,&deformated_value);
3462
3463     if (dirproperty)
3464     {
3465         folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3466         if (!folder)
3467             folder = msi_dup_property( package, dirproperty );
3468     }
3469     else
3470         folder = msi_dup_property( package, szWindowsFolder );
3471
3472     if (!folder)
3473     {
3474         ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3475         goto cleanup;
3476     }
3477
3478     fullname = build_directory_name(2, folder, filename);
3479
3480     if (action == 0)
3481     {
3482         TRACE("Adding value %s to section %s in %s\n",
3483                 debugstr_w(deformated_key), debugstr_w(deformated_section),
3484                 debugstr_w(fullname));
3485         WritePrivateProfileStringW(deformated_section, deformated_key,
3486                                    deformated_value, fullname);
3487     }
3488     else if (action == 1)
3489     {
3490         WCHAR returned[10];
3491         GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3492                                  returned, 10, fullname);
3493         if (returned[0] == 0)
3494         {
3495             TRACE("Adding value %s to section %s in %s\n",
3496                     debugstr_w(deformated_key), debugstr_w(deformated_section),
3497                     debugstr_w(fullname));
3498
3499             WritePrivateProfileStringW(deformated_section, deformated_key,
3500                                        deformated_value, fullname);
3501         }
3502     }
3503     else if (action == 3)
3504         FIXME("Append to existing section not yet implemented\n");
3505
3506     uirow = MSI_CreateRecord(4);
3507     MSI_RecordSetStringW(uirow,1,identifier);
3508     MSI_RecordSetStringW(uirow,2,deformated_section);
3509     MSI_RecordSetStringW(uirow,3,deformated_key);
3510     MSI_RecordSetStringW(uirow,4,deformated_value);
3511     ui_actiondata(package,szWriteIniValues,uirow);
3512     msiobj_release( &uirow->hdr );
3513 cleanup:
3514     msi_free(fullname);
3515     msi_free(folder);
3516     msi_free(deformated_key);
3517     msi_free(deformated_value);
3518     msi_free(deformated_section);
3519     return ERROR_SUCCESS;
3520 }
3521
3522 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3523 {
3524     UINT rc;
3525     MSIQUERY * view;
3526     static const WCHAR ExecSeqQuery[] = 
3527         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3528          '`','I','n','i','F','i','l','e','`',0};
3529
3530     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3531     if (rc != ERROR_SUCCESS)
3532     {
3533         TRACE("no IniFile table\n");
3534         return ERROR_SUCCESS;
3535     }
3536
3537     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3538     msiobj_release(&view->hdr);
3539     return rc;
3540 }
3541
3542 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3543 {
3544     MSIPACKAGE *package = (MSIPACKAGE*)param;
3545     LPCWSTR filename;
3546     LPWSTR FullName;
3547     MSIFILE *file;
3548     DWORD len;
3549     static const WCHAR ExeStr[] =
3550         {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3551     static const WCHAR close[] =  {'\"',0};
3552     STARTUPINFOW si;
3553     PROCESS_INFORMATION info;
3554     BOOL brc;
3555     MSIRECORD *uirow;
3556     LPWSTR uipath, p;
3557
3558     memset(&si,0,sizeof(STARTUPINFOW));
3559
3560     filename = MSI_RecordGetString(row,1);
3561     file = get_loaded_file( package, filename );
3562
3563     if (!file)
3564     {
3565         ERR("Unable to find file id %s\n",debugstr_w(filename));
3566         return ERROR_SUCCESS;
3567     }
3568
3569     len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3570
3571     FullName = msi_alloc(len*sizeof(WCHAR));
3572     strcpyW(FullName,ExeStr);
3573     strcatW( FullName, file->TargetPath );
3574     strcatW(FullName,close);
3575
3576     TRACE("Registering %s\n",debugstr_w(FullName));
3577     brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3578                     &si, &info);
3579
3580     if (brc)
3581         msi_dialog_check_messages(info.hProcess);
3582
3583     msi_free(FullName);
3584
3585     /* the UI chunk */
3586     uirow = MSI_CreateRecord( 2 );
3587     uipath = strdupW( file->TargetPath );
3588     p = strrchrW(uipath,'\\');
3589     if (p)
3590         p[0]=0;
3591     MSI_RecordSetStringW( uirow, 1, &p[1] );
3592     MSI_RecordSetStringW( uirow, 2, uipath);
3593     ui_actiondata( package, szSelfRegModules, uirow);
3594     msiobj_release( &uirow->hdr );
3595     msi_free( uipath );
3596     /* FIXME: call ui_progress? */
3597
3598     return ERROR_SUCCESS;
3599 }
3600
3601 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3602 {
3603     UINT rc;
3604     MSIQUERY * view;
3605     static const WCHAR ExecSeqQuery[] = 
3606         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3607          '`','S','e','l','f','R','e','g','`',0};
3608
3609     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3610     if (rc != ERROR_SUCCESS)
3611     {
3612         TRACE("no SelfReg table\n");
3613         return ERROR_SUCCESS;
3614     }
3615
3616     MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3617     msiobj_release(&view->hdr);
3618
3619     return ERROR_SUCCESS;
3620 }
3621
3622 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3623 {
3624     MSIFEATURE *feature;
3625     UINT rc;
3626     HKEY hkey=0;
3627     HKEY hukey=0;
3628     HKEY userdata=0;
3629
3630     if (!msi_check_publish(package))
3631         return ERROR_SUCCESS;
3632
3633     rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3634     if (rc != ERROR_SUCCESS)
3635         goto end;
3636
3637     rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3638     if (rc != ERROR_SUCCESS)
3639         goto end;
3640
3641     rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3642     if (rc != ERROR_SUCCESS)
3643         goto end;
3644
3645     /* here the guids are base 85 encoded */
3646     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3647     {
3648         ComponentList *cl;
3649         LPWSTR data = NULL;
3650         GUID clsid;
3651         INT size;
3652         BOOL absent = FALSE;
3653         MSIRECORD *uirow;
3654
3655         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3656             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3657             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3658             absent = TRUE;
3659
3660         size = 1;
3661         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3662         {
3663             size += 21;
3664         }
3665         if (feature->Feature_Parent)
3666             size += strlenW( feature->Feature_Parent )+2;
3667
3668         data = msi_alloc(size * sizeof(WCHAR));
3669
3670         data[0] = 0;
3671         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3672         {
3673             MSICOMPONENT* component = cl->component;
3674             WCHAR buf[21];
3675
3676             buf[0] = 0;
3677             if (component->ComponentId)
3678             {
3679                 TRACE("From %s\n",debugstr_w(component->ComponentId));
3680                 CLSIDFromString(component->ComponentId, &clsid);
3681                 encode_base85_guid(&clsid,buf);
3682                 TRACE("to %s\n",debugstr_w(buf));
3683                 strcatW(data,buf);
3684             }
3685         }
3686
3687         if (feature->Feature_Parent)
3688         {
3689             static const WCHAR sep[] = {'\2',0};
3690             strcatW(data,sep);
3691             strcatW(data,feature->Feature_Parent);
3692         }
3693
3694         msi_reg_set_val_str( hkey, feature->Feature, data );
3695         msi_reg_set_val_str( userdata, feature->Feature, data );
3696         msi_free(data);
3697
3698         size = 0;
3699         if (feature->Feature_Parent)
3700             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3701         if (!absent)
3702         {
3703             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3704                        (LPBYTE)feature->Feature_Parent,size);
3705         }
3706         else
3707         {
3708             size += 2*sizeof(WCHAR);
3709             data = msi_alloc(size);
3710             data[0] = 0x6;
3711             data[1] = 0;
3712             if (feature->Feature_Parent)
3713                 strcpyW( &data[1], feature->Feature_Parent );
3714             RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3715                        (LPBYTE)data,size);
3716             msi_free(data);
3717         }
3718
3719         /* the UI chunk */
3720         uirow = MSI_CreateRecord( 1 );
3721         MSI_RecordSetStringW( uirow, 1, feature->Feature );
3722         ui_actiondata( package, szPublishFeatures, uirow);
3723         msiobj_release( &uirow->hdr );
3724         /* FIXME: call ui_progress? */
3725     }
3726
3727 end:
3728     RegCloseKey(hkey);
3729     RegCloseKey(hukey);
3730     return rc;
3731 }
3732
3733 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3734 {
3735     UINT r;
3736     HKEY hkey;
3737
3738     TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3739
3740     r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3741     if (r == ERROR_SUCCESS)
3742     {
3743         RegDeleteValueW(hkey, feature->Feature);
3744         RegCloseKey(hkey);
3745     }
3746
3747     r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3748     if (r == ERROR_SUCCESS)
3749     {
3750         RegDeleteValueW(hkey, feature->Feature);
3751         RegCloseKey(hkey);
3752     }
3753
3754     return ERROR_SUCCESS;
3755 }
3756
3757 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3758 {
3759     MSIFEATURE *feature;
3760
3761     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3762     {
3763         if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3764             return FALSE;
3765     }
3766
3767     return TRUE;
3768 }
3769
3770 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3771 {
3772     MSIFEATURE *feature;
3773
3774     if (!msi_check_unpublish(package))
3775         return ERROR_SUCCESS;
3776
3777     LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3778     {
3779         msi_unpublish_feature(package, feature);
3780     }
3781
3782     return ERROR_SUCCESS;
3783 }
3784
3785 static UINT msi_get_local_package_name( LPWSTR path )
3786 {
3787     static const WCHAR szInstaller[] = {
3788         '\\','I','n','s','t','a','l','l','e','r','\\',0};
3789     static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3790     DWORD time, len, i;
3791     HANDLE handle;
3792
3793     time = GetTickCount();
3794     GetWindowsDirectoryW( path, MAX_PATH );
3795     lstrcatW( path, szInstaller );
3796     CreateDirectoryW( path, NULL );
3797
3798     len = lstrlenW(path);
3799     for (i=0; i<0x10000; i++)
3800     {
3801         snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3802         handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3803                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3804         if (handle != INVALID_HANDLE_VALUE)
3805         {
3806             CloseHandle(handle);
3807             break;
3808         }
3809         if (GetLastError() != ERROR_FILE_EXISTS &&
3810             GetLastError() != ERROR_SHARING_VIOLATION)
3811             return ERROR_FUNCTION_FAILED;
3812     }
3813
3814     return ERROR_SUCCESS;
3815 }
3816
3817 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3818 {
3819     static const WCHAR szOriginalDatabase[] =
3820         {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3821     WCHAR packagefile[MAX_PATH];
3822     LPWSTR msiFilePath;
3823     HKEY props;
3824     UINT r;
3825
3826     r = msi_get_local_package_name( packagefile );
3827     if (r != ERROR_SUCCESS)
3828         return r;
3829
3830     TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3831
3832     msiFilePath = msi_dup_property( package, szOriginalDatabase );
3833     r = CopyFileW( msiFilePath, packagefile, FALSE);
3834
3835     if (!r)
3836     {
3837         ERR("Unable to copy package (%s -> %s) (error %d)\n",
3838             debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3839         msi_free( msiFilePath );
3840         return ERROR_FUNCTION_FAILED;
3841     }
3842     msi_free( msiFilePath );
3843
3844     msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3845
3846     r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3847     if (r != ERROR_SUCCESS)
3848         return r;
3849
3850     msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3851     RegCloseKey(props);
3852     return ERROR_SUCCESS;
3853 }
3854
3855 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3856 {
3857     LPWSTR prop, val, key;
3858     static const LPCSTR propval[] = {
3859         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3860         "ARPCONTACT",             "Contact",
3861         "ARPCOMMENTS",            "Comments",
3862         "ProductName",            "DisplayName",
3863         "ProductVersion",         "DisplayVersion",
3864         "ARPHELPLINK",            "HelpLink",
3865         "ARPHELPTELEPHONE",       "HelpTelephone",
3866         "ARPINSTALLLOCATION",     "InstallLocation",
3867         "SourceDir",              "InstallSource",
3868         "Manufacturer",           "Publisher",
3869         "ARPREADME",              "Readme",
3870         "ARPSIZE",                "Size",
3871         "ARPURLINFOABOUT",        "URLInfoAbout",
3872         "ARPURLUPDATEINFO",       "URLUpdateInfo",
3873         NULL,
3874     };
3875     const LPCSTR *p = propval;
3876
3877     while( *p )
3878     {
3879         prop = strdupAtoW( *p++ );
3880         key = strdupAtoW( *p++ );
3881         val = msi_dup_property( package, prop );
3882         msi_reg_set_val_str( hkey, key, val );
3883         msi_free(val);
3884         msi_free(key);
3885         msi_free(prop);
3886     }
3887     return ERROR_SUCCESS;
3888 }
3889
3890 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3891 {
3892     HKEY hkey=0;
3893     HKEY hudkey=0, props=0;
3894     LPWSTR buffer = NULL;
3895     UINT rc;
3896     DWORD size, langid;
3897     static const WCHAR szWindowsInstaller[] = 
3898         {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3899     static const WCHAR szUpgradeCode[] = 
3900         {'U','p','g','r','a','d','e','C','o','d','e',0};
3901     static const WCHAR modpath_fmt[] = 
3902         {'M','s','i','E','x','e','c','.','e','x','e',' ',
3903          '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3904     static const WCHAR szModifyPath[] = 
3905         {'M','o','d','i','f','y','P','a','t','h',0};
3906     static const WCHAR szUninstallString[] = 
3907         {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3908     static const WCHAR szEstimatedSize[] = 
3909         {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3910     static const WCHAR szProductLanguage[] =
3911         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3912     static const WCHAR szProductVersion[] =
3913         {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3914
3915     SYSTEMTIME systime;
3916     static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3917     LPWSTR upgrade_code;
3918     WCHAR szDate[9];
3919
3920     /* FIXME: also need to publish if the product is in advertise mode */
3921     if (!msi_check_publish(package))
3922         return ERROR_SUCCESS;
3923
3924     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3925     if (rc != ERROR_SUCCESS)
3926         return rc;
3927
3928     /* dump all the info i can grab */
3929     /* FIXME: Flesh out more information */
3930
3931     msi_write_uninstall_property_vals( package, hkey );
3932
3933     msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3934     
3935     msi_make_package_local( package, hkey );
3936
3937     /* do ModifyPath and UninstallString */
3938     size = deformat_string(package,modpath_fmt,&buffer);
3939     RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3940     RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3941     msi_free(buffer);
3942
3943     /* FIXME: Write real Estimated Size when we have it */
3944     msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3945    
3946     GetLocalTime(&systime);
3947     sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3948     msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3949    
3950     langid = msi_get_property_int( package, szProductLanguage, 0 );
3951     msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3952
3953     buffer = msi_dup_property( package, szProductVersion );
3954     if (buffer)
3955     {
3956         DWORD verdword = msi_version_str_to_dword(buffer);
3957
3958         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3959         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3960         msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3961     }
3962     msi_free(buffer);
3963     
3964     /* Handle Upgrade Codes */
3965     upgrade_code = msi_dup_property( package, szUpgradeCode );
3966     if (upgrade_code)
3967     {
3968         HKEY hkey2;
3969         WCHAR squashed[33];
3970         MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3971         squash_guid(package->ProductCode,squashed);
3972         msi_reg_set_val_str( hkey2, squashed, NULL );
3973         RegCloseKey(hkey2);
3974         MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3975         squash_guid(package->ProductCode,squashed);
3976         msi_reg_set_val_str( hkey2, squashed, NULL );
3977         RegCloseKey(hkey2);
3978
3979         msi_free(upgrade_code);
3980     }
3981     
3982     RegCloseKey(hkey);
3983
3984     rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3985     if (rc != ERROR_SUCCESS)
3986         return rc;
3987
3988     RegCloseKey(hudkey);
3989
3990     rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3991     if (rc != ERROR_SUCCESS)
3992         return rc;
3993
3994     msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
3995     RegCloseKey(props);
3996
3997     return ERROR_SUCCESS;
3998 }
3999
4000 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4001 {
4002     return execute_script(package,INSTALL_SCRIPT);
4003 }
4004
4005 static UINT msi_unpublish_product(MSIPACKAGE *package)
4006 {
4007     LPWSTR remove = NULL;
4008     LPWSTR *features = NULL;
4009     BOOL full_uninstall = TRUE;
4010     MSIFEATURE *feature;
4011
4012     static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4013     static const WCHAR szAll[] = {'A','L','L',0};
4014
4015     remove = msi_dup_property(package, szRemove);
4016     if (!remove)
4017         return ERROR_SUCCESS;
4018
4019     features = msi_split_string(remove, ',');
4020     if (!features)
4021     {
4022         msi_free(remove);
4023         ERR("REMOVE feature list is empty!\n");
4024         return ERROR_FUNCTION_FAILED;
4025     }
4026
4027     if (!lstrcmpW(features[0], szAll))
4028         full_uninstall = TRUE;
4029     else
4030     {
4031         LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4032         {
4033             if (feature->Action != INSTALLSTATE_ABSENT)
4034                 full_uninstall = FALSE;
4035         }
4036     }
4037
4038     if (!full_uninstall)
4039         goto done;
4040
4041     MSIREG_DeleteProductKey(package->ProductCode);
4042     MSIREG_DeleteUserProductKey(package->ProductCode);
4043     MSIREG_DeleteUserDataProductKey(package->ProductCode);
4044     MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4045
4046 done:
4047     msi_free(remove);
4048     msi_free(features);
4049     return ERROR_SUCCESS;
4050 }
4051
4052 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4053 {
4054     UINT rc;
4055
4056     rc = msi_unpublish_product(package);
4057     if (rc != ERROR_SUCCESS)
4058         return rc;
4059
4060     /* turn off scheduling */
4061     package->script->CurrentlyScripting= FALSE;
4062
4063     /* first do the same as an InstallExecute */
4064     rc = ACTION_InstallExecute(package);
4065     if (rc != ERROR_SUCCESS)
4066         return rc;
4067
4068     /* then handle Commit Actions */
4069     rc = execute_script(package,COMMIT_SCRIPT);
4070
4071     return rc;
4072 }
4073
4074 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4075 {
4076     static const WCHAR RunOnce[] = {
4077     'S','o','f','t','w','a','r','e','\\',
4078     'M','i','c','r','o','s','o','f','t','\\',
4079     'W','i','n','d','o','w','s','\\',
4080     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4081     'R','u','n','O','n','c','e',0};
4082     static const WCHAR InstallRunOnce[] = {
4083     'S','o','f','t','w','a','r','e','\\',
4084     'M','i','c','r','o','s','o','f','t','\\',
4085     'W','i','n','d','o','w','s','\\',
4086     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4087     'I','n','s','t','a','l','l','e','r','\\',
4088     'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4089
4090     static const WCHAR msiexec_fmt[] = {
4091     '%','s',
4092     '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4093     '\"','%','s','\"',0};
4094     static const WCHAR install_fmt[] = {
4095     '/','I',' ','\"','%','s','\"',' ',
4096     'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4097     'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4098     WCHAR buffer[256], sysdir[MAX_PATH];
4099     HKEY hkey;
4100     WCHAR squished_pc[100];
4101
4102     squash_guid(package->ProductCode,squished_pc);
4103
4104     GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4105     RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4106     snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4107      squished_pc);
4108
4109     msi_reg_set_val_str( hkey, squished_pc, buffer );
4110     RegCloseKey(hkey);
4111
4112     TRACE("Reboot command %s\n",debugstr_w(buffer));
4113
4114     RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4115     sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4116
4117     msi_reg_set_val_str( hkey, squished_pc, buffer );
4118     RegCloseKey(hkey);
4119
4120     return ERROR_INSTALL_SUSPEND;
4121 }
4122
4123 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
4124 {
4125     LPWSTR p, source;
4126     DWORD len;
4127
4128     p = strrchrW( package->PackagePath, '\\' );
4129     if (!p)
4130         return ERROR_SUCCESS;
4131
4132     len = p - package->PackagePath + 2;
4133     source = msi_alloc( len * sizeof(WCHAR) );
4134     lstrcpynW( source, package->PackagePath, len );
4135
4136     MSI_SetPropertyW( package, cszSourceDir, source );
4137     MSI_SetPropertyW( package, cszSOURCEDIR, source );
4138
4139     msi_free( source );
4140
4141     return ERROR_SUCCESS;
4142 }
4143
4144 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4145 {
4146     DWORD attrib;
4147     UINT rc;
4148
4149     /*
4150      * We are currently doing what should be done here in the top level Install
4151      * however for Administrative and uninstalls this step will be needed
4152      */
4153     if (!package->PackagePath)
4154         return ERROR_SUCCESS;
4155
4156     msi_set_sourcedir_props(package);
4157
4158     attrib = GetFileAttributesW(package->PackagePath);
4159     if (attrib == INVALID_FILE_ATTRIBUTES)
4160     {
4161         LPWSTR prompt;
4162         LPWSTR msg;
4163         DWORD size = 0;
4164
4165         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
4166                 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4167                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4168         if (rc == ERROR_MORE_DATA)
4169         {
4170             prompt = msi_alloc(size * sizeof(WCHAR));
4171             MsiSourceListGetInfoW(package->ProductCode, NULL, 
4172                     MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4173                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4174         }
4175         else
4176             prompt = strdupW(package->PackagePath);
4177
4178         msg = generate_error_string(package,1302,1,prompt);
4179         while(attrib == INVALID_FILE_ATTRIBUTES)
4180         {
4181             rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4182             if (rc == IDCANCEL)
4183             {
4184                 rc = ERROR_INSTALL_USEREXIT;
4185                 break;
4186             }
4187             attrib = GetFileAttributesW(package->PackagePath);
4188         }
4189         msi_free(prompt);
4190         rc = ERROR_SUCCESS;
4191     }
4192     else
4193         return ERROR_SUCCESS;
4194
4195     return rc;
4196 }
4197
4198 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4199 {
4200     HKEY hkey=0;
4201     LPWSTR buffer;
4202     LPWSTR productid;
4203     UINT rc,i;
4204
4205     static const WCHAR szPropKeys[][80] = 
4206     {
4207         {'P','r','o','d','u','c','t','I','D',0},
4208         {'U','S','E','R','N','A','M','E',0},
4209         {'C','O','M','P','A','N','Y','N','A','M','E',0},
4210         {0},
4211     };
4212
4213     static const WCHAR szRegKeys[][80] = 
4214     {
4215         {'P','r','o','d','u','c','t','I','D',0},
4216         {'R','e','g','O','w','n','e','r',0},
4217         {'R','e','g','C','o','m','p','a','n','y',0},
4218         {0},
4219     };
4220
4221     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4222     if (!productid)
4223         return ERROR_SUCCESS;
4224
4225     rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4226     if (rc != ERROR_SUCCESS)
4227         goto end;
4228
4229     for( i = 0; szPropKeys[i][0]; i++ )
4230     {
4231         buffer = msi_dup_property( package, szPropKeys[i] );
4232         msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4233         msi_free( buffer );
4234     }
4235
4236 end:
4237     msi_free(productid);
4238     RegCloseKey(hkey);
4239
4240     /* FIXME: call ui_actiondata */
4241
4242     return ERROR_SUCCESS;
4243 }
4244
4245
4246 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4247 {
4248     UINT rc;
4249
4250     package->script->InWhatSequence |= SEQUENCE_EXEC;
4251     rc = ACTION_ProcessExecSequence(package,FALSE);
4252     return rc;
4253 }
4254
4255
4256 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4257 {
4258     MSIPACKAGE *package = (MSIPACKAGE*)param;
4259     LPCWSTR compgroupid=NULL;
4260     LPCWSTR feature=NULL;
4261     LPCWSTR text = NULL;
4262     LPCWSTR qualifier = NULL;
4263     LPCWSTR component = NULL;
4264     LPWSTR advertise = NULL;
4265     LPWSTR output = NULL;
4266     HKEY hkey;
4267     UINT rc = ERROR_SUCCESS;
4268     MSICOMPONENT *comp;
4269     DWORD sz = 0;
4270     MSIRECORD *uirow;
4271
4272     component = MSI_RecordGetString(rec,3);
4273     comp = get_loaded_component(package,component);
4274
4275     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
4276        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4277        !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4278     {
4279         TRACE("Skipping: Component %s not scheduled for install\n",
4280                         debugstr_w(component));
4281
4282         return ERROR_SUCCESS;
4283     }
4284
4285     compgroupid = MSI_RecordGetString(rec,1);
4286     qualifier = MSI_RecordGetString(rec,2);
4287
4288     rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4289     if (rc != ERROR_SUCCESS)
4290         goto end;
4291     
4292     text = MSI_RecordGetString(rec,4);
4293     feature = MSI_RecordGetString(rec,5);
4294   
4295     advertise = create_component_advertise_string(package, comp, feature);
4296
4297     sz = strlenW(advertise);
4298
4299     if (text)
4300         sz += lstrlenW(text);
4301
4302     sz+=3;
4303     sz *= sizeof(WCHAR);
4304            
4305     output = msi_alloc_zero(sz);
4306     strcpyW(output,advertise);
4307     msi_free(advertise);
4308
4309     if (text)
4310         strcatW(output,text);
4311
4312     msi_reg_set_val_multi_str( hkey, qualifier, output );
4313     
4314 end:
4315     RegCloseKey(hkey);
4316     msi_free(output);
4317
4318     /* the UI chunk */
4319     uirow = MSI_CreateRecord( 2 );
4320     MSI_RecordSetStringW( uirow, 1, compgroupid );
4321     MSI_RecordSetStringW( uirow, 2, qualifier);
4322     ui_actiondata( package, szPublishComponents, uirow);
4323     msiobj_release( &uirow->hdr );
4324     /* FIXME: call ui_progress? */
4325
4326     return rc;
4327 }
4328
4329 /*
4330  * At present I am ignorning the advertised components part of this and only
4331  * focusing on the qualified component sets
4332  */
4333 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4334 {
4335     UINT rc;
4336     MSIQUERY * view;
4337     static const WCHAR ExecSeqQuery[] =
4338         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4339          '`','P','u','b','l','i','s','h',
4340          'C','o','m','p','o','n','e','n','t','`',0};
4341     
4342     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4343     if (rc != ERROR_SUCCESS)
4344         return ERROR_SUCCESS;
4345
4346     rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4347     msiobj_release(&view->hdr);
4348
4349     return rc;
4350 }
4351
4352 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4353 {
4354     MSIPACKAGE *package = (MSIPACKAGE*)param;
4355     MSIRECORD *row;
4356     MSIFILE *file;
4357     SC_HANDLE hscm, service = NULL;
4358     LPCWSTR name, disp, comp, depends, pass;
4359     LPCWSTR load_order, serv_name, key;
4360     DWORD serv_type, start_type;
4361     DWORD err_control;
4362
4363     static const WCHAR query[] =
4364         {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4365          '`','C','o','m','p','o','n','e','n','t','`',' ',
4366          'W','H','E','R','E',' ',
4367          '`','C','o','m','p','o','n','e','n','t','`',' ',
4368          '=','\'','%','s','\'',0};
4369
4370     hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4371     if (!hscm)
4372     {
4373         ERR("Failed to open the SC Manager!\n");
4374         goto done;
4375     }
4376
4377     start_type = MSI_RecordGetInteger(rec, 5);
4378     if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4379         goto done;
4380
4381     depends = MSI_RecordGetString(rec, 8);
4382     if (depends && *depends)
4383         FIXME("Dependency list unhandled!\n");
4384
4385     name = MSI_RecordGetString(rec, 2);
4386     disp = MSI_RecordGetString(rec, 3);
4387     serv_type = MSI_RecordGetInteger(rec, 4);
4388     err_control = MSI_RecordGetInteger(rec, 6);
4389     load_order = MSI_RecordGetString(rec, 7);
4390     serv_name = MSI_RecordGetString(rec, 9);
4391     pass = MSI_RecordGetString(rec, 10);
4392     comp = MSI_RecordGetString(rec, 12);
4393
4394     /* fetch the service path */
4395     row = MSI_QueryGetRecord(package->db, query, comp);
4396     if (!row)
4397     {
4398         ERR("Control query failed!\n");
4399         goto done;
4400     }
4401
4402     key = MSI_RecordGetString(row, 6);
4403
4404     file = get_loaded_file(package, key);
4405     msiobj_release(&row->hdr);
4406     if (!file)
4407     {
4408         ERR("Failed to load the service file\n");
4409         goto done;
4410     }
4411
4412     service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4413                              start_type, err_control, file->TargetPath,
4414                              load_order, NULL, NULL, serv_name, pass);
4415     if (!service)
4416     {
4417         if (GetLastError() != ERROR_SERVICE_EXISTS)
4418             ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4419     }
4420
4421 done:
4422     CloseServiceHandle(service);
4423     CloseServiceHandle(hscm);
4424
4425     return ERROR_SUCCESS;
4426 }
4427
4428 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4429 {
4430     UINT rc;
4431     MSIQUERY * view;
4432     static const WCHAR ExecSeqQuery[] =
4433         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4434          'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4435     
4436     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4437     if (rc != ERROR_SUCCESS)
4438         return ERROR_SUCCESS;
4439
4440     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4441     msiobj_release(&view->hdr);
4442
4443     return rc;
4444 }
4445
4446 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4447 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4448 {
4449     LPCWSTR *vector;
4450     LPWSTR p, q;
4451     DWORD sep_len;
4452
4453     static const WCHAR separator[] = {'[','~',']',0};
4454
4455     *numargs = 0;
4456     sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4457
4458     if (!args)
4459         return NULL;
4460
4461     vector = msi_alloc(sizeof(LPWSTR));
4462     if (!vector)
4463         return NULL;
4464
4465     p = args;
4466     do
4467     {
4468         (*numargs)++;
4469         vector[*numargs - 1] = p;
4470
4471         if ((q = strstrW(p, separator)))
4472         {
4473             *q = '\0';
4474
4475             vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4476             if (!vector)
4477                 return NULL;
4478
4479             p = q + sep_len;
4480         }
4481     } while (q);
4482
4483     return vector;
4484 }
4485
4486 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4487 {
4488     MSIPACKAGE *package = (MSIPACKAGE *)param;
4489     MSICOMPONENT *comp;
4490     SC_HANDLE scm, service = NULL;
4491     LPCWSTR name, *vector = NULL;
4492     LPWSTR args;
4493     DWORD event, numargs;
4494     UINT r = ERROR_FUNCTION_FAILED;
4495
4496     comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4497     if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4498         return ERROR_SUCCESS;
4499
4500     name = MSI_RecordGetString(rec, 2);
4501     event = MSI_RecordGetInteger(rec, 3);
4502     args = strdupW(MSI_RecordGetString(rec, 4));
4503
4504     if (!(event & msidbServiceControlEventStart))
4505         return ERROR_SUCCESS;
4506
4507     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4508     if (!scm)
4509     {
4510         ERR("Failed to open the service control manager\n");
4511         goto done;
4512     }
4513
4514     service = OpenServiceW(scm, name, SERVICE_START);
4515     if (!service)
4516     {
4517         ERR("Failed to open service %s\n", debugstr_w(name));
4518         goto done;
4519     }
4520
4521     vector = msi_service_args_to_vector(name, args, &numargs);
4522
4523     if (!StartServiceW(service, numargs, vector))
4524     {
4525         ERR("Failed to start service %s\n", debugstr_w(name));
4526         goto done;
4527     }
4528
4529     r = ERROR_SUCCESS;
4530
4531 done:
4532     CloseServiceHandle(service);
4533     CloseServiceHandle(scm);
4534
4535     msi_free(args);
4536     msi_free(vector);
4537     return r;
4538 }
4539
4540 static UINT ACTION_StartServices( MSIPACKAGE *package )
4541 {
4542     UINT rc;
4543     MSIQUERY *view;
4544
4545     static const WCHAR query[] = {
4546         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4547         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4548
4549     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4550     if (rc != ERROR_SUCCESS)
4551         return ERROR_SUCCESS;
4552
4553     rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4554     msiobj_release(&view->hdr);
4555
4556     return rc;
4557 }
4558
4559 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4560 {
4561     MSIFILE *file;
4562
4563     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4564     {
4565         if (!lstrcmpW(file->File, filename))
4566             return file;
4567     }
4568
4569     return NULL;
4570 }
4571
4572 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4573 {
4574     MSIPACKAGE *package = (MSIPACKAGE*)param;
4575     LPWSTR driver, driver_path, ptr;
4576     WCHAR outpath[MAX_PATH];
4577     MSIFILE *driver_file, *setup_file;
4578     LPCWSTR desc;
4579     DWORD len, usage;
4580     UINT r = ERROR_SUCCESS;
4581
4582     static const WCHAR driver_fmt[] = {
4583         'D','r','i','v','e','r','=','%','s',0};
4584     static const WCHAR setup_fmt[] = {
4585         'S','e','t','u','p','=','%','s',0};
4586     static const WCHAR usage_fmt[] = {
4587         'F','i','l','e','U','s','a','g','e','=','1',0};
4588
4589     desc = MSI_RecordGetString(rec, 3);
4590
4591     driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4592     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4593
4594     if (!driver_file || !setup_file)
4595     {
4596         ERR("ODBC Driver entry not found!\n");
4597         return ERROR_FUNCTION_FAILED;
4598     }
4599
4600     len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4601           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4602           lstrlenW(usage_fmt) + 1;
4603     driver = msi_alloc(len * sizeof(WCHAR));
4604     if (!driver)
4605         return ERROR_OUTOFMEMORY;
4606
4607     ptr = driver;
4608     lstrcpyW(ptr, desc);
4609     ptr += lstrlenW(ptr) + 1;
4610
4611     sprintfW(ptr, driver_fmt, driver_file->FileName);
4612     ptr += lstrlenW(ptr) + 1;
4613
4614     sprintfW(ptr, setup_fmt, setup_file->FileName);
4615     ptr += lstrlenW(ptr) + 1;
4616
4617     lstrcpyW(ptr, usage_fmt);
4618     ptr += lstrlenW(ptr) + 1;
4619     *ptr = '\0';
4620
4621     driver_path = strdupW(driver_file->TargetPath);
4622     ptr = strrchrW(driver_path, '\\');
4623     if (ptr) *ptr = '\0';
4624
4625     if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4626                              NULL, ODBC_INSTALL_COMPLETE, &usage))
4627     {
4628         ERR("Failed to install SQL driver!\n");
4629         r = ERROR_FUNCTION_FAILED;
4630     }
4631
4632     msi_free(driver);
4633     msi_free(driver_path);
4634
4635     return r;
4636 }
4637
4638 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4639 {
4640     MSIPACKAGE *package = (MSIPACKAGE*)param;
4641     LPWSTR translator, translator_path, ptr;
4642     WCHAR outpath[MAX_PATH];
4643     MSIFILE *translator_file, *setup_file;
4644     LPCWSTR desc;
4645     DWORD len, usage;
4646     UINT r = ERROR_SUCCESS;
4647
4648     static const WCHAR translator_fmt[] = {
4649         'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4650     static const WCHAR setup_fmt[] = {
4651         'S','e','t','u','p','=','%','s',0};
4652
4653     desc = MSI_RecordGetString(rec, 3);
4654
4655     translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4656     setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4657
4658     if (!translator_file || !setup_file)
4659     {
4660         ERR("ODBC Translator entry not found!\n");
4661         return ERROR_FUNCTION_FAILED;
4662     }
4663
4664     len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4665           lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4666     translator = msi_alloc(len * sizeof(WCHAR));
4667     if (!translator)
4668         return ERROR_OUTOFMEMORY;
4669
4670     ptr = translator;
4671     lstrcpyW(ptr, desc);
4672     ptr += lstrlenW(ptr) + 1;
4673
4674     sprintfW(ptr, translator_fmt, translator_file->FileName);
4675     ptr += lstrlenW(ptr) + 1;
4676
4677     sprintfW(ptr, setup_fmt, setup_file->FileName);
4678     ptr += lstrlenW(ptr) + 1;
4679     *ptr = '\0';
4680
4681     translator_path = strdupW(translator_file->TargetPath);
4682     ptr = strrchrW(translator_path, '\\');
4683     if (ptr) *ptr = '\0';
4684
4685     if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4686                                  NULL, ODBC_INSTALL_COMPLETE, &usage))
4687     {
4688         ERR("Failed to install SQL translator!\n");
4689         r = ERROR_FUNCTION_FAILED;
4690     }
4691
4692     msi_free(translator);
4693     msi_free(translator_path);
4694
4695     return r;
4696 }
4697
4698 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4699 {
4700     LPWSTR attrs;
4701     LPCWSTR desc, driver;
4702     WORD request = ODBC_ADD_SYS_DSN;
4703     INT registration;
4704     DWORD len;
4705     UINT r = ERROR_SUCCESS;
4706
4707     static const WCHAR attrs_fmt[] = {
4708         'D','S','N','=','%','s',0 };
4709
4710     desc = MSI_RecordGetString(rec, 3);
4711     driver = MSI_RecordGetString(rec, 4);
4712     registration = MSI_RecordGetInteger(rec, 5);
4713
4714     if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4715     else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4716
4717     len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4718     attrs = msi_alloc(len * sizeof(WCHAR));
4719     if (!attrs)
4720         return ERROR_OUTOFMEMORY;
4721
4722     sprintfW(attrs, attrs_fmt, desc);
4723     attrs[len - 1] = '\0';
4724
4725     if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4726     {
4727         ERR("Failed to install SQL data source!\n");
4728         r = ERROR_FUNCTION_FAILED;
4729     }
4730
4731     msi_free(attrs);
4732
4733     return r;
4734 }
4735
4736 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4737 {
4738     UINT rc;
4739     MSIQUERY *view;
4740
4741     static const WCHAR driver_query[] = {
4742         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4743         'O','D','B','C','D','r','i','v','e','r',0 };
4744
4745     static const WCHAR translator_query[] = {
4746         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4747         'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4748
4749     static const WCHAR source_query[] = {
4750         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4751         'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4752
4753     rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4754     if (rc != ERROR_SUCCESS)
4755         return ERROR_SUCCESS;
4756
4757     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4758     msiobj_release(&view->hdr);
4759
4760     rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4761     if (rc != ERROR_SUCCESS)
4762         return ERROR_SUCCESS;
4763
4764     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4765     msiobj_release(&view->hdr);
4766
4767     rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4768     if (rc != ERROR_SUCCESS)
4769         return ERROR_SUCCESS;
4770
4771     rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4772     msiobj_release(&view->hdr);
4773
4774     return rc;
4775 }
4776
4777 #define ENV_ACT_SETALWAYS   0x1
4778 #define ENV_ACT_SETABSENT   0x2
4779 #define ENV_ACT_REMOVE      0x4
4780 #define ENV_ACT_REMOVEMATCH 0x8
4781
4782 #define ENV_MOD_MACHINE     0x20000000
4783 #define ENV_MOD_APPEND      0x40000000
4784 #define ENV_MOD_PREFIX      0x80000000
4785 #define ENV_MOD_MASK        0xC0000000
4786
4787 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4788
4789 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4790 {
4791     LPCWSTR cptr = *name;
4792     LPCWSTR ptr = *value;
4793
4794     static const WCHAR prefix[] = {'[','~',']',0};
4795     static const int prefix_len = 3;
4796
4797     *flags = 0;
4798     while (*cptr)
4799     {
4800         if (*cptr == '=')
4801             *flags |= ENV_ACT_SETALWAYS;
4802         else if (*cptr == '+')
4803             *flags |= ENV_ACT_SETABSENT;
4804         else if (*cptr == '-')
4805             *flags |= ENV_ACT_REMOVE;
4806         else if (*cptr == '!')
4807             *flags |= ENV_ACT_REMOVEMATCH;
4808         else if (*cptr == '*')
4809             *flags |= ENV_MOD_MACHINE;
4810         else
4811             break;
4812
4813         cptr++;
4814         (*name)++;
4815     }
4816
4817     if (!*cptr)
4818     {
4819         ERR("Missing environment variable\n");
4820         return ERROR_FUNCTION_FAILED;
4821     }
4822
4823     if (!strncmpW(ptr, prefix, prefix_len))
4824     {
4825         *flags |= ENV_MOD_APPEND;
4826         *value += lstrlenW(prefix);
4827     }
4828     else if (lstrlenW(*value) >= prefix_len)
4829     {
4830         ptr += lstrlenW(ptr) - prefix_len;
4831         if (!lstrcmpW(ptr, prefix))
4832         {
4833             *flags |= ENV_MOD_PREFIX;
4834             /* the "[~]" will be removed by deformat_string */;
4835         }
4836     }
4837
4838     if (!*flags ||
4839         check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4840         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4841         check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4842         check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4843     {
4844         ERR("Invalid flags: %08x\n", *flags);
4845         return ERROR_FUNCTION_FAILED;
4846     }
4847
4848     return ERROR_SUCCESS;
4849 }
4850
4851 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4852 {
4853     MSIPACKAGE *package = param;
4854     LPCWSTR name, value, comp;
4855     LPWSTR data = NULL, newval = NULL;
4856     LPWSTR deformatted = NULL, ptr;
4857     DWORD flags, type, size;
4858     LONG res;
4859     HKEY env = NULL, root = HKEY_CURRENT_USER;
4860
4861     static const WCHAR environment[] =
4862         {'S','y','s','t','e','m','\\',
4863          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4864          'C','o','n','t','r','o','l','\\',
4865          'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4866          'E','n','v','i','r','o','n','m','e','n','t',0};
4867     static const WCHAR semicolon[] = {';',0};
4868
4869     name = MSI_RecordGetString(rec, 2);
4870     value = MSI_RecordGetString(rec, 3);
4871     comp = MSI_RecordGetString(rec, 4);
4872
4873     res = env_set_flags(&name, &value, &flags);
4874     if (res != ERROR_SUCCESS)
4875        goto done;
4876
4877     deformat_string(package, value, &deformatted);
4878     if (!deformatted)
4879     {
4880         res = ERROR_OUTOFMEMORY;
4881         goto done;
4882     }
4883
4884     value = deformatted;
4885
4886     if (flags & ENV_MOD_MACHINE)
4887         root = HKEY_LOCAL_MACHINE;
4888
4889     res = RegOpenKeyExW(root, environment, 0, KEY_ALL_ACCESS, &env);
4890     if (res != ERROR_SUCCESS)
4891         goto done;
4892
4893     if (flags & ENV_ACT_REMOVE)
4894         FIXME("Not removing environment variable on uninstall!\n");
4895
4896     size = 0;
4897     res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4898     if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4899         (res == ERROR_SUCCESS && type != REG_SZ))
4900         goto done;
4901
4902     if (res != ERROR_FILE_NOT_FOUND)
4903     {
4904         if (flags & ENV_ACT_SETABSENT)
4905         {
4906             res = ERROR_SUCCESS;
4907             goto done;
4908         }
4909
4910         data = msi_alloc(size);
4911         if (!data)
4912         {
4913             RegCloseKey(env);
4914             return ERROR_OUTOFMEMORY;
4915         }
4916
4917         res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4918         if (res != ERROR_SUCCESS)
4919             goto done;
4920
4921         if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4922         {
4923             res = RegDeleteKeyW(env, name);
4924             goto done;
4925         }
4926
4927         size =  (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4928         newval =  msi_alloc(size);
4929         ptr = newval;
4930         if (!newval)
4931         {
4932             res = ERROR_OUTOFMEMORY;
4933             goto done;
4934         }
4935
4936         if (!(flags & ENV_MOD_MASK))
4937             lstrcpyW(newval, value);
4938         else
4939         {
4940             if (flags & ENV_MOD_PREFIX)
4941             {
4942                 lstrcpyW(newval, value);
4943                 lstrcatW(newval, semicolon);
4944                 ptr = newval + lstrlenW(value) + 1;
4945             }
4946
4947             lstrcpyW(ptr, data);
4948
4949             if (flags & ENV_MOD_APPEND)
4950             {
4951                 lstrcatW(newval, semicolon);
4952                 lstrcatW(newval, value);
4953             }
4954         }
4955     }
4956     else
4957     {
4958         size = (lstrlenW(value) + 1) * sizeof(WCHAR);
4959         newval = msi_alloc(size);
4960         if (!newval)
4961         {
4962             res = ERROR_OUTOFMEMORY;
4963             goto done;
4964         }
4965
4966         lstrcpyW(newval, value);
4967     }
4968
4969     TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
4970     res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
4971
4972 done:
4973     if (env) RegCloseKey(env);
4974     msi_free(deformatted);
4975     msi_free(data);
4976     msi_free(newval);
4977     return res;
4978 }
4979
4980 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4981 {
4982     UINT rc;
4983     MSIQUERY * view;
4984     static const WCHAR ExecSeqQuery[] =
4985         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4986          '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4987     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4988     if (rc != ERROR_SUCCESS)
4989         return ERROR_SUCCESS;
4990
4991     rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
4992     msiobj_release(&view->hdr);
4993
4994     return rc;
4995 }
4996
4997 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
4998
4999 typedef struct
5000 {
5001     struct list entry;
5002     LPWSTR sourcename;
5003     LPWSTR destname;
5004     LPWSTR source;
5005     LPWSTR dest;
5006 } FILE_LIST;
5007
5008 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5009 {
5010     BOOL ret;
5011
5012     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5013         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5014     {
5015         WARN("Source or dest is directory, not moving\n");
5016         return FALSE;
5017     }
5018
5019     if (options == msidbMoveFileOptionsMove)
5020     {
5021         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5022         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5023         if (!ret)
5024         {
5025             WARN("MoveFile failed: %d\n", GetLastError());
5026             return FALSE;
5027         }
5028     }
5029     else
5030     {
5031         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5032         ret = CopyFileW(source, dest, FALSE);
5033         if (!ret)
5034         {
5035             WARN("CopyFile failed: %d\n", GetLastError());
5036             return FALSE;
5037         }
5038     }
5039
5040     return TRUE;
5041 }
5042
5043 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5044 {
5045     LPWSTR path, ptr;
5046     DWORD dirlen, pathlen;
5047
5048     ptr = strrchrW(wildcard, '\\');
5049     dirlen = ptr - wildcard + 1;
5050
5051     pathlen = dirlen + lstrlenW(filename) + 1;
5052     path = msi_alloc(pathlen * sizeof(WCHAR));
5053
5054     lstrcpynW(path, wildcard, dirlen + 1);
5055     lstrcatW(path, filename);
5056
5057     return path;
5058 }
5059
5060 static void free_file_entry(FILE_LIST *file)
5061 {
5062     msi_free(file->source);
5063     msi_free(file->dest);
5064     msi_free(file);
5065 }
5066
5067 static void free_list(FILE_LIST *list)
5068 {
5069     while (!list_empty(&list->entry))
5070     {
5071         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5072
5073         list_remove(&file->entry);
5074         free_file_entry(file);
5075     }
5076 }
5077
5078 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5079 {
5080     FILE_LIST *new, *file;
5081     LPWSTR ptr, filename;
5082     DWORD size;
5083
5084     new = msi_alloc_zero(sizeof(FILE_LIST));
5085     if (!new)
5086         return FALSE;
5087
5088     new->source = strdupW(source);
5089     ptr = strrchrW(dest, '\\') + 1;
5090     filename = strrchrW(new->source, '\\') + 1;
5091
5092     new->sourcename = filename;
5093
5094     if (*ptr)
5095         new->destname = ptr;
5096     else
5097         new->destname = new->sourcename;
5098
5099     size = (ptr - dest) + lstrlenW(filename) + 1;
5100     new->dest = msi_alloc(size * sizeof(WCHAR));
5101     if (!new->dest)
5102     {
5103         free_file_entry(new);
5104         return FALSE;
5105     }
5106
5107     lstrcpynW(new->dest, dest, ptr - dest + 1);
5108     lstrcatW(new->dest, filename);
5109
5110     if (list_empty(&files->entry))
5111     {
5112         list_add_head(&files->entry, &new->entry);
5113         return TRUE;
5114     }
5115
5116     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5117     {
5118         if (lstrcmpW(source, file->source) < 0)
5119         {
5120             list_add_before(&file->entry, &new->entry);
5121             return TRUE;
5122         }
5123     }
5124
5125     list_add_after(&file->entry, &new->entry);
5126     return TRUE;
5127 }
5128
5129 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5130 {
5131     WIN32_FIND_DATAW wfd;
5132     HANDLE hfile;
5133     LPWSTR path;
5134     BOOL res;
5135     FILE_LIST files, *file;
5136     DWORD size;
5137
5138     hfile = FindFirstFileW(source, &wfd);
5139     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5140
5141     list_init(&files.entry);
5142
5143     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5144     {
5145         if (is_dot_dir(wfd.cFileName)) continue;
5146
5147         path = wildcard_to_file(source, wfd.cFileName);
5148         if (!path)
5149         {
5150             free_list(&files);
5151             return FALSE;
5152         }
5153
5154         add_wildcard(&files, path, dest);
5155         msi_free(path);
5156     }
5157
5158     /* only the first wildcard match gets renamed to dest */
5159     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5160     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5161     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5162     if (!file->dest)
5163     {
5164         free_list(&files);
5165         return FALSE;
5166     }
5167
5168     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5169
5170     while (!list_empty(&files.entry))
5171     {
5172         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5173
5174         msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5175
5176         list_remove(&file->entry);
5177         free_file_entry(file);
5178     }
5179
5180     return TRUE;
5181 }
5182
5183 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5184 {
5185     MSIPACKAGE *package = param;
5186     MSICOMPONENT *comp;
5187     LPCWSTR sourcename, destname;
5188     LPWSTR sourcedir = NULL, destdir = NULL;
5189     LPWSTR source = NULL, dest = NULL;
5190     int options;
5191     DWORD size;
5192     BOOL ret, wildcards;
5193
5194     static const WCHAR backslash[] = {'\\',0};
5195
5196     comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5197     if (!comp || !comp->Enabled ||
5198         !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5199     {
5200         TRACE("Component not set for install, not moving file\n");
5201         return ERROR_SUCCESS;
5202     }
5203
5204     sourcename = MSI_RecordGetString(rec, 3);
5205     destname = MSI_RecordGetString(rec, 4);
5206     options = MSI_RecordGetInteger(rec, 7);
5207
5208     sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5209     if (!sourcedir)
5210         goto done;
5211
5212     destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5213     if (!destdir)
5214         goto done;
5215
5216     if (!sourcename)
5217     {
5218         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5219             goto done;
5220
5221         source = strdupW(sourcedir);
5222         if (!source)
5223             goto done;
5224     }
5225     else
5226     {
5227         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5228         source = msi_alloc(size * sizeof(WCHAR));
5229         if (!source)
5230             goto done;
5231
5232         lstrcpyW(source, sourcedir);
5233         if (source[lstrlenW(source) - 1] != '\\')
5234             lstrcatW(source, backslash);
5235         lstrcatW(source, sourcename);
5236     }
5237
5238     wildcards = strchrW(source, '*') || strchrW(source, '?');
5239
5240     if (!destname && !wildcards)
5241     {
5242         destname = strdupW(sourcename);
5243         if (!destname)
5244             goto done;
5245     }
5246
5247     size = 0;
5248     if (destname)
5249         size = lstrlenW(destname);
5250
5251     size += lstrlenW(destdir) + 2;
5252     dest = msi_alloc(size * sizeof(WCHAR));
5253     if (!dest)
5254         goto done;
5255
5256     lstrcpyW(dest, destdir);
5257     if (dest[lstrlenW(dest) - 1] != '\\')
5258         lstrcatW(dest, backslash);
5259
5260     if (destname)
5261         lstrcatW(dest, destname);
5262
5263     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5264     {
5265         ret = CreateDirectoryW(destdir, NULL);
5266         if (!ret)
5267         {
5268             WARN("CreateDirectory failed: %d\n", GetLastError());
5269             return ERROR_SUCCESS;
5270         }
5271     }
5272
5273     if (!wildcards)
5274         msi_move_file(source, dest, options);
5275     else
5276         move_files_wildcard(source, dest, options);
5277
5278 done:
5279     msi_free(sourcedir);
5280     msi_free(destdir);
5281     msi_free(source);
5282     msi_free(dest);
5283
5284     return ERROR_SUCCESS;
5285 }
5286
5287 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5288 {
5289     UINT rc;
5290     MSIQUERY *view;
5291
5292     static const WCHAR ExecSeqQuery[] =
5293         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5294          '`','M','o','v','e','F','i','l','e','`',0};
5295
5296     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5297     if (rc != ERROR_SUCCESS)
5298         return ERROR_SUCCESS;
5299
5300     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5301     msiobj_release(&view->hdr);
5302
5303     return rc;
5304 }
5305
5306 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5307                                            LPCSTR action, LPCWSTR table )
5308 {
5309     static const WCHAR query[] = {
5310         'S','E','L','E','C','T',' ','*',' ',
5311         'F','R','O','M',' ','`','%','s','`',0 };
5312     MSIQUERY *view = NULL;
5313     DWORD count = 0;
5314     UINT r;
5315     
5316     r = MSI_OpenQuery( package->db, &view, query, table );
5317     if (r == ERROR_SUCCESS)
5318     {
5319         r = MSI_IterateRecords(view, &count, NULL, package);
5320         msiobj_release(&view->hdr);
5321     }
5322
5323     if (count)
5324         FIXME("%s -> %u ignored %s table values\n",
5325               action, count, debugstr_w(table));
5326
5327     return ERROR_SUCCESS;
5328 }
5329
5330 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5331 {
5332     TRACE("%p\n", package);
5333     return ERROR_SUCCESS;
5334 }
5335
5336 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5337 {
5338     static const WCHAR table[] =
5339          {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5340     return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5341 }
5342
5343 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5344 {
5345     static const WCHAR table[] = { 'P','a','t','c','h',0 };
5346     return msi_unimplemented_action_stub( package, "PatchFiles", table );
5347 }
5348
5349 static UINT ACTION_BindImage( MSIPACKAGE *package )
5350 {
5351     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5352     return msi_unimplemented_action_stub( package, "BindImage", table );
5353 }
5354
5355 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5356 {
5357     static const WCHAR table[] = {
5358         'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5359     return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5360 }
5361
5362 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5363 {
5364     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5365     return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5366 }
5367
5368 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5369 {
5370     static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5371     return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5372 }
5373
5374 static UINT ACTION_StopServices( MSIPACKAGE *package )
5375 {
5376     static const WCHAR table[] = {
5377         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5378     return msi_unimplemented_action_stub( package, "StopServices", table );
5379 }
5380
5381 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5382 {
5383     static const WCHAR table[] = {
5384         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5385     return msi_unimplemented_action_stub( package, "DeleteServices", table );
5386 }
5387 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5388 {
5389         static const WCHAR table[] = {
5390                 'P','r','o','d','u','c','t','I','D',0 };
5391         return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5392 }
5393
5394 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5395 {
5396     static const WCHAR table[] = {
5397         'E','n','v','i','r','o','n','m','e','n','t',0 };
5398     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5399 }
5400
5401 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5402 {
5403     static const WCHAR table[] = {
5404         'M','s','i','A','s','s','e','m','b','l','y',0 };
5405     return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5406 }
5407
5408 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5409 {
5410     static const WCHAR table[] = {
5411         'M','s','i','A','s','s','e','m','b','l','y',0 };
5412     return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5413 }
5414
5415 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5416 {
5417     static const WCHAR table[] = { 'F','o','n','t',0 };
5418     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5419 }
5420
5421 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
5422 {
5423     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5424     return msi_unimplemented_action_stub( package, "CCPSearch", table );
5425 }
5426
5427 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5428 {
5429     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5430     return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5431 }
5432
5433 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5434 {
5435     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5436     return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5437 }
5438
5439 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5440 {
5441     static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5442     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5443 }
5444
5445 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5446 {
5447     static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5448     return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5449 }
5450
5451 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5452 {
5453     static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5454     return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5455 }
5456
5457 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5458 {
5459     static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5460     return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5461 }
5462
5463 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5464 {
5465     static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5466     return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5467 }
5468
5469 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5470 {
5471     static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5472     return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5473 }
5474
5475 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5476 {
5477     static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5478     return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5479 }
5480
5481 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5482 {
5483     static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5484     return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5485 }
5486
5487 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5488 {
5489     static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5490     return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5491 }
5492
5493 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5494 {
5495     static const WCHAR table[] = { 'A','p','p','I','d',0 };
5496     return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5497 }
5498
5499 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5500 {
5501     static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5502     return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5503 }
5504
5505 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5506 {
5507     static const WCHAR table[] = { 'M','I','M','E',0 };
5508     return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5509 }
5510
5511 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5512 {
5513     static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5514     return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5515 }
5516
5517 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5518 {
5519     static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5520     return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5521 }
5522
5523 static const struct _actions StandardActions[] = {
5524     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5525     { szAppSearch, ACTION_AppSearch },
5526     { szBindImage, ACTION_BindImage },
5527     { szCCPSearch, ACTION_CCPSearch },
5528     { szCostFinalize, ACTION_CostFinalize },
5529     { szCostInitialize, ACTION_CostInitialize },
5530     { szCreateFolders, ACTION_CreateFolders },
5531     { szCreateShortcuts, ACTION_CreateShortcuts },
5532     { szDeleteServices, ACTION_DeleteServices },
5533     { szDisableRollback, NULL },
5534     { szDuplicateFiles, ACTION_DuplicateFiles },
5535     { szExecuteAction, ACTION_ExecuteAction },
5536     { szFileCost, ACTION_FileCost },
5537     { szFindRelatedProducts, ACTION_FindRelatedProducts },
5538     { szForceReboot, ACTION_ForceReboot },
5539     { szInstallAdminPackage, NULL },
5540     { szInstallExecute, ACTION_InstallExecute },
5541     { szInstallExecuteAgain, ACTION_InstallExecute },
5542     { szInstallFiles, ACTION_InstallFiles},
5543     { szInstallFinalize, ACTION_InstallFinalize },
5544     { szInstallInitialize, ACTION_InstallInitialize },
5545     { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5546     { szInstallValidate, ACTION_InstallValidate },
5547     { szIsolateComponents, ACTION_IsolateComponents },
5548     { szLaunchConditions, ACTION_LaunchConditions },
5549     { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5550     { szMoveFiles, ACTION_MoveFiles },
5551     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5552     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5553     { szInstallODBC, ACTION_InstallODBC },
5554     { szInstallServices, ACTION_InstallServices },
5555     { szPatchFiles, ACTION_PatchFiles },
5556     { szProcessComponents, ACTION_ProcessComponents },
5557     { szPublishComponents, ACTION_PublishComponents },
5558     { szPublishFeatures, ACTION_PublishFeatures },
5559     { szPublishProduct, ACTION_PublishProduct },
5560     { szRegisterClassInfo, ACTION_RegisterClassInfo },
5561     { szRegisterComPlus, ACTION_RegisterComPlus},
5562     { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5563     { szRegisterFonts, ACTION_RegisterFonts },
5564     { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5565     { szRegisterProduct, ACTION_RegisterProduct },
5566     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5567     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5568     { szRegisterUser, ACTION_RegisterUser },
5569     { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5570     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5571     { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5572     { szRemoveFiles, ACTION_RemoveFiles },
5573     { szRemoveFolders, ACTION_RemoveFolders },
5574     { szRemoveIniValues, ACTION_RemoveIniValues },
5575     { szRemoveODBC, ACTION_RemoveODBC },
5576     { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5577     { szRemoveShortcuts, ACTION_RemoveShortcuts },
5578     { szResolveSource, ACTION_ResolveSource },
5579     { szRMCCPSearch, ACTION_RMCCPSearch },
5580     { szScheduleReboot, NULL },
5581     { szSelfRegModules, ACTION_SelfRegModules },
5582     { szSelfUnregModules, ACTION_SelfUnregModules },
5583     { szSetODBCFolders, NULL },
5584     { szStartServices, ACTION_StartServices },
5585     { szStopServices, ACTION_StopServices },
5586     { szUnpublishComponents, ACTION_UnpublishComponents },
5587     { szUnpublishFeatures, ACTION_UnpublishFeatures },
5588     { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5589     { szUnregisterComPlus, ACTION_UnregisterComPlus },
5590     { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5591     { szUnregisterFonts, ACTION_UnregisterFonts },
5592     { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5593     { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5594     { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5595     { szValidateProductID, ACTION_ValidateProductID },
5596     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5597     { szWriteIniValues, ACTION_WriteIniValues },
5598     { szWriteRegistryValues, ACTION_WriteRegistryValues },
5599     { NULL, NULL },
5600 };