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