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