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