Sync to Wine-20050725:
[reactos.git] / reactos / lib / msi / helpers.c
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * Here are helper functions formally in action.c that are used by a variaty of
23 * actions and functions.
24 */
25
26 #include <stdarg.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winerror.h"
31 #include "wine/debug.h"
32 #include "msipriv.h"
33 #include "winuser.h"
34 #include "wine/unicode.h"
35 #include "action.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(msi);
38
39 static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
40 static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
41
42 const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
43 const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
44 const WCHAR cszbs[]={'\\',0};
45
46 DWORD build_version_dword(LPCWSTR version_string)
47 {
48 SHORT major,minor;
49 WORD build;
50 DWORD rc = 0x00000000;
51 LPCWSTR ptr1;
52
53 ptr1 = version_string;
54
55 if (!ptr1)
56 return rc;
57 else
58 major = atoiW(ptr1);
59
60
61 if(ptr1)
62 ptr1 = strchrW(ptr1,'.');
63 if (ptr1)
64 {
65 ptr1++;
66 minor = atoiW(ptr1);
67 }
68 else
69 minor = 0;
70
71 if (ptr1)
72 ptr1 = strchrW(ptr1,'.');
73
74 if (ptr1)
75 {
76 ptr1++;
77 build = atoiW(ptr1);
78 }
79 else
80 build = 0;
81
82 rc = MAKELONG(build,MAKEWORD(minor,major));
83 TRACE("%s -> 0x%lx\n",debugstr_w(version_string),rc);
84 return rc;
85 }
86
87 UINT build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name,
88 LPWSTR *FilePath)
89 {
90 LPWSTR SystemFolder;
91 LPWSTR dest;
92
93 static const WCHAR szInstaller[] =
94 {'M','i','c','r','o','s','o','f','t','\\',
95 'I','n','s','t','a','l','l','e','r','\\',0};
96 static const WCHAR szFolder[] =
97 {'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
98
99 SystemFolder = load_dynamic_property(package,szFolder,NULL);
100
101 dest = build_directory_name(3, SystemFolder, szInstaller, package->ProductCode);
102
103 create_full_pathW(dest);
104
105 *FilePath = build_directory_name(2, dest, icon_name);
106
107 HeapFree(GetProcessHeap(),0,SystemFolder);
108 HeapFree(GetProcessHeap(),0,dest);
109 return ERROR_SUCCESS;
110 }
111
112 WCHAR *load_dynamic_stringW(MSIRECORD *row, INT index)
113 {
114 UINT rc;
115 DWORD sz;
116 LPWSTR ret;
117
118 sz = 0;
119 if (MSI_RecordIsNull(row,index))
120 return NULL;
121
122 rc = MSI_RecordGetStringW(row,index,NULL,&sz);
123
124 /* having an empty string is different than NULL */
125 if (sz == 0)
126 {
127 ret = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR));
128 ret[0] = 0;
129 return ret;
130 }
131
132 sz ++;
133 ret = HeapAlloc(GetProcessHeap(),0,sz * sizeof (WCHAR));
134 rc = MSI_RecordGetStringW(row,index,ret,&sz);
135 if (rc!=ERROR_SUCCESS)
136 {
137 ERR("Unable to load dynamic string\n");
138 HeapFree(GetProcessHeap(), 0, ret);
139 ret = NULL;
140 }
141 return ret;
142 }
143
144 LPWSTR load_dynamic_property(MSIPACKAGE *package, LPCWSTR prop, UINT* rc)
145 {
146 DWORD sz = 0;
147 LPWSTR str;
148 UINT r;
149
150 r = MSI_GetPropertyW(package, prop, NULL, &sz);
151 if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
152 {
153 if (rc)
154 *rc = r;
155 return NULL;
156 }
157 sz++;
158 str = HeapAlloc(GetProcessHeap(),0,sz*sizeof(WCHAR));
159 r = MSI_GetPropertyW(package, prop, str, &sz);
160 if (r != ERROR_SUCCESS)
161 {
162 HeapFree(GetProcessHeap(),0,str);
163 str = NULL;
164 }
165 if (rc)
166 *rc = r;
167 return str;
168 }
169
170 int get_loaded_component(MSIPACKAGE* package, LPCWSTR Component )
171 {
172 int rc = -1;
173 DWORD i;
174
175 for (i = 0; i < package->loaded_components; i++)
176 {
177 if (strcmpW(Component,package->components[i].Component)==0)
178 {
179 rc = i;
180 break;
181 }
182 }
183 return rc;
184 }
185
186 int get_loaded_feature(MSIPACKAGE* package, LPCWSTR Feature )
187 {
188 int rc = -1;
189 DWORD i;
190
191 for (i = 0; i < package->loaded_features; i++)
192 {
193 if (strcmpW(Feature,package->features[i].Feature)==0)
194 {
195 rc = i;
196 break;
197 }
198 }
199 return rc;
200 }
201
202 int get_loaded_file(MSIPACKAGE* package, LPCWSTR file)
203 {
204 int rc = -1;
205 DWORD i;
206
207 for (i = 0; i < package->loaded_files; i++)
208 {
209 if (strcmpW(file,package->files[i].File)==0)
210 {
211 rc = i;
212 break;
213 }
214 }
215 return rc;
216 }
217
218 int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path)
219 {
220 DWORD i;
221 DWORD index;
222
223 if (!package)
224 return -2;
225
226 for (i=0; i < package->loaded_files; i++)
227 if (strcmpW(package->files[i].File,name)==0)
228 return -1;
229
230 index = package->loaded_files;
231 package->loaded_files++;
232 if (package->loaded_files== 1)
233 package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
234 else
235 package->files = HeapReAlloc(GetProcessHeap(),0,
236 package->files , package->loaded_files * sizeof(MSIFILE));
237
238 memset(&package->files[index],0,sizeof(MSIFILE));
239
240 package->files[index].File = strdupW(name);
241 package->files[index].TargetPath = strdupW(path);
242 package->files[index].Temporary = TRUE;
243
244 TRACE("Tracking tempfile (%s)\n",debugstr_w(package->files[index].File));
245
246 return 0;
247 }
248
249 LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name, BOOL source,
250 BOOL set_prop, MSIFOLDER **folder)
251 {
252 DWORD i;
253 LPWSTR p, path = NULL;
254
255 TRACE("Working to resolve %s\n",debugstr_w(name));
256
257 if (!name)
258 return NULL;
259
260 /* special resolving for Target and Source root dir */
261 if (strcmpW(name,cszTargetDir)==0 || strcmpW(name,cszSourceDir)==0)
262 {
263 if (!source)
264 {
265 LPWSTR check_path;
266 check_path = load_dynamic_property(package,cszTargetDir,NULL);
267 if (!check_path)
268 {
269 check_path = load_dynamic_property(package,cszRootDrive,NULL);
270 if (set_prop)
271 MSI_SetPropertyW(package,cszTargetDir,check_path);
272 }
273
274 /* correct misbuilt target dir */
275 path = build_directory_name(2, check_path, NULL);
276 if (strcmpiW(path,check_path)!=0)
277 MSI_SetPropertyW(package,cszTargetDir,path);
278
279 if (folder)
280 {
281 for (i = 0; i < package->loaded_folders; i++)
282 {
283 if (strcmpW(package->folders[i].Directory,name)==0)
284 break;
285 }
286 *folder = &(package->folders[i]);
287 }
288 return path;
289 }
290 else
291 {
292 path = load_dynamic_property(package,cszSourceDir,NULL);
293 if (!path)
294 {
295 path = load_dynamic_property(package,cszDatabase,NULL);
296 if (path)
297 {
298 p = strrchrW(path,'\\');
299 if (p)
300 *(p+1) = 0;
301 }
302 }
303 if (folder)
304 {
305 for (i = 0; i < package->loaded_folders; i++)
306 {
307 if (strcmpW(package->folders[i].Directory,name)==0)
308 break;
309 }
310 *folder = &(package->folders[i]);
311 }
312 return path;
313 }
314 }
315
316 for (i = 0; i < package->loaded_folders; i++)
317 {
318 if (strcmpW(package->folders[i].Directory,name)==0)
319 break;
320 }
321
322 if (i >= package->loaded_folders)
323 return NULL;
324
325 if (folder)
326 *folder = &(package->folders[i]);
327
328 if (!source && package->folders[i].ResolvedTarget)
329 {
330 path = strdupW(package->folders[i].ResolvedTarget);
331 TRACE(" already resolved to %s\n",debugstr_w(path));
332 return path;
333 }
334 else if (source && package->folders[i].ResolvedSource)
335 {
336 path = strdupW(package->folders[i].ResolvedSource);
337 TRACE(" (source)already resolved to %s\n",debugstr_w(path));
338 return path;
339 }
340 else if (!source && package->folders[i].Property)
341 {
342 path = build_directory_name(2, package->folders[i].Property, NULL);
343
344 TRACE(" internally set to %s\n",debugstr_w(path));
345 if (set_prop)
346 MSI_SetPropertyW(package,name,path);
347 return path;
348 }
349
350 if (package->folders[i].ParentIndex >= 0)
351 {
352 LPWSTR parent = package->folders[package->folders[i].ParentIndex].Directory;
353
354 TRACE(" ! Parent is %s\n", debugstr_w(parent));
355
356 p = resolve_folder(package, parent, source, set_prop, NULL);
357 if (!source)
358 {
359 TRACE(" TargetDefault = %s\n",
360 debugstr_w(package->folders[i].TargetDefault));
361
362 path = build_directory_name(3, p,
363 package->folders[i].TargetDefault, NULL);
364 package->folders[i].ResolvedTarget = strdupW(path);
365 TRACE(" resolved into %s\n",debugstr_w(path));
366 if (set_prop)
367 MSI_SetPropertyW(package,name,path);
368 }
369 else
370 {
371 if (package->folders[i].SourceDefault &&
372 package->folders[i].SourceDefault[0]!='.')
373 path = build_directory_name(3, p, package->folders[i].SourceDefault, NULL);
374 else
375 path = strdupW(p);
376 TRACE(" (source)resolved into %s\n",debugstr_w(path));
377 package->folders[i].ResolvedSource = strdupW(path);
378 }
379 HeapFree(GetProcessHeap(),0,p);
380 }
381 return path;
382 }
383
384 /* wrapper to resist a need for a full rewrite right now */
385 DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data )
386 {
387 if (ptr)
388 {
389 MSIRECORD *rec = MSI_CreateRecord(1);
390 DWORD size = 0;
391
392 MSI_RecordSetStringW(rec,0,ptr);
393 MSI_FormatRecordW(package,rec,NULL,&size);
394 if (size >= 0)
395 {
396 size++;
397 *data = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
398 if (size > 1)
399 MSI_FormatRecordW(package,rec,*data,&size);
400 else
401 *data[0] = 0;
402 msiobj_release( &rec->hdr );
403 return sizeof(WCHAR)*size;
404 }
405 msiobj_release( &rec->hdr );
406 }
407
408 *data = NULL;
409 return 0;
410 }
411
412 UINT schedule_action(MSIPACKAGE *package, UINT script, LPCWSTR action)
413 {
414 UINT count;
415 LPWSTR *newbuf = NULL;
416 if (script >= TOTAL_SCRIPTS)
417 {
418 FIXME("Unknown script requested %i\n",script);
419 return ERROR_FUNCTION_FAILED;
420 }
421 TRACE("Scheduling Action %s in script %i\n",debugstr_w(action), script);
422
423 count = package->script->ActionCount[script];
424 package->script->ActionCount[script]++;
425 if (count != 0)
426 newbuf = HeapReAlloc(GetProcessHeap(),0,
427 package->script->Actions[script],
428 package->script->ActionCount[script]* sizeof(LPWSTR));
429 else
430 newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(LPWSTR));
431
432 newbuf[count] = strdupW(action);
433 package->script->Actions[script] = newbuf;
434
435 return ERROR_SUCCESS;
436 }
437
438 static void remove_tracked_tempfiles(MSIPACKAGE* package)
439 {
440 DWORD i;
441
442 if (!package)
443 return;
444
445 for (i = 0; i < package->loaded_files; i++)
446 {
447 if (package->files[i].Temporary)
448 {
449 TRACE("Cleaning up %s\n",debugstr_w(package->files[i].TargetPath));
450 DeleteFileW(package->files[i].TargetPath);
451 }
452
453 }
454 }
455
456 /* Called when the package is being closed */
457 void ACTION_free_package_structures( MSIPACKAGE* package)
458 {
459 INT i;
460
461 TRACE("Freeing package action data\n");
462
463 remove_tracked_tempfiles(package);
464
465 /* No dynamic buffers in features */
466 if (package->features && package->loaded_features > 0)
467 HeapFree(GetProcessHeap(),0,package->features);
468
469 for (i = 0; i < package->loaded_folders; i++)
470 {
471 HeapFree(GetProcessHeap(),0,package->folders[i].Directory);
472 HeapFree(GetProcessHeap(),0,package->folders[i].TargetDefault);
473 HeapFree(GetProcessHeap(),0,package->folders[i].SourceDefault);
474 HeapFree(GetProcessHeap(),0,package->folders[i].ResolvedTarget);
475 HeapFree(GetProcessHeap(),0,package->folders[i].ResolvedSource);
476 HeapFree(GetProcessHeap(),0,package->folders[i].Property);
477 }
478 if (package->folders && package->loaded_folders > 0)
479 HeapFree(GetProcessHeap(),0,package->folders);
480
481 for (i = 0; i < package->loaded_components; i++)
482 HeapFree(GetProcessHeap(),0,package->components[i].FullKeypath);
483
484 if (package->components && package->loaded_components > 0)
485 HeapFree(GetProcessHeap(),0,package->components);
486
487 for (i = 0; i < package->loaded_files; i++)
488 {
489 HeapFree(GetProcessHeap(),0,package->files[i].File);
490 HeapFree(GetProcessHeap(),0,package->files[i].FileName);
491 HeapFree(GetProcessHeap(),0,package->files[i].ShortName);
492 HeapFree(GetProcessHeap(),0,package->files[i].Version);
493 HeapFree(GetProcessHeap(),0,package->files[i].Language);
494 HeapFree(GetProcessHeap(),0,package->files[i].SourcePath);
495 HeapFree(GetProcessHeap(),0,package->files[i].TargetPath);
496 }
497
498 if (package->files && package->loaded_files > 0)
499 HeapFree(GetProcessHeap(),0,package->files);
500
501 /* clean up extension, progid, class and verb structures */
502 for (i = 0; i < package->loaded_classes; i++)
503 {
504 HeapFree(GetProcessHeap(),0,package->classes[i].Description);
505 HeapFree(GetProcessHeap(),0,package->classes[i].FileTypeMask);
506 HeapFree(GetProcessHeap(),0,package->classes[i].IconPath);
507 HeapFree(GetProcessHeap(),0,package->classes[i].DefInprocHandler);
508 HeapFree(GetProcessHeap(),0,package->classes[i].DefInprocHandler32);
509 HeapFree(GetProcessHeap(),0,package->classes[i].Argument);
510 HeapFree(GetProcessHeap(),0,package->classes[i].ProgIDText);
511 }
512
513 if (package->classes && package->loaded_classes > 0)
514 HeapFree(GetProcessHeap(),0,package->classes);
515
516 for (i = 0; i < package->loaded_extensions; i++)
517 {
518 HeapFree(GetProcessHeap(),0,package->extensions[i].ProgIDText);
519 }
520
521 if (package->extensions && package->loaded_extensions > 0)
522 HeapFree(GetProcessHeap(),0,package->extensions);
523
524 for (i = 0; i < package->loaded_progids; i++)
525 {
526 HeapFree(GetProcessHeap(),0,package->progids[i].ProgID);
527 HeapFree(GetProcessHeap(),0,package->progids[i].Description);
528 HeapFree(GetProcessHeap(),0,package->progids[i].IconPath);
529 }
530
531 if (package->progids && package->loaded_progids > 0)
532 HeapFree(GetProcessHeap(),0,package->progids);
533
534 for (i = 0; i < package->loaded_verbs; i++)
535 {
536 HeapFree(GetProcessHeap(),0,package->verbs[i].Verb);
537 HeapFree(GetProcessHeap(),0,package->verbs[i].Command);
538 HeapFree(GetProcessHeap(),0,package->verbs[i].Argument);
539 }
540
541 if (package->verbs && package->loaded_verbs > 0)
542 HeapFree(GetProcessHeap(),0,package->verbs);
543
544 for (i = 0; i < package->loaded_mimes; i++)
545 HeapFree(GetProcessHeap(),0,package->mimes[i].ContentType);
546
547 if (package->mimes && package->loaded_mimes > 0)
548 HeapFree(GetProcessHeap(),0,package->mimes);
549
550 for (i = 0; i < package->loaded_appids; i++)
551 {
552 HeapFree(GetProcessHeap(),0,package->appids[i].RemoteServerName);
553 HeapFree(GetProcessHeap(),0,package->appids[i].LocalServer);
554 HeapFree(GetProcessHeap(),0,package->appids[i].ServiceParameters);
555 HeapFree(GetProcessHeap(),0,package->appids[i].DllSurrogate);
556 }
557
558 if (package->appids && package->loaded_appids > 0)
559 HeapFree(GetProcessHeap(),0,package->appids);
560
561 if (package->script)
562 {
563 for (i = 0; i < TOTAL_SCRIPTS; i++)
564 {
565 int j;
566 for (j = 0; j < package->script->ActionCount[i]; j++)
567 HeapFree(GetProcessHeap(),0,package->script->Actions[i][j]);
568
569 HeapFree(GetProcessHeap(),0,package->script->Actions[i]);
570 }
571
572 for (i = 0; i < package->script->UniqueActionsCount; i++)
573 HeapFree(GetProcessHeap(),0,package->script->UniqueActions[i]);
574
575 HeapFree(GetProcessHeap(),0,package->script->UniqueActions);
576 HeapFree(GetProcessHeap(),0,package->script);
577 }
578
579 HeapFree(GetProcessHeap(),0,package->PackagePath);
580 HeapFree(GetProcessHeap(),0,package->msiFilePath);
581 HeapFree(GetProcessHeap(),0,package->ProductCode);
582
583 /* cleanup control event subscriptions */
584 ControlEvent_CleanupSubscriptions(package);
585 }
586
587 /*
588 * build_directory_name()
589 *
590 * This function is to save messing round with directory names
591 * It handles adding backslashes between path segments,
592 * and can add \ at the end of the directory name if told to.
593 *
594 * It takes a variable number of arguments.
595 * It always allocates a new string for the result, so make sure
596 * to free the return value when finished with it.
597 *
598 * The first arg is the number of path segments that follow.
599 * The arguments following count are a list of path segments.
600 * A path segment may be NULL.
601 *
602 * Path segments will be added with a \ separating them.
603 * A \ will not be added after the last segment, however if the
604 * last segment is NULL, then the last character will be a \
605 *
606 */
607 LPWSTR build_directory_name(DWORD count, ...)
608 {
609 DWORD sz = 1, i;
610 LPWSTR dir;
611 va_list va;
612
613 va_start(va,count);
614 for(i=0; i<count; i++)
615 {
616 LPCWSTR str = va_arg(va,LPCWSTR);
617 if (str)
618 sz += strlenW(str) + 1;
619 }
620 va_end(va);
621
622 dir = HeapAlloc(GetProcessHeap(), 0, sz*sizeof(WCHAR));
623 dir[0]=0;
624
625 va_start(va,count);
626 for(i=0; i<count; i++)
627 {
628 LPCWSTR str = va_arg(va,LPCWSTR);
629 if (!str)
630 continue;
631 strcatW(dir, str);
632 if( ((i+1)!=count) && dir[strlenW(dir)-1]!='\\')
633 strcatW(dir, cszbs);
634 }
635 return dir;
636 }
637
638 /***********************************************************************
639 * create_full_pathW
640 *
641 * Recursively create all directories in the path.
642 *
643 * shamelessly stolen from setupapi/queue.c
644 */
645 BOOL create_full_pathW(const WCHAR *path)
646 {
647 BOOL ret = TRUE;
648 int len;
649 WCHAR *new_path;
650
651 new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) *
652 sizeof(WCHAR));
653
654 strcpyW(new_path, path);
655
656 while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
657 new_path[len - 1] = 0;
658
659 while(!CreateDirectoryW(new_path, NULL))
660 {
661 WCHAR *slash;
662 DWORD last_error = GetLastError();
663 if(last_error == ERROR_ALREADY_EXISTS)
664 break;
665
666 if(last_error != ERROR_PATH_NOT_FOUND)
667 {
668 ret = FALSE;
669 break;
670 }
671
672 if(!(slash = strrchrW(new_path, '\\')))
673 {
674 ret = FALSE;
675 break;
676 }
677
678 len = slash - new_path;
679 new_path[len] = 0;
680 if(!create_full_pathW(new_path))
681 {
682 ret = FALSE;
683 break;
684 }
685 new_path[len] = '\\';
686 }
687
688 HeapFree(GetProcessHeap(), 0, new_path);
689 return ret;
690 }
691
692 void ui_progress(MSIPACKAGE *package, int a, int b, int c, int d )
693 {
694 MSIRECORD * row;
695
696 row = MSI_CreateRecord(4);
697 MSI_RecordSetInteger(row,1,a);
698 MSI_RecordSetInteger(row,2,b);
699 MSI_RecordSetInteger(row,3,c);
700 MSI_RecordSetInteger(row,4,d);
701 MSI_ProcessMessage(package, INSTALLMESSAGE_PROGRESS, row);
702 msiobj_release(&row->hdr);
703
704 msi_dialog_check_messages(NULL);
705 }
706
707 void ui_actiondata(MSIPACKAGE *package, LPCWSTR action, MSIRECORD * record)
708 {
709 static const WCHAR Query_t[] =
710 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
711 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
712 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',
713 ' ','\'','%','s','\'',0};
714 WCHAR message[1024];
715 MSIRECORD * row = 0;
716 DWORD size;
717 static const WCHAR szActionData[] =
718 {'A','c','t','i','o','n','D','a','t','a',0};
719
720 if (!package->LastAction || strcmpW(package->LastAction,action))
721 {
722 row = MSI_QueryGetRecord(package->db, Query_t, action);
723 if (!row)
724 return;
725
726 if (MSI_RecordIsNull(row,3))
727 {
728 msiobj_release(&row->hdr);
729 return;
730 }
731
732 /* update the cached actionformat */
733 HeapFree(GetProcessHeap(),0,package->ActionFormat);
734 package->ActionFormat = load_dynamic_stringW(row,3);
735
736 HeapFree(GetProcessHeap(),0,package->LastAction);
737 package->LastAction = strdupW(action);
738
739 msiobj_release(&row->hdr);
740 }
741
742 MSI_RecordSetStringW(record,0,package->ActionFormat);
743 size = 1024;
744 MSI_FormatRecordW(package,record,message,&size);
745
746 row = MSI_CreateRecord(1);
747 MSI_RecordSetStringW(row,1,message);
748
749 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
750
751 ControlEvent_FireSubscribedEvent(package,szActionData, row);
752
753 msiobj_release(&row->hdr);
754 }
755
756 BOOL ACTION_VerifyComponentForAction(MSIPACKAGE* package, INT index,
757 INSTALLSTATE check )
758 {
759 if (package->components[index].Installed == check)
760 return FALSE;
761
762 if (package->components[index].ActionRequest == check)
763 return TRUE;
764 else
765 return FALSE;
766 }
767
768 BOOL ACTION_VerifyFeatureForAction(MSIPACKAGE* package, INT index,
769 INSTALLSTATE check )
770 {
771 if (package->features[index].Installed == check)
772 return FALSE;
773
774 if (package->features[index].ActionRequest == check)
775 return TRUE;
776 else
777 return FALSE;
778 }
779
780 void reduce_to_longfilename(WCHAR* filename)
781 {
782 LPWSTR p = strchrW(filename,'|');
783 if (p)
784 memmove(filename, p+1, (strlenW(p+1)+1)*sizeof(WCHAR));
785 }
786
787 void reduce_to_shortfilename(WCHAR* filename)
788 {
789 LPWSTR p = strchrW(filename,'|');
790 if (p)
791 *p = 0;
792 }
793
794 LPWSTR create_component_advertise_string(MSIPACKAGE* package,
795 MSICOMPONENT* component, LPCWSTR feature)
796 {
797 GUID clsid;
798 WCHAR productid_85[21];
799 WCHAR component_85[21];
800 /*
801 * I have a fair bit of confusion as to when a < is used and when a > is
802 * used. I do not think i have it right...
803 *
804 * Ok it appears that the > is used if there is a guid for the compoenent
805 * and the < is used if not.
806 */
807 static WCHAR fmt1[] = {'%','s','%','s','<',0,0};
808 static WCHAR fmt2[] = {'%','s','%','s','>','%','s',0,0};
809 LPWSTR output = NULL;
810 DWORD sz = 0;
811
812 memset(productid_85,0,sizeof(productid_85));
813 memset(component_85,0,sizeof(component_85));
814
815 CLSIDFromString(package->ProductCode, &clsid);
816
817 encode_base85_guid(&clsid,productid_85);
818
819 CLSIDFromString(component->ComponentId, &clsid);
820 encode_base85_guid(&clsid,component_85);
821
822 TRACE("Doing something with this... %s %s %s\n",
823 debugstr_w(productid_85), debugstr_w(feature),
824 debugstr_w(component_85));
825
826 sz = lstrlenW(productid_85) + lstrlenW(feature);
827 if (component)
828 sz += lstrlenW(component_85);
829
830 sz+=3;
831 sz *= sizeof(WCHAR);
832
833 output = HeapAlloc(GetProcessHeap(),0,sz);
834 memset(output,0,sz);
835
836 if (component)
837 sprintfW(output,fmt2,productid_85,feature,component_85);
838 else
839 sprintfW(output,fmt1,productid_85,feature);
840
841 return output;
842 }
843
844 /* update compoennt state based on a feature change */
845 void ACTION_UpdateComponentStates(MSIPACKAGE *package, LPCWSTR szFeature)
846 {
847 int i;
848 INSTALLSTATE newstate;
849 MSIFEATURE *feature;
850
851 i = get_loaded_feature(package,szFeature);
852 if (i < 0)
853 return;
854
855 feature = &package->features[i];
856 newstate = feature->ActionRequest;
857
858 for( i = 0; i < feature->ComponentCount; i++)
859 {
860 MSICOMPONENT* component = &package->components[feature->Components[i]];
861
862 TRACE("MODIFYING(%i): Component %s (Installed %i, Action %i, Request %i)\n",
863 newstate, debugstr_w(component->Component), component->Installed,
864 component->Action, component->ActionRequest);
865
866 if (!component->Enabled)
867 continue;
868 else
869 {
870 if (newstate == INSTALLSTATE_LOCAL)
871 {
872 component->ActionRequest = INSTALLSTATE_LOCAL;
873 component->Action = INSTALLSTATE_LOCAL;
874 }
875 else
876 {
877 int j,k;
878
879 component->ActionRequest = newstate;
880 component->Action = newstate;
881
882 /*if any other feature wants is local we need to set it local*/
883 for (j = 0;
884 j < package->loaded_features &&
885 component->ActionRequest != INSTALLSTATE_LOCAL;
886 j++)
887 {
888 for (k = 0; k < package->features[j].ComponentCount; k++)
889 if ( package->features[j].Components[k] ==
890 feature->Components[i] )
891 {
892 if (package->features[j].ActionRequest ==
893 INSTALLSTATE_LOCAL)
894 {
895 TRACE("Saved by %s\n", debugstr_w(package->features[j].Feature));
896 component->ActionRequest = INSTALLSTATE_LOCAL;
897 component->Action = INSTALLSTATE_LOCAL;
898 }
899 break;
900 }
901 }
902 }
903 }
904 TRACE("Result (%i): Component %s (Installed %i, Action %i, Request %i)\n",
905 newstate, debugstr_w(component->Component), component->Installed,
906 component->Action, component->ActionRequest);
907 }
908 }
909
910 UINT register_unique_action(MSIPACKAGE *package, LPCWSTR action)
911 {
912 UINT count;
913 LPWSTR *newbuf = NULL;
914
915 if (!package || !package->script)
916 return FALSE;
917
918 TRACE("Registering Action %s as having fun\n",debugstr_w(action));
919
920 count = package->script->UniqueActionsCount;
921 package->script->UniqueActionsCount++;
922 if (count != 0)
923 newbuf = HeapReAlloc(GetProcessHeap(),0,
924 package->script->UniqueActions,
925 package->script->UniqueActionsCount* sizeof(LPWSTR));
926 else
927 newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(LPWSTR));
928
929 newbuf[count] = strdupW(action);
930 package->script->UniqueActions = newbuf;
931
932 return ERROR_SUCCESS;
933 }
934
935 BOOL check_unique_action(MSIPACKAGE *package, LPCWSTR action)
936 {
937 INT i;
938
939 if (!package || !package->script)
940 return FALSE;
941
942 for (i = 0; i < package->script->UniqueActionsCount; i++)
943 if (!strcmpW(package->script->UniqueActions[i],action))
944 return TRUE;
945
946 return FALSE;
947 }
948
949 WCHAR* generate_error_string(MSIPACKAGE *package, UINT error, DWORD count, ... )
950 {
951 static const WCHAR query[] = {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ','F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ','%','i',0};
952
953 MSIRECORD *rec;
954 MSIRECORD *row;
955 DWORD size = 0;
956 DWORD i;
957 va_list va;
958 LPCWSTR str;
959 LPWSTR data;
960
961 row = MSI_QueryGetRecord(package->db, query, error);
962 if (!row)
963 return 0;
964
965 rec = MSI_CreateRecord(count+2);
966
967 str = MSI_RecordGetString(row,1);
968 MSI_RecordSetStringW(rec,0,str);
969 msiobj_release( &row->hdr );
970 MSI_RecordSetInteger(rec,1,error);
971
972 va_start(va,count);
973 for (i = 0; i < count; i++)
974 {
975 str = va_arg(va,LPCWSTR);
976 MSI_RecordSetStringW(rec,(i+2),str);
977 }
978 va_end(va);
979
980 MSI_FormatRecordW(package,rec,NULL,&size);
981 if (size >= 0)
982 {
983 size++;
984 data = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
985 if (size > 1)
986 MSI_FormatRecordW(package,rec,data,&size);
987 else
988 data[0] = 0;
989 msiobj_release( &rec->hdr );
990 return data;
991 }
992
993 msiobj_release( &rec->hdr );
994 data = NULL;
995 return data;
996 }