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