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