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