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