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