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