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