Enhance uxtheme to store the themed system metrics in the registry and
[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 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 }