Fix bugs in resolve_folder.
[wine] / dlls / msi / action.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004 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  * Pages I need
23  *
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
25
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
27  */
28
29 #include <stdarg.h>
30 #include <stdio.h>
31
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winerror.h"
35 #include "winreg.h"
36 #include "wine/debug.h"
37 #include "msi.h"
38 #include "msiquery.h"
39 #include "objbase.h"
40 #include "objidl.h"
41 #include "msipriv.h"
42 #include "winnls.h"
43 #include "winuser.h"
44 #include "shlobj.h"
45 #include "wine/unicode.h"
46 #include "ver.h"
47
48 #define CUSTOM_ACTION_TYPE_MASK 0x3F
49
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
51
52 typedef struct tagMSIFEATURE
53 {
54     WCHAR Feature[96];
55     WCHAR Feature_Parent[96];
56     WCHAR Title[0x100];
57     WCHAR Description[0x100];
58     INT Display;
59     INT Level;
60     WCHAR Directory[96];
61     INT Attributes;
62     
63     INSTALLSTATE State;
64     INT ComponentCount;
65     INT Components[1024]; /* yes hardcoded limit.... I am bad */
66     INT Cost;
67 } MSIFEATURE;
68
69 typedef struct tagMSICOMPONENT
70 {
71     WCHAR Component[96];
72     WCHAR ComponentId[96];
73     WCHAR Directory[96];
74     INT Attributes;
75     WCHAR Condition[0x100];
76     WCHAR KeyPath[96];
77
78     INSTALLSTATE State;
79     BOOL FeatureState;
80     BOOL Enabled;
81     INT  Cost;
82 }MSICOMPONENT;
83
84 typedef struct tagMSIFOLDER
85 {
86     WCHAR Directory[96];
87     WCHAR TargetDefault[96];
88     WCHAR SourceDefault[96];
89
90     WCHAR ResolvedTarget[MAX_PATH];
91     WCHAR ResolvedSource[MAX_PATH];
92     WCHAR Property[MAX_PATH];   /* initialy set property */
93     INT   ParentIndex;
94     INT   State;
95         /* 0 = uninitialized */
96         /* 1 = existing */
97         /* 2 = created remove if empty */
98         /* 3 = created persist if empty */
99     INT   Cost;
100     INT   Space;
101 }MSIFOLDER;
102
103 typedef struct tagMSIFILE
104 {
105     WCHAR File[72];
106     INT ComponentIndex;
107     WCHAR FileName[MAX_PATH];
108     INT FileSize;
109     WCHAR Version[72];
110     WCHAR Language[20];
111     INT Attributes;
112     INT Sequence;   
113
114     INT State;
115        /* 0 = uninitialize */
116        /* 1 = not present */
117        /* 2 = present but replace */
118        /* 3 = present do not replace */
119        /* 4 = Installed */
120     WCHAR   SourcePath[MAX_PATH];
121     WCHAR   TargetPath[MAX_PATH];
122     BOOL    Temporary; 
123 }MSIFILE;
124
125 /*
126  * Prototypes
127  */
128 static UINT ACTION_ProcessExecSequence(MSIHANDLE hPackage, BOOL UIran);
129 static UINT ACTION_ProcessUISequence(MSIHANDLE hPackage);
130
131 UINT ACTION_PerformAction(MSIHANDLE hPackage, const WCHAR *action);
132 static UINT ACTION_CostInitialize(MSIHANDLE hPackage);
133 static UINT ACTION_CreateFolders(MSIHANDLE hPackage);
134 static UINT ACTION_CostFinalize(MSIHANDLE hPackage);
135 static UINT ACTION_FileCost(MSIHANDLE hPackage);
136 static UINT ACTION_InstallFiles(MSIHANDLE hPackage);
137 static UINT ACTION_DuplicateFiles(MSIHANDLE hPackage);
138 static UINT ACTION_WriteRegistryValues(MSIHANDLE hPackage);
139 static UINT ACTION_CustomAction(MSIHANDLE hPackage,const WCHAR *action);
140 static UINT ACTION_InstallInitialize(MSIHANDLE hPackage);
141 static UINT ACTION_InstallValidate(MSIHANDLE hPackage);
142
143 static UINT HANDLE_CustomType1(MSIHANDLE hPackage, const LPWSTR source, 
144                                 const LPWSTR target, const INT type);
145 static UINT HANDLE_CustomType2(MSIHANDLE hPackage, const LPWSTR source, 
146                                 const LPWSTR target, const INT type);
147
148 static DWORD deformat_string(MSIHANDLE hPackage, WCHAR* ptr,WCHAR** data);
149 static UINT resolve_folder(MSIHANDLE hPackage, LPCWSTR name, LPWSTR path, 
150                            BOOL source, BOOL set_prop, MSIFOLDER **folder);
151
152 static UINT track_tempfile(MSIHANDLE hPackage, LPCWSTR name, LPCWSTR path);
153  
154 /*
155  * consts and values used
156  */
157 static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
158 static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
159 static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
160 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
161 static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
162 static const WCHAR c_collen[] = {'C',':','\\',0};
163  
164 static const WCHAR cszlsb[]={'[',0};
165 static const WCHAR cszrsb[]={']',0};
166 static const WCHAR cszbs[]={'\\',0};
167
168
169 /******************************************************** 
170  * helper functions to get around current HACKS and such
171  ********************************************************/
172 inline static void reduce_to_longfilename(WCHAR* filename)
173 {
174     if (strchrW(filename,'|'))
175     {
176         WCHAR newname[MAX_PATH];
177         strcpyW(newname,strchrW(filename,'|')+1);
178         strcpyW(filename,newname);
179     }
180 }
181
182 inline static char *strdupWtoA( const WCHAR *str )
183 {
184     char *ret = NULL;
185     if (str)
186     {
187         DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL
188 );
189         if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
190             WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
191     }
192     return ret;
193 }
194
195 inline static int get_loaded_component(MSIPACKAGE* package, LPCWSTR Component )
196 {
197     INT rc = -1;
198     INT i;
199
200     for (i = 0; i < package->loaded_components; i++)
201     {
202         if (strcmpW(Component,package->components[i].Component)==0)
203         {
204             rc = i;
205             break;
206         }
207     }
208     return rc;
209 }
210
211 inline static int get_loaded_feature(MSIPACKAGE* package, LPCWSTR Feature )
212 {
213     INT rc = -1;
214     INT i;
215
216     for (i = 0; i < package->loaded_features; i++)
217     {
218         if (strcmpW(Feature,package->features[i].Feature)==0)
219         {
220             rc = i;
221             break;
222         }
223     }
224     return rc;
225 }
226
227 static UINT track_tempfile(MSIHANDLE hPackage, LPCWSTR name, LPCWSTR path)
228 {
229     MSIPACKAGE *package;
230     int i;
231     int index;
232
233     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
234
235     if (!package)
236         return -2;
237
238     for (i=0; i < package->loaded_files; i++)
239         if (strcmpW(package->files[i].File,name)==0)
240             return -1;
241
242     index = package->loaded_files;
243     package->loaded_files++;
244     if (package->loaded_files== 1)
245         package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
246     else
247         package->files = HeapReAlloc(GetProcessHeap(),0,
248             package->files , package->loaded_files * sizeof(MSIFILE));
249
250     memset(&package->files[index],0,sizeof(MSIFILE));
251
252     strcpyW(package->files[index].File,name);
253     strcpyW(package->files[index].TargetPath,path);
254     package->files[index].Temporary = TRUE;
255
256     TRACE("Tracking tempfile (%s)\n",debugstr_w(package->files[index].File));  
257
258     return 0;
259 }
260
261 void ACTION_remove_tracked_tempfiles(MSIPACKAGE* package)
262 {
263     int i;
264
265     if (!package)
266         return;
267
268     for (i = 0; i < package->loaded_files; i++)
269     {
270         if (package->files[i].Temporary)
271             DeleteFileW(package->files[i].TargetPath);
272
273     }
274 }
275
276 static void progress_message(MSIHANDLE hPackage, int a, int b, int c, int d )
277 {
278     MSIHANDLE row;
279
280     row = MsiCreateRecord(4);
281     MsiRecordSetInteger(row,1,a);
282     MsiRecordSetInteger(row,2,b);
283     MsiRecordSetInteger(row,3,c);
284     MsiRecordSetInteger(row,4,d);
285     MsiProcessMessage(hPackage, INSTALLMESSAGE_PROGRESS, row);
286     MsiCloseHandle(row);
287 }
288
289 /****************************************************
290  * TOP level entry points 
291  *****************************************************/
292
293 UINT ACTION_DoTopLevelINSTALL(MSIHANDLE hPackage, LPCWSTR szPackagePath,
294                               LPCWSTR szCommandLine)
295 {
296     DWORD sz;
297     CHAR buffer[10];
298     UINT rc;
299
300     if (szPackagePath)   
301     {
302         LPWSTR p;
303         WCHAR check[MAX_PATH];
304         WCHAR pth[MAX_PATH];
305         DWORD size;
306  
307         strcpyW(pth,szPackagePath);
308         p = strrchrW(pth,'\\');    
309         if (p)
310         {
311             p++;
312             *p=0;
313         }
314
315         size = MAX_PATH;
316         if (MsiGetPropertyW(hPackage,cszSourceDir,check,&size) 
317             != ERROR_SUCCESS )
318             MsiSetPropertyW(hPackage, cszSourceDir, pth);
319     }
320
321     if (szCommandLine)
322     {
323         LPWSTR ptr,ptr2;
324         ptr = (LPWSTR)szCommandLine;
325         
326         while (*ptr)
327         {
328             WCHAR prop[0x100];
329             WCHAR val[0x100];
330
331             TRACE("Looking at %s\n",debugstr_w(ptr));
332
333             ptr2 = strchrW(ptr,'=');
334             if (ptr2)
335             {
336                 BOOL quote=FALSE;
337                 DWORD len = 0;
338                 strncpyW(prop,ptr,ptr2-ptr);
339                 prop[ptr2-ptr]=0;
340                 ptr2++;
341             
342                 ptr = ptr2; 
343                 while (*ptr && (quote || (!quote && *ptr!=' ')))
344                 {
345                     if (*ptr == '"')
346                         quote = !quote;
347                     ptr++;
348                     len++;
349                 }
350                
351                 if (*ptr2=='"')
352                 {
353                     ptr2++;
354                     len -= 2;
355                 }
356                 strncpyW(val,ptr2,len);
357                 val[len]=0;
358
359                 if (*ptr)
360                     ptr++;
361             }            
362             TRACE("Found commandline property (%s) = (%s)\n", debugstr_w(prop),
363                   debugstr_w(val));
364             MsiSetPropertyW(hPackage,prop,val);
365         }
366     }
367   
368     sz = 10; 
369     if (MsiGetPropertyA(hPackage,"UILevel",buffer,&sz) == ERROR_SUCCESS)
370     {
371         if (atoi(buffer) >= INSTALLUILEVEL_REDUCED)
372         {
373             rc = ACTION_ProcessUISequence(hPackage);
374             if (rc == ERROR_SUCCESS)
375                 rc = ACTION_ProcessExecSequence(hPackage,TRUE);
376         }
377         else
378             rc = ACTION_ProcessExecSequence(hPackage,FALSE);
379     }
380     else
381         rc = ACTION_ProcessExecSequence(hPackage,FALSE);
382
383     return rc;
384 }
385
386
387 static UINT ACTION_ProcessExecSequence(MSIHANDLE hPackage, BOOL UIran)
388 {
389     MSIHANDLE view;
390     UINT rc;
391     static const CHAR *ExecSeqQuery = 
392 "select * from InstallExecuteSequence where Sequence > %i order by Sequence";
393     CHAR Query[1024];
394     MSIHANDLE db;
395     MSIHANDLE row = 0;
396
397     db = MsiGetActiveDatabase(hPackage);
398
399     if (UIran)
400     {
401         INT seq = 0;
402         static const CHAR *IVQuery = 
403 "select Sequence from InstallExecuteSequence where Action = `InstallValidate`" ;
404         
405         MsiDatabaseOpenViewA(db, IVQuery, &view);
406         MsiViewExecute(view, 0);
407         MsiViewFetch(view,&row);
408         seq = MsiRecordGetInteger(row,1);
409         MsiCloseHandle(row);
410         MsiViewClose(view);
411         MsiCloseHandle(view);
412         sprintf(Query,ExecSeqQuery,0);
413     }
414     else
415         sprintf(Query,ExecSeqQuery,0);
416
417     rc = MsiDatabaseOpenViewA(db, Query, &view);
418     MsiCloseHandle(db);
419     
420     if (rc == ERROR_SUCCESS)
421     {
422         rc = MsiViewExecute(view, 0);
423
424         if (rc != ERROR_SUCCESS)
425         {
426             MsiViewClose(view);
427             MsiCloseHandle(view);
428             goto end;
429         }
430        
431         TRACE("Running the actions \n"); 
432
433         while (1)
434         {
435             WCHAR buffer[0x100];
436             DWORD sz = 0x100;
437
438             rc = MsiViewFetch(view,&row);
439             if (rc != ERROR_SUCCESS)
440             {
441                 rc = ERROR_SUCCESS;
442                 break;
443             }
444
445             /* check conditions */
446             if (!MsiRecordIsNull(row,2))
447             {
448                 sz=0x100;
449                 rc = MsiRecordGetStringW(row,2,buffer,&sz);
450                 if (rc != ERROR_SUCCESS)
451                 {
452                     MsiCloseHandle(row);
453                     break;
454                 }
455
456                 /* this is a hack to skip errors in the condition code */
457                 if (MsiEvaluateConditionW(hPackage, buffer) ==
458                     MSICONDITION_FALSE)
459                 {
460                     MsiCloseHandle(row);
461                     continue; 
462                 }
463
464             }
465
466             sz=0x100;
467             rc =  MsiRecordGetStringW(row,1,buffer,&sz);
468             if (rc != ERROR_SUCCESS)
469             {
470                 ERR("Error is %x\n",rc);
471                 MsiCloseHandle(row);
472                 break;
473             }
474
475             rc = ACTION_PerformAction(hPackage,buffer);
476
477             if (rc != ERROR_SUCCESS)
478             {
479                 ERR("Execution halted due to error (%i)\n",rc);
480                 MsiCloseHandle(row);
481                 break;
482             }
483
484             MsiCloseHandle(row);
485         }
486
487         MsiViewClose(view);
488         MsiCloseHandle(view);
489     }
490
491 end:
492     return rc;
493 }
494
495
496 static UINT ACTION_ProcessUISequence(MSIHANDLE hPackage)
497 {
498     MSIHANDLE view;
499     UINT rc;
500     static const CHAR *ExecSeqQuery = 
501 "select * from InstallUISequence where Sequence > 0 order by Sequence";
502     MSIHANDLE db;
503     
504     db = MsiGetActiveDatabase(hPackage);
505     rc = MsiDatabaseOpenViewA(db, ExecSeqQuery, &view);
506     MsiCloseHandle(db);
507     
508     if (rc == ERROR_SUCCESS)
509     {
510         rc = MsiViewExecute(view, 0);
511
512         if (rc != ERROR_SUCCESS)
513         {
514             MsiViewClose(view);
515             MsiCloseHandle(view);
516             goto end;
517         }
518        
519         TRACE("Running the actions \n"); 
520
521         while (1)
522         {
523             WCHAR buffer[0x100];
524             DWORD sz = 0x100;
525             MSIHANDLE row = 0;
526
527             rc = MsiViewFetch(view,&row);
528             if (rc != ERROR_SUCCESS)
529             {
530                 rc = ERROR_SUCCESS;
531                 break;
532             }
533
534             /* check conditions */
535             if (!MsiRecordIsNull(row,2))
536             {
537                 sz=0x100;
538                 rc = MsiRecordGetStringW(row,2,buffer,&sz);
539                 if (rc != ERROR_SUCCESS)
540                 {
541                     MsiCloseHandle(row);
542                     break;
543                 }
544
545                 if (MsiEvaluateConditionW(hPackage, buffer) ==
546                     MSICONDITION_FALSE)
547                 {
548                     MsiCloseHandle(row);
549                     continue; 
550                 }
551
552             }
553
554             sz=0x100;
555             rc =  MsiRecordGetStringW(row,1,buffer,&sz);
556             if (rc != ERROR_SUCCESS)
557             {
558                 ERR("Error is %x\n",rc);
559                 MsiCloseHandle(row);
560                 break;
561             }
562
563             rc = ACTION_PerformAction(hPackage,buffer);
564
565             if (rc != ERROR_SUCCESS)
566             {
567                 ERR("Execution halted due to error (%i)\n",rc);
568                 MsiCloseHandle(row);
569                 break;
570             }
571
572             MsiCloseHandle(row);
573         }
574
575         MsiViewClose(view);
576         MsiCloseHandle(view);
577     }
578
579 end:
580     return rc;
581 }
582
583 /********************************************************
584  * ACTION helper functions and functions that perform the actions
585  *******************************************************/
586
587 /* 
588  * Alot of actions are really important even if they don't do anything
589  * explicit.. Lots of properties are set at the beginning of the installation
590  * CostFinalize does a bunch of work to translated the directories and such
591  * 
592  * But until I get write access to the database that is hard, so I am going to
593  * hack it to see if I can get something to run.
594  */
595 UINT ACTION_PerformAction(MSIHANDLE hPackage, const WCHAR *action)
596 {
597     const static WCHAR szCreateFolders[] = 
598         {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
599     const static WCHAR szCostFinalize[] = 
600         {'C','o','s','t','F','i','n','a','l','i','z','e',0};
601     const static WCHAR szInstallFiles[] = 
602         {'I','n','s','t','a','l','l','F','i','l','e','s',0};
603     const static WCHAR szDuplicateFiles[] = 
604         {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
605     const static WCHAR szWriteRegistryValues[] = 
606 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
607     const static WCHAR szCostInitialize[] = 
608         {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
609     const static WCHAR szFileCost[] = 
610         {'F','i','l','e','C','o','s','t',0};
611     const static WCHAR szInstallInitialize[] = 
612 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
613     const static WCHAR szInstallValidate[] = 
614 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
615
616     TRACE("Performing action (%s)\n",debugstr_w(action));
617     progress_message(hPackage,2,25,0,0);
618
619     /* pre install, setup and configureation block */
620     if (strcmpW(action,szCostInitialize)==0)
621         return ACTION_CostInitialize(hPackage);
622     if (strcmpW(action,szFileCost)==0)
623         return ACTION_FileCost(hPackage);
624     if (strcmpW(action,szCostFinalize)==0)
625         return ACTION_CostFinalize(hPackage);
626     if (strcmpW(action,szInstallValidate)==0)
627         return ACTION_InstallValidate(hPackage);
628
629     /* install block */
630     if (strcmpW(action,szInstallInitialize)==0)
631         return ACTION_InstallInitialize(hPackage);
632     if (strcmpW(action,szCreateFolders)==0)
633         return ACTION_CreateFolders(hPackage);
634     if (strcmpW(action,szInstallFiles)==0)
635         return ACTION_InstallFiles(hPackage);
636     if (strcmpW(action,szDuplicateFiles)==0)
637         return ACTION_DuplicateFiles(hPackage);
638     if (strcmpW(action,szWriteRegistryValues)==0)
639         return ACTION_WriteRegistryValues(hPackage);
640
641     /*
642      Current called during itunes but unimplemented
643
644      AppSearch
645      LaunchConditions
646      FindRelatedProducts
647      CostInitialize
648      MigrateFeatureStates
649      ResolveSource  (sets SourceDir)
650      ValidateProductID (sets ProductID)
651      IsolateComponents (Empty)
652      SetODBCFolders 
653      MigrateFeatureStates
654      RemoveExistingProducts
655      AllocateRegistrySpace
656      ProcessComponents
657      UnpublishComponents
658      UnpublishFeatures
659      StopServices
660      DeleteServices
661      UnregisterComPlus
662      SelfUnregModules (Empty)
663      UnregisterTypeLibraries
664      RemoveODBC
665      UnregisterFonts
666      RemoveRegistryValues
667      UnregisterClassInfo
668      UnregisterExtensionInfo
669      UnregisterProgIdInfo
670      UnregisterMIMEInfo
671      RemoveIniValues
672      RemoveShortcuts
673      RemoveEnviromentStrings
674      RemoveDuplicateFiles
675      RemoveFiles (Empty)
676      MoveFiles (Empty)
677      RemoveRegistryValues (Empty)
678      SelfRegModules (Empty)
679      RemoveFolders
680      PatchFiles
681      BindImage (Empty)
682      CreateShortcuts (would be nice to have soon)
683      RegisterClassInfo
684      RegisterExtensionInfo (Empty)
685      RegisterProgIdInfo (Lots to do)
686      RegisterMIMEInfo (Empty)
687      WriteIniValues (Empty)
688      WriteEnvironmentStrings (Empty)
689      RegisterFonts(Empty)
690      InstallODBC
691      RegisterTypeLibraries
692      SelfRegModules
693      RegisterComPlus
694      RegisterUser
695      RegisterProduct
696      PublishComponents
697      PublishFeatures
698      PublishProduct
699      InstallFinalize
700      .
701      */
702      if (ACTION_CustomAction(hPackage,action) != ERROR_SUCCESS)
703         FIXME("UNHANDLED MSI ACTION %s\n",debugstr_w(action));
704
705     return ERROR_SUCCESS;
706 }
707
708
709 static UINT ACTION_CustomAction(MSIHANDLE hPackage,const WCHAR *action)
710 {
711     UINT rc;
712     MSIHANDLE view;
713     MSIHANDLE row = 0;
714     WCHAR ExecSeqQuery[1024] = 
715     {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','u','s','t','o'
716 ,'m','A','c','t','i','o','n',' ','w','h','e','r','e',' ','`','A','c','t','i'
717 ,'o','n','`',' ','=',' ','`',0};
718     static const WCHAR end[]={'`',0};
719     UINT type;
720     DWORD sz;
721     WCHAR source[0x100];
722     WCHAR target[0x200];
723     WCHAR *deformated=NULL;
724     MSIHANDLE db;
725
726     strcatW(ExecSeqQuery,action);
727     strcatW(ExecSeqQuery,end);
728
729     db = MsiGetActiveDatabase(hPackage);
730     rc = MsiDatabaseOpenViewW(db, ExecSeqQuery, &view);
731     MsiCloseHandle(db);
732
733     if (rc != ERROR_SUCCESS)
734         return rc;
735
736     rc = MsiViewExecute(view, 0);
737     if (rc != ERROR_SUCCESS)
738     {
739         MsiViewClose(view);
740         MsiCloseHandle(view);
741         return rc;
742     }
743
744     rc = MsiViewFetch(view,&row);
745     if (rc != ERROR_SUCCESS)
746     {
747         MsiViewClose(view);
748         MsiCloseHandle(view);
749         return rc;
750     }
751
752     type = MsiRecordGetInteger(row,2);
753
754     sz=0x100;
755     MsiRecordGetStringW(row,3,source,&sz);
756     sz=0x200;
757     MsiRecordGetStringW(row,4,target,&sz);
758
759     TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
760           debugstr_w(source), debugstr_w(target));
761
762     /* we are ignoring ALOT of flags and important synchronization stuff */
763     switch (type & CUSTOM_ACTION_TYPE_MASK)
764     {
765         case 1: /* DLL file stored in a Binary table stream */
766             rc = HANDLE_CustomType1(hPackage,source,target,type);
767             break;
768         case 2: /* Exe file stored in a Binary table strem */
769             rc = HANDLE_CustomType2(hPackage,source,target,type);
770             break;
771         case 35: /* Directory set with formatted text. */
772         case 51: /* Property set with formatted text. */
773             deformat_string(hPackage,target,&deformated);
774             MsiSetPropertyW(hPackage,source,deformated);
775             HeapFree(GetProcessHeap(),0,deformated);
776             break;
777         default:
778             FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
779              type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
780              debugstr_w(target));
781     }
782
783     MsiCloseHandle(row);
784     MsiViewClose(view);
785     MsiCloseHandle(view);
786     return rc;
787 }
788
789 static UINT store_binary_to_temp(MSIHANDLE hPackage, const LPWSTR source, 
790                                 LPWSTR tmp_file)
791 {
792     DWORD sz=MAX_PATH;
793
794     if (MsiGetPropertyW(hPackage, cszTempFolder, tmp_file, &sz) 
795         != ERROR_SUCCESS)
796         GetTempPathW(MAX_PATH,tmp_file);
797
798     strcatW(tmp_file,source);
799
800     if (GetFileAttributesW(tmp_file) != INVALID_FILE_ATTRIBUTES)
801     {
802         TRACE("File already exists\n");
803         return ERROR_SUCCESS;
804     }
805     else
806     {
807         /* write out the file */
808         UINT rc;
809         MSIHANDLE view;
810         MSIHANDLE row = 0;
811         WCHAR Query[1024] =
812         {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','B','i'
813 ,'n','a','r','y',' ','w','h','e','r','e',' ','N','a','m','e','=','`',0};
814         static const WCHAR end[]={'`',0};
815         HANDLE the_file;
816         CHAR buffer[1024];
817         MSIHANDLE db;
818
819         if (track_tempfile(hPackage, source, tmp_file)!=0)
820             FIXME("File Name in temp tracking collision\n");
821
822         the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
823                            FILE_ATTRIBUTE_NORMAL, NULL);
824     
825         if (the_file == INVALID_HANDLE_VALUE)
826             return ERROR_FUNCTION_FAILED;
827
828         strcatW(Query,source);
829         strcatW(Query,end);
830
831         db = MsiGetActiveDatabase(hPackage);
832         rc = MsiDatabaseOpenViewW(db, Query, &view);
833         MsiCloseHandle(db);
834
835         if (rc != ERROR_SUCCESS)
836             return rc;
837
838         rc = MsiViewExecute(view, 0);
839         if (rc != ERROR_SUCCESS)
840         {
841             MsiViewClose(view);
842             MsiCloseHandle(view);
843             return rc;
844         }
845
846         rc = MsiViewFetch(view,&row);
847         if (rc != ERROR_SUCCESS)
848         {
849             MsiViewClose(view);
850             MsiCloseHandle(view);
851             return rc;
852         }
853
854         do 
855         {
856             DWORD write;
857             sz = 1024;
858             rc = MsiRecordReadStream(row,2,buffer,&sz);
859             if (rc != ERROR_SUCCESS)
860             {
861                 ERR("Failed to get stream\n");
862                 CloseHandle(the_file);  
863                 DeleteFileW(tmp_file);
864                 break;
865             }
866             WriteFile(the_file,buffer,sz,&write,NULL);
867         } while (sz == 1024);
868
869         CloseHandle(the_file);
870
871         MsiCloseHandle(row);
872         MsiViewClose(view);
873         MsiCloseHandle(view);
874     }
875
876     return ERROR_SUCCESS;
877 }
878
879
880 typedef UINT CustomEntry(MSIHANDLE);
881
882 static UINT HANDLE_CustomType1(MSIHANDLE hPackage, const LPWSTR source, 
883                                 const LPWSTR target, const INT type)
884 {
885     WCHAR tmp_file[MAX_PATH];
886     CustomEntry *fn;
887     HANDLE DLL;
888     LPSTR proc;
889
890     store_binary_to_temp(hPackage, source, tmp_file);
891
892     TRACE("Calling function %s from %s\n",debugstr_w(target),
893           debugstr_w(tmp_file));
894
895     if (type & 0xc0)
896     {
897         FIXME("Asynchronous execution.. UNHANDLED\n");
898         return ERROR_SUCCESS;
899     }
900
901     if (!strchrW(tmp_file,'.'))
902     {
903         static const WCHAR dot[]={'.',0};
904         strcatW(tmp_file,dot);
905     } 
906  
907     DLL = LoadLibraryW(tmp_file);
908     if (DLL)
909     {
910         proc = strdupWtoA( target );
911         fn = (CustomEntry*)GetProcAddress(DLL,proc);
912         if (fn)
913         {
914             TRACE("Calling function\n");
915             fn(hPackage);
916         }
917         else
918             ERR("Cannot load functon\n");
919
920         HeapFree(GetProcessHeap(),0,proc);
921         FreeLibrary(DLL);
922     }
923     else
924         ERR("Unable to load library\n");
925
926     return ERROR_SUCCESS;
927 }
928
929 static UINT HANDLE_CustomType2(MSIHANDLE hPackage, const LPWSTR source, 
930                                 const LPWSTR target, const INT type)
931 {
932     WCHAR tmp_file[MAX_PATH*2];
933     STARTUPINFOW si;
934     PROCESS_INFORMATION info;
935     BOOL rc;
936     WCHAR *deformated;
937     static const WCHAR spc[] = {' ',0};
938
939     memset(&si,0,sizeof(STARTUPINFOW));
940     memset(&info,0,sizeof(PROCESS_INFORMATION));
941
942     store_binary_to_temp(hPackage, source, tmp_file);
943
944     strcatW(tmp_file,spc);
945     deformat_string(hPackage,target,&deformated);
946     strcatW(tmp_file,deformated);
947
948     HeapFree(GetProcessHeap(),0,deformated);
949
950     TRACE("executing exe %s \n",debugstr_w(tmp_file));
951
952     rc = CreateProcessW(NULL, tmp_file, NULL, NULL, FALSE, 0, NULL,
953                   c_collen, &si, &info);
954
955     if ( !rc )
956     {
957         ERR("Unable to execute command\n");
958         return ERROR_SUCCESS;
959     }
960
961     if (!(type & 0xc0))
962         WaitForSingleObject(info.hProcess,INFINITE);
963
964     return ERROR_SUCCESS;
965 }
966
967 /***********************************************************************
968  *            create_full_pathW
969  *
970  * Recursively create all directories in the path.
971  *
972  * shamelessly stolen from setupapi/queue.c
973  */
974 static BOOL create_full_pathW(const WCHAR *path)
975 {
976     BOOL ret = TRUE;
977     int len;
978     WCHAR *new_path;
979
980     new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) *
981 sizeof(WCHAR));
982     strcpyW(new_path, path);
983
984     while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
985     new_path[len - 1] = 0;
986
987     while(!CreateDirectoryW(new_path, NULL))
988     {
989     WCHAR *slash;
990     DWORD last_error = GetLastError();
991     if(last_error == ERROR_ALREADY_EXISTS)
992         break;
993
994     if(last_error != ERROR_PATH_NOT_FOUND)
995     {
996         ret = FALSE;
997         break;
998     }
999
1000     if(!(slash = strrchrW(new_path, '\\')))
1001     {
1002         ret = FALSE;
1003         break;
1004     }
1005
1006     len = slash - new_path;
1007     new_path[len] = 0;
1008     if(!create_full_pathW(new_path))
1009     {
1010         ret = FALSE;
1011         break;
1012     }
1013     new_path[len] = '\\';
1014     }
1015
1016     HeapFree(GetProcessHeap(), 0, new_path);
1017     return ret;
1018 }
1019
1020 /*
1021  * Also we cannot enable/disable components either, so for now I am just going 
1022  * to do all the directories for all the components.
1023  */
1024 static UINT ACTION_CreateFolders(MSIHANDLE hPackage)
1025 {
1026     static const CHAR *ExecSeqQuery = "select Directory_ from CreateFolder";
1027     UINT rc;
1028     MSIHANDLE view;
1029     MSIHANDLE db;
1030     MSIFOLDER *folder;
1031
1032     db = MsiGetActiveDatabase(hPackage);
1033     rc = MsiDatabaseOpenViewA(db, ExecSeqQuery, &view);
1034     MsiCloseHandle(db);
1035
1036     if (rc != ERROR_SUCCESS)
1037         return rc;
1038
1039     rc = MsiViewExecute(view, 0);
1040     if (rc != ERROR_SUCCESS)
1041     {
1042         MsiViewClose(view);
1043         MsiCloseHandle(view);
1044         return rc;
1045     }
1046     
1047     while (1)
1048     {
1049         WCHAR dir[0x100];
1050         WCHAR full_path[MAX_PATH];
1051         DWORD sz;
1052         MSIHANDLE row = 0;
1053
1054         rc = MsiViewFetch(view,&row);
1055         if (rc != ERROR_SUCCESS)
1056         {
1057             rc = ERROR_SUCCESS;
1058             break;
1059         }
1060
1061         sz=0x100;
1062         rc = MsiRecordGetStringW(row,1,dir,&sz);
1063
1064         if (rc!= ERROR_SUCCESS)
1065         {
1066             ERR("Unable to get folder id \n");
1067             MsiCloseHandle(row);
1068             continue;
1069         }
1070
1071         sz = MAX_PATH;
1072         rc = resolve_folder(hPackage,dir,full_path,FALSE,FALSE,&folder);
1073
1074         if (rc != ERROR_SUCCESS)
1075         {
1076             ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1077             MsiCloseHandle(row);
1078             continue;
1079         }
1080
1081         TRACE("Folder is %s\n",debugstr_w(full_path));
1082
1083         if (folder->State == 0)
1084             create_full_pathW(full_path);
1085
1086         folder->State = 3;
1087
1088         MsiCloseHandle(row);
1089     }
1090     MsiViewClose(view);
1091     MsiCloseHandle(view);
1092    
1093     return rc;
1094 }
1095
1096 static int load_component(MSIPACKAGE* package, MSIHANDLE row)
1097 {
1098     int index = package->loaded_components;
1099     DWORD sz;
1100
1101     /* fill in the data */
1102
1103     package->loaded_components++;
1104     if (package->loaded_components == 1)
1105         package->components = HeapAlloc(GetProcessHeap(),0,
1106                                         sizeof(MSICOMPONENT));
1107     else
1108         package->components = HeapReAlloc(GetProcessHeap(),0,
1109             package->components, package->loaded_components * 
1110             sizeof(MSICOMPONENT));
1111
1112     memset(&package->components[index],0,sizeof(MSICOMPONENT));
1113
1114     sz = 96;       
1115     MsiRecordGetStringW(row,1,package->components[index].Component,&sz);
1116
1117     TRACE("Loading Component %s\n",
1118            debugstr_w(package->components[index].Component));
1119
1120     sz = 0x100;
1121     if (!MsiRecordIsNull(row,2))
1122         MsiRecordGetStringW(row,2,package->components[index].ComponentId,&sz);
1123             
1124     sz = 96;       
1125     MsiRecordGetStringW(row,3,package->components[index].Directory,&sz);
1126
1127     package->components[index].Attributes = MsiRecordGetInteger(row,4);
1128
1129     sz = 0x100;       
1130     MsiRecordGetStringW(row,5,package->components[index].Condition,&sz);
1131
1132     sz = 96;       
1133     MsiRecordGetStringW(row,6,package->components[index].KeyPath,&sz);
1134
1135     package->components[index].State = INSTALLSTATE_UNKNOWN;
1136     package->components[index].Enabled = TRUE;
1137     package->components[index].FeatureState= FALSE;
1138
1139     return index;
1140 }
1141
1142 static void load_feature(MSIPACKAGE* package, MSIHANDLE row)
1143 {
1144     int index = package->loaded_features;
1145     DWORD sz;
1146     static const WCHAR Query1[] = {'S','E','L','E','C','T',' ','C','o','m','p',
1147 'o','n','e','n','t','_',' ','F','R','O','M',' ','F','e','a','t','u','r','e',
1148 'C','o','m','p','o','n','e','n','t','s',' ','W','H','E','R','E',' ','F','e',
1149 'a','t','u','r','e','_','=','\'','%','s','\'',0};
1150     static const WCHAR Query2[] = {'S','E','L','E','C','T',' ','*',' ','F','R',
1151 'O','M',' ','C','o','m','p','o','n','e','n','t',' ','W','H','E','R','E',' ','C',
1152 'o','m','p','o','n','e','n','t','=','\'','%','s','\'',0};
1153     WCHAR Query[1024];
1154     MSIHANDLE view;
1155     MSIHANDLE view2;
1156     MSIHANDLE row2;
1157     MSIHANDLE row3;
1158
1159     /* fill in the data */
1160
1161     package->loaded_features ++;
1162     if (package->loaded_features == 1)
1163         package->features = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFEATURE));
1164     else
1165         package->features = HeapReAlloc(GetProcessHeap(),0,package->features,
1166                                 package->loaded_features * sizeof(MSIFEATURE));
1167
1168     memset(&package->features[index],0,sizeof(MSIFEATURE));
1169     
1170     sz = 96;       
1171     MsiRecordGetStringW(row,1,package->features[index].Feature,&sz);
1172
1173     TRACE("Loading feature %s\n",debugstr_w(package->features[index].Feature));
1174
1175     sz = 96;
1176     if (!MsiRecordIsNull(row,2))
1177         MsiRecordGetStringW(row,2,package->features[index].Feature_Parent,&sz);
1178
1179     sz = 0x100;
1180      if (!MsiRecordIsNull(row,3))
1181         MsiRecordGetStringW(row,3,package->features[index].Title,&sz);
1182
1183      sz = 0x100;
1184      if (!MsiRecordIsNull(row,4))
1185         MsiRecordGetStringW(row,4,package->features[index].Description,&sz);
1186
1187     if (!MsiRecordIsNull(row,5))
1188         package->features[index].Display = MsiRecordGetInteger(row,5);
1189   
1190     package->features[index].Level= MsiRecordGetInteger(row,6);
1191
1192      sz = 96;
1193      if (!MsiRecordIsNull(row,7))
1194         MsiRecordGetStringW(row,7,package->features[index].Directory,&sz);
1195
1196     package->features[index].Attributes= MsiRecordGetInteger(row,8);
1197     package->features[index].State = INSTALLSTATE_UNKNOWN;
1198
1199     /* load feature components */
1200
1201     sprintfW(Query,Query1,package->features[index].Feature);
1202     MsiDatabaseOpenViewW(package->db,Query,&view);
1203     MsiViewExecute(view,0);
1204     while (1)
1205     {
1206         DWORD sz = 0x100;
1207         WCHAR buffer[0x100];
1208         DWORD rc;
1209         INT c_indx;
1210         INT cnt = package->features[index].ComponentCount;
1211
1212         rc = MsiViewFetch(view,&row2);
1213         if (rc != ERROR_SUCCESS)
1214             break;
1215
1216         sz = 0x100;
1217         MsiRecordGetStringW(row2,1,buffer,&sz);
1218
1219         /* check to see if the component is already loaded */
1220         c_indx = get_loaded_component(package,buffer);
1221         if (c_indx != -1)
1222         {
1223             TRACE("Component %s already loaded at %i\n", debugstr_w(buffer),
1224                   c_indx);
1225             package->features[index].Components[cnt] = c_indx;
1226             package->features[index].ComponentCount ++;
1227         }
1228
1229         sprintfW(Query,Query2,buffer);
1230    
1231         MsiDatabaseOpenViewW(package->db,Query,&view2);
1232         MsiViewExecute(view2,0);
1233         while (1)
1234         {
1235             DWORD rc;
1236
1237             rc = MsiViewFetch(view2,&row3);
1238             if (rc != ERROR_SUCCESS)
1239                 break;
1240             c_indx = load_component(package,row3);
1241             MsiCloseHandle(row3);
1242
1243             package->features[index].Components[cnt] = c_indx;
1244             package->features[index].ComponentCount ++;
1245         }
1246         MsiViewClose(view2);
1247         MsiCloseHandle(view2);
1248         MsiCloseHandle(row2);
1249     }
1250     MsiViewClose(view);
1251     MsiCloseHandle(view);
1252 }
1253
1254 /*
1255  * I am not doing any of the costing functionality yet. 
1256  * Mostly looking at doing the Component and Feature loading
1257  *
1258  * The native MSI does ALOT of modification to tables here. Mostly adding alot
1259  * of temporary columns to the Feature and Component tables. 
1260  *
1261  *    note: native msi also tracks the short filename. but i am only going to
1262  *          track the long ones.  Also looking at this directory table
1263  *          it appears that the directory table does not get the parents
1264  *          resolved base on property only based on their entrys in the 
1265  *          directory table.
1266  */
1267 static UINT ACTION_CostInitialize(MSIHANDLE hPackage)
1268 {
1269     MSIHANDLE view;
1270     MSIHANDLE row;
1271     DWORD sz;
1272     MSIPACKAGE *package;
1273
1274     static const CHAR Query_all[] = "SELECT * FROM Feature";
1275
1276     MsiSetPropertyA(hPackage,"CostingComplete","0");
1277     MsiSetPropertyW(hPackage, cszRootDrive , c_collen);
1278
1279     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
1280
1281     sz = 0x100;
1282     MsiDatabaseOpenViewA(package->db,Query_all,&view);
1283     MsiViewExecute(view,0);
1284     while (1)
1285     {
1286         DWORD rc;
1287
1288         rc = MsiViewFetch(view,&row);
1289         if (rc != ERROR_SUCCESS)
1290             break;
1291        
1292         load_feature(package,row); 
1293         MsiCloseHandle(row);
1294     }
1295     MsiViewClose(view);
1296     MsiCloseHandle(view);
1297
1298     return ERROR_SUCCESS;
1299 }
1300
1301 static int load_file(MSIPACKAGE* package, MSIHANDLE row)
1302 {
1303     int index = package->loaded_files;
1304     int i;
1305     WCHAR buffer[0x100];
1306     DWORD sz;
1307
1308     /* fill in the data */
1309
1310     package->loaded_files++;
1311     if (package->loaded_files== 1)
1312         package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
1313     else
1314         package->files = HeapReAlloc(GetProcessHeap(),0,
1315             package->files , package->loaded_files * sizeof(MSIFILE));
1316
1317     memset(&package->files[index],0,sizeof(MSIFILE));
1318
1319     sz = 72;       
1320     MsiRecordGetStringW(row,1,package->files[index].File,&sz);
1321
1322     sz = 0x100;       
1323     MsiRecordGetStringW(row,2,buffer,&sz);
1324
1325     package->files[index].ComponentIndex = -1;
1326     for (i = 0; i < package->loaded_components; i++)
1327         if (strcmpW(package->components[i].Component,buffer)==0)
1328         {
1329             package->files[index].ComponentIndex = i;
1330             break;
1331         }
1332     if (package->files[index].ComponentIndex == -1)
1333         ERR("Unfound Component %s\n",debugstr_w(buffer));
1334
1335     sz = MAX_PATH;       
1336     MsiRecordGetStringW(row,3,package->files[index].FileName,&sz);
1337
1338     reduce_to_longfilename(package->files[index].FileName);
1339     
1340     package->files[index].FileSize = MsiRecordGetInteger(row,4);
1341
1342     sz = 72;       
1343     if (!MsiRecordIsNull(row,5))
1344         MsiRecordGetStringW(row,5,package->files[index].Version,&sz);
1345
1346     sz = 20;       
1347     if (!MsiRecordIsNull(row,6))
1348         MsiRecordGetStringW(row,6,package->files[index].Language,&sz);
1349
1350     if (!MsiRecordIsNull(row,7))
1351         package->files[index].Attributes= MsiRecordGetInteger(row,7);
1352
1353     package->files[index].Sequence= MsiRecordGetInteger(row,8);
1354
1355     package->files[index].Temporary = FALSE;
1356     package->files[index].State = 0;
1357
1358     TRACE("File Loaded (%s)\n",debugstr_w(package->files[index].File));  
1359  
1360     return ERROR_SUCCESS;
1361 }
1362
1363 static UINT ACTION_FileCost(MSIHANDLE hPackage)
1364 {
1365     MSIHANDLE view;
1366     MSIHANDLE row;
1367     MSIPACKAGE *package;
1368     UINT rc;
1369     static const CHAR Query[] = "SELECT * FROM File Order by Sequence";
1370
1371     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
1372     if (!package)
1373         return ERROR_INVALID_HANDLE;
1374
1375     rc = MsiDatabaseOpenViewA(package->db, Query, &view);
1376     if (rc != ERROR_SUCCESS)
1377         return rc;
1378    
1379     rc = MsiViewExecute(view, 0);
1380     if (rc != ERROR_SUCCESS)
1381     {
1382         MsiViewClose(view);
1383         MsiCloseHandle(view);
1384         return rc;
1385     }
1386
1387     while (1)
1388     {
1389         rc = MsiViewFetch(view,&row);
1390         if (rc != ERROR_SUCCESS)
1391         {
1392             rc = ERROR_SUCCESS;
1393             break;
1394         }
1395         load_file(package,row);
1396         MsiCloseHandle(row);
1397     }
1398     MsiViewClose(view);
1399     MsiCloseHandle(view);
1400
1401     return ERROR_SUCCESS;
1402 }
1403
1404 static INT load_folder(MSIHANDLE hPackage, const WCHAR* dir)
1405
1406 {
1407     WCHAR Query[1024] = 
1408 {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','D','i','r','e','c',
1409 't','o','r','y',' ','w','h','e','r','e',' ','`','D','i','r','e','c','t',
1410 'o','r','y','`',' ','=',' ','`',0};
1411     static const WCHAR end[]={'`',0};
1412     UINT rc;
1413     MSIHANDLE view;
1414     WCHAR targetbuffer[0x100];
1415     WCHAR *srcdir = NULL;
1416     WCHAR *targetdir = NULL;
1417     WCHAR parent[0x100];
1418     DWORD sz=0x100;
1419     MSIHANDLE row = 0;
1420     MSIPACKAGE *package;
1421     INT i,index = -1;
1422
1423     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
1424
1425     TRACE("Looking for dir %s\n",debugstr_w(dir));
1426
1427     for (i = 0; i < package->loaded_folders; i++)
1428     {
1429         if (strcmpW(package->folders[i].Directory,dir)==0)
1430         {
1431             TRACE(" %s retuning on index %i\n",debugstr_w(dir),i);
1432             return i;
1433         }
1434     }
1435
1436     TRACE("Working to load %s\n",debugstr_w(dir));
1437
1438     index = package->loaded_folders; 
1439
1440     package->loaded_folders++;
1441     if (package->loaded_folders== 1)
1442         package->folders = HeapAlloc(GetProcessHeap(),0,
1443                                         sizeof(MSIFOLDER));
1444     else
1445         package->folders= HeapReAlloc(GetProcessHeap(),0,
1446             package->folders, package->loaded_folders* 
1447             sizeof(MSIFOLDER));
1448
1449     memset(&package->folders[index],0,sizeof(MSIFOLDER));
1450
1451     strcpyW(package->folders[index].Directory,dir);
1452
1453     strcatW(Query,dir);
1454     strcatW(Query,end);
1455
1456     rc = MsiDatabaseOpenViewW(package->db, Query, &view);
1457
1458     if (rc != ERROR_SUCCESS)
1459         return -1;
1460
1461     rc = MsiViewExecute(view, 0);
1462     if (rc != ERROR_SUCCESS)
1463     {
1464         MsiViewClose(view);
1465         MsiCloseHandle(view);
1466         return -1;
1467     }
1468
1469     rc = MsiViewFetch(view,&row);
1470     if (rc != ERROR_SUCCESS)
1471     {
1472         MsiViewClose(view);
1473         MsiCloseHandle(view);
1474         return -1;
1475     }
1476
1477     sz=0x100;
1478     MsiRecordGetStringW(row,3,targetbuffer,&sz);
1479     targetdir=targetbuffer;
1480
1481     /* split src and target dir */
1482     if (strchrW(targetdir,':'))
1483     {
1484         srcdir=strchrW(targetdir,':');
1485         *srcdir=0;
1486         srcdir ++;
1487     }
1488     else
1489         srcdir=NULL;
1490
1491     /* for now only pick long filename versions */
1492     if (strchrW(targetdir,'|'))
1493     {
1494         targetdir = strchrW(targetdir,'|'); 
1495         *targetdir = 0;
1496         targetdir ++;
1497     }
1498     if (srcdir && strchrW(srcdir,'|'))
1499     {
1500         srcdir= strchrW(srcdir,'|'); 
1501         *srcdir= 0;
1502         srcdir ++;
1503     }
1504
1505     /* now check for root dirs */
1506     if (targetdir[0] == '.' && targetdir[1] == 0)
1507         targetdir = NULL;
1508         
1509     if (srcdir && srcdir[0] == '.' && srcdir[1] == 0)
1510         srcdir = NULL;
1511
1512      if (targetdir)
1513         strcpyW(package->folders[index].TargetDefault,targetdir);
1514
1515      if (srcdir)
1516         strcpyW(package->folders[index].SourceDefault,srcdir);
1517      else if (targetdir)
1518         strcpyW(package->folders[index].SourceDefault,targetdir);
1519
1520     if (MsiRecordIsNull(row,2))
1521         parent[0]=0;
1522     else
1523     {
1524             sz=0x100;
1525             MsiRecordGetStringW(row,2,parent,&sz);
1526     }
1527
1528     if (parent[0]) 
1529     {
1530         i = load_folder(hPackage,parent);
1531         package->folders[index].ParentIndex = i;
1532         TRACE("Parent is index %i... %s %s\n",
1533                     package->folders[index].ParentIndex,
1534     debugstr_w(package->folders[package->folders[index].ParentIndex].Directory),
1535                     debugstr_w(parent));
1536     }
1537     else
1538         package->folders[index].ParentIndex = -2;
1539
1540     sz = MAX_PATH;
1541     rc = MsiGetPropertyW(hPackage, dir, package->folders[index].Property, &sz);
1542     if (rc != ERROR_SUCCESS)
1543         package->folders[index].Property[0]=0;
1544
1545     MsiCloseHandle(row);
1546     MsiViewClose(view);
1547     MsiCloseHandle(view);
1548     TRACE(" %s retuning on index %i\n",debugstr_w(dir),index);
1549     return index;
1550 }
1551
1552 static UINT resolve_folder(MSIHANDLE hPackage, LPCWSTR name, LPWSTR path, 
1553                            BOOL source, BOOL set_prop, MSIFOLDER **folder)
1554 {
1555     MSIPACKAGE *package;
1556     INT i;
1557     UINT rc = ERROR_SUCCESS;
1558     DWORD sz;
1559
1560     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
1561
1562     TRACE("Working to resolve %s\n",debugstr_w(name));
1563
1564     if (!path)
1565         return rc;
1566
1567     /* special resolving for Target and Source root dir */
1568     if (strcmpW(name,cszTargetDir)==0 || strcmpW(name,cszSourceDir)==0)
1569     {
1570         if (!source)
1571         {
1572             sz = MAX_PATH;
1573             rc = MsiGetPropertyW(hPackage,cszTargetDir,path,&sz);
1574             if (rc != ERROR_SUCCESS)
1575             {
1576                 sz = MAX_PATH;
1577                 rc = MsiGetPropertyW(hPackage,cszRootDrive,path,&sz);
1578                 if (set_prop)
1579                     MsiSetPropertyW(hPackage,cszTargetDir,path);
1580             }
1581             if (folder)
1582                 *folder = &(package->folders[0]);
1583             return rc;
1584         }
1585         else
1586         {
1587             sz = MAX_PATH;
1588             rc = MsiGetPropertyW(hPackage,cszSourceDir,path,&sz);
1589             if (rc != ERROR_SUCCESS)
1590             {
1591                 sz = MAX_PATH;
1592                 rc = MsiGetPropertyW(hPackage,cszDatabase,path,&sz);
1593                 if (rc == ERROR_SUCCESS)
1594                 {
1595                     LPWSTR ptr = strrchrW(path,'\\');
1596                     if (ptr)
1597                     {
1598                         ptr++;
1599                         *ptr = 0;
1600                     }
1601                 }
1602             }
1603             if (folder)
1604                 *folder = &(package->folders[0]);
1605             return rc;
1606         }
1607     }
1608
1609     for (i = 0; i < package->loaded_folders; i++)
1610     {
1611         if (strcmpW(package->folders[i].Directory,name)==0)
1612             break;
1613     }
1614
1615     if (i >= package->loaded_folders)
1616         return ERROR_FUNCTION_FAILED;
1617
1618     if (folder)
1619         *folder = &(package->folders[i]);
1620
1621     if (!source && package->folders[i].ResolvedTarget[0])
1622     {
1623         strcpyW(path,package->folders[i].ResolvedTarget);
1624         TRACE("   already resolved to %s\n",debugstr_w(path));
1625         return ERROR_SUCCESS;
1626     }
1627     else if (source && package->folders[i].ResolvedSource[0])
1628     {
1629         strcpyW(path,package->folders[i].ResolvedSource);
1630         return ERROR_SUCCESS;
1631     }
1632     else if (!source && package->folders[i].Property[0])
1633     {
1634         strcpyW(path,package->folders[i].Property);
1635         TRACE("   internally set to %s\n",debugstr_w(path));
1636         if (set_prop)
1637             MsiSetPropertyW(hPackage,name,path);
1638         return ERROR_SUCCESS;
1639     }
1640
1641     if (package->folders[i].ParentIndex >= 0)
1642     {
1643         TRACE(" ! Parent is %s\n", debugstr_w(package->folders[
1644                    package->folders[i].ParentIndex].Directory));
1645         resolve_folder(hPackage, package->folders[
1646                        package->folders[i].ParentIndex].Directory, path,source,
1647                        set_prop, NULL);
1648
1649         if (!source)
1650         {
1651             if (package->folders[i].TargetDefault[0])
1652             {
1653                 strcatW(path,package->folders[i].TargetDefault);
1654                 strcatW(path,cszbs);
1655             }
1656             strcpyW(package->folders[i].ResolvedTarget,path);
1657             TRACE("   resolved into %s\n",debugstr_w(path));
1658             if (set_prop)
1659                 MsiSetPropertyW(hPackage,name,path);
1660         }
1661         else 
1662         {
1663             if (package->folders[i].SourceDefault[0])
1664             {
1665                 strcatW(path,package->folders[i].SourceDefault);
1666                 strcatW(path,cszbs);
1667             }
1668             strcpyW(package->folders[i].ResolvedSource,path);
1669         }
1670     }
1671     return rc;
1672 }
1673
1674 /* 
1675  * Alot is done in this function aside from just the costing.
1676  * The costing needs to be implemented at some point but for now I am going
1677  * to focus on the directory building
1678  *
1679  */
1680 static UINT ACTION_CostFinalize(MSIHANDLE hPackage)
1681 {
1682     static const CHAR *ExecSeqQuery = "select * from Directory";
1683     static const CHAR *ConditionQuery = "select * from Condition";
1684     UINT rc;
1685     MSIHANDLE view;
1686     MSIPACKAGE *package;
1687     INT i;
1688
1689     TRACE("Building Directory properties\n");
1690
1691     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
1692
1693     rc = MsiDatabaseOpenViewA(package->db, ExecSeqQuery, &view);
1694
1695     if (rc != ERROR_SUCCESS)
1696         return rc;
1697
1698     rc = MsiViewExecute(view, 0);
1699     if (rc != ERROR_SUCCESS)
1700     {
1701         MsiViewClose(view);
1702         MsiCloseHandle(view);
1703         return rc;
1704     }
1705     
1706     while (1)
1707     {
1708         WCHAR name[0x100];
1709         WCHAR path[MAX_PATH];
1710         MSIHANDLE row = 0;
1711         DWORD sz;
1712
1713         rc = MsiViewFetch(view,&row);
1714
1715         if (rc != ERROR_SUCCESS)
1716         {
1717             rc = ERROR_SUCCESS;
1718             break;
1719         }
1720
1721         sz=0x100;
1722         MsiRecordGetStringW(row,1,name,&sz);
1723
1724         /* This helper function now does ALL the work */
1725         TRACE("Dir %s ...\n",debugstr_w(name));
1726         load_folder(hPackage,name);
1727         resolve_folder(hPackage,name,path,FALSE,TRUE,NULL);
1728         TRACE("resolves to %s\n",debugstr_w(path));
1729
1730         MsiCloseHandle(row);
1731      }
1732     MsiViewClose(view);
1733     MsiCloseHandle(view);
1734
1735     TRACE("File calculations %i files\n",package->loaded_files);
1736
1737     for (i = 0; i < package->loaded_files; i++)
1738     {
1739         MSICOMPONENT* comp = NULL;
1740         MSIFILE* file= NULL;
1741
1742         file = &package->files[i];
1743         if (file->ComponentIndex >= 0)
1744             comp = &package->components[file->ComponentIndex];
1745
1746         if (comp)
1747         {
1748             /* calculate target */
1749             resolve_folder(hPackage, comp->Directory, file->TargetPath, FALSE,
1750                        FALSE, NULL);
1751             strcatW(file->TargetPath,file->FileName);
1752
1753             TRACE("file %s resolves to %s\n",
1754                    debugstr_w(file->File),debugstr_w(file->TargetPath));       
1755  
1756             if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1757             {
1758                 file->State = 1;
1759                 comp->Cost += file->FileSize;
1760             }
1761             else
1762             {
1763                 if (file->Version[0])
1764                 {
1765                     DWORD handle;
1766                     DWORD versize;
1767                     UINT sz;
1768                     LPVOID version;
1769                     WCHAR filever[0x100];
1770                     static const WCHAR name[] =
1771                         {'\\','V','a','r','F','i','l','e','I','n','f','o',
1772                          '\\','F','i','l','e','V','e','r','s','i','o','n',0};
1773
1774                     FIXME("Version comparison.. Untried Untested and most "
1775                           "likely very very wrong\n");
1776                     versize = GetFileVersionInfoSizeW(file->TargetPath,&handle);
1777                     version = HeapAlloc(GetProcessHeap(),0,versize);
1778                     GetFileVersionInfoW(file->TargetPath, 0, versize, version);
1779                     sz = 0x100;
1780                     VerQueryValueW(version,name,(LPVOID)filever,&sz);
1781                     HeapFree(GetProcessHeap(),0,version);
1782                 
1783                     if (strcmpW(version,file->Version)<0)
1784                     {
1785                         file->State = 2;
1786                         FIXME("cost should be diff in size\n");
1787                         comp->Cost += file->FileSize;
1788                     }
1789                     else
1790                         file->State = 3;
1791                 }
1792                 else
1793                     file->State = 3;
1794             }
1795         } 
1796     }
1797
1798     TRACE("Evaluating Condition Table\n");
1799
1800     rc = MsiDatabaseOpenViewA(package->db, ConditionQuery, &view);
1801
1802     if (rc != ERROR_SUCCESS)
1803         return rc;
1804
1805     rc = MsiViewExecute(view, 0);
1806     if (rc != ERROR_SUCCESS)
1807     {
1808         MsiViewClose(view);
1809         MsiCloseHandle(view);
1810         return rc;
1811     }
1812     
1813     while (1)
1814     {
1815         WCHAR Feature[0x100];
1816         WCHAR Condition[0x100];
1817         MSIHANDLE row = 0;
1818         DWORD sz;
1819         int feature_index;
1820
1821         rc = MsiViewFetch(view,&row);
1822
1823         if (rc != ERROR_SUCCESS)
1824         {
1825             rc = ERROR_SUCCESS;
1826             break;
1827         }
1828
1829         sz = 0x100;
1830         MsiRecordGetStringW(row,1,Feature,&sz);
1831         sz = 0x100;
1832         MsiRecordGetStringW(row,3,Condition,&sz);
1833
1834         feature_index = get_loaded_feature(package,Feature);
1835         if (feature_index < 0)
1836             ERR("FAILED to find loaded feature %s\n",debugstr_w(Feature));
1837         else
1838         {
1839             if (MsiEvaluateConditionW(hPackage,Condition) == MSICONDITION_TRUE)
1840             {
1841                 int level = MsiRecordGetInteger(row,2);
1842                 TRACE("Reseting feature %s to level %i\n",debugstr_w(Feature),
1843                        level);
1844                 package->features[feature_index].Level = level;
1845             }
1846         }
1847
1848         MsiCloseHandle(row);
1849     }
1850     MsiViewClose(view);
1851     MsiCloseHandle(view);
1852
1853     TRACE("Enabling or Disabling Components\n");
1854     for (i = 0; i < package->loaded_components; i++)
1855     {
1856         if (package->components[i].Condition[0])
1857         {
1858             if (MsiEvaluateConditionW(hPackage,
1859                 package->components[i].Condition) == MSICONDITION_FALSE)
1860             {
1861                 TRACE("Disabling component %s\n",
1862                       debugstr_w(package->components[i].Component));
1863                 package->components[i].Enabled = FALSE;
1864             }
1865         }
1866     }
1867
1868     MsiSetPropertyA(hPackage,"CostingComplete","1");
1869     return ERROR_SUCCESS;
1870 }
1871
1872 /*
1873  * This is a helper function for handling embedded cabinet media
1874  */
1875 static UINT writeout_cabinet_stream(MSIHANDLE hPackage, WCHAR* stream_name,
1876                                     WCHAR* source)
1877 {
1878     UINT rc;
1879     USHORT* data;
1880     UINT    size;
1881     DWORD   write;
1882     HANDLE  the_file;
1883     MSIHANDLE db;
1884     WCHAR tmp[MAX_PATH];
1885
1886     db = MsiGetActiveDatabase(hPackage);
1887     rc = read_raw_stream_data(db,stream_name,&data,&size); 
1888     MsiCloseHandle(db);
1889
1890     if (rc != ERROR_SUCCESS)
1891         return rc;
1892
1893     write = MAX_PATH;
1894     if (MsiGetPropertyW(hPackage, cszTempFolder, tmp, &write))
1895         GetTempPathW(MAX_PATH,tmp);
1896
1897     GetTempFileNameW(tmp,stream_name,0,source);
1898
1899     track_tempfile(hPackage,strrchrW(source,'\\'), source);
1900     the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1901                            FILE_ATTRIBUTE_NORMAL, NULL);
1902
1903     if (the_file == INVALID_HANDLE_VALUE)
1904     {
1905         rc = ERROR_FUNCTION_FAILED;
1906         goto end;
1907     }
1908
1909     WriteFile(the_file,data,size,&write,NULL);
1910     CloseHandle(the_file);
1911     TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
1912 end:
1913     HeapFree(GetProcessHeap(),0,data);
1914     return rc;
1915 }
1916
1917
1918 /***********************************************************************
1919  *            extract_cabinet_file
1920  *
1921  * Extract  files from a cab file.
1922  */
1923 static void (WINAPI *pExtractFiles)( LPSTR, LPSTR, DWORD, DWORD, DWORD, DWORD );
1924
1925 static BOOL extract_cabinet_file_advpack( const WCHAR *cabinet, 
1926                                           const WCHAR *root)
1927 {
1928     static HMODULE advpack;
1929
1930     char *cab_path, *cab_file;
1931
1932     if (!pExtractFiles)
1933     {
1934         if (!advpack && !(advpack = LoadLibraryA( "advpack.dll" )))
1935         {
1936             ERR( "could not load advpack.dll\n" );
1937             return FALSE;
1938         }
1939         if (!(pExtractFiles = (void *)GetProcAddress( advpack, "ExtractFiles"
1940 )))
1941         {
1942             ERR( "could not find ExtractFiles in advpack.dll\n" );
1943             return FALSE;
1944         }
1945     }
1946
1947     if (!(cab_file = strdupWtoA( cabinet ))) return FALSE;
1948     if (!(cab_path = strdupWtoA( root ))) return FALSE;
1949
1950     FIXME( "awful hack: extracting cabinet %s\n", debugstr_a(cab_file) );
1951     pExtractFiles( cab_file, cab_path, 0, 0, 0, 0 );
1952     HeapFree( GetProcessHeap(), 0, cab_file );
1953     HeapFree( GetProcessHeap(), 0, cab_path );
1954     return TRUE;
1955 }
1956
1957 static BOOL extract_cabinet_file_cabinet( const WCHAR *cabinet, 
1958                                           const WCHAR *root)
1959                                   
1960 {
1961     /* from cabinet.h */
1962
1963     struct ExtractFileList {
1964         LPSTR  filename;
1965         struct ExtractFileList *next;
1966         BOOL   unknown;  /* always 1L */
1967     } ;
1968
1969     typedef struct {
1970         long  result1;          /* 0x000 */
1971         long  unknown1[3];      /* 0x004 */
1972         struct ExtractFileList* filelist;         /* 0x010 */
1973         long  filecount;        /* 0x014 */
1974         long  unknown2;         /* 0x018 */
1975         char  directory[0x104]; /* 0x01c */
1976         char  lastfile[0x20c];  /* 0x120 */
1977     } EXTRACTdest;
1978
1979     HRESULT WINAPI Extract(EXTRACTdest *dest, LPCSTR what);
1980
1981     char *cab_path, *src_path;
1982     EXTRACTdest exd;
1983     struct ExtractFileList fl;
1984
1985     if (!(cab_path = strdupWtoA( cabinet ))) return FALSE;
1986     if (!(src_path = strdupWtoA( root ))) return FALSE;
1987
1988     memset(&exd,0,sizeof(exd));
1989     strcpy(exd.directory,src_path);
1990     exd.unknown2 = 0x1;
1991     fl.filename = cab_path;
1992     fl.next = NULL;
1993     fl.unknown = 1;
1994     exd.filelist = &fl;
1995     FIXME( "more aweful hack: extracting cabinet %s\n", debugstr_a(cab_path) );
1996     Extract(&exd,cab_path);
1997
1998     HeapFree( GetProcessHeap(), 0, cab_path );
1999     HeapFree( GetProcessHeap(), 0, src_path );
2000     return TRUE;
2001 }
2002
2003 static BOOL extract_cabinet_file(const WCHAR* source, const WCHAR* path)
2004 {
2005     TRACE("Extracting %s to %s\n",debugstr_w(source), debugstr_w(path));
2006     if (!extract_cabinet_file_advpack(source,path))
2007         return extract_cabinet_file_cabinet(source,path);
2008     return TRUE;
2009 }
2010
2011 static UINT ready_media_for_file(MSIHANDLE hPackage, UINT sequence, 
2012                                  WCHAR* path)
2013 {
2014     UINT rc;
2015     MSIHANDLE view;
2016     MSIHANDLE row = 0;
2017     WCHAR source[MAX_PATH];
2018     static const CHAR *ExecSeqQuery = 
2019         "select * from Media where LastSequence >= %i order by LastSequence";
2020     CHAR Query[1024];
2021     WCHAR cab[0x100];
2022     DWORD sz=0x100;
2023     INT seq;
2024     static INT last_sequence = 0; 
2025     MSIHANDLE db;
2026
2027     if (sequence <= last_sequence)
2028     {
2029         TRACE("Media already ready (%i, %i)\n",sequence,last_sequence);
2030         return ERROR_SUCCESS;
2031     }
2032
2033     sprintf(Query,ExecSeqQuery,sequence);
2034
2035     db = MsiGetActiveDatabase(hPackage);
2036     rc = MsiDatabaseOpenViewA(db, Query, &view);
2037     MsiCloseHandle(db);
2038
2039     if (rc != ERROR_SUCCESS)
2040         return rc;
2041
2042     rc = MsiViewExecute(view, 0);
2043     if (rc != ERROR_SUCCESS)
2044     {
2045         MsiViewClose(view);
2046         MsiCloseHandle(view);
2047         return rc;
2048     }
2049
2050     rc = MsiViewFetch(view,&row);
2051     if (rc != ERROR_SUCCESS)
2052     {
2053         MsiViewClose(view);
2054         MsiCloseHandle(view);
2055         return rc;
2056     }
2057     seq = MsiRecordGetInteger(row,2);
2058     last_sequence = seq;
2059
2060     if (!MsiRecordIsNull(row,4))
2061     {
2062         sz=0x100;
2063         MsiRecordGetStringW(row,4,cab,&sz);
2064         TRACE("Source is CAB %s\n",debugstr_w(cab));
2065         /* the stream does not contain the # character */
2066         if (cab[0]=='#')
2067         {
2068             writeout_cabinet_stream(hPackage,&cab[1],source);
2069             strcpyW(path,source);
2070             *(strrchrW(path,'\\')+1)=0;
2071         }
2072         else
2073         {
2074             sz = 0x100;
2075             if (MsiGetPropertyW(hPackage, cszSourceDir, source, &sz))
2076             {
2077                 ERR("No Source dir defined \n");
2078                 rc = ERROR_FUNCTION_FAILED;
2079             }
2080             else
2081             {
2082                 strcpyW(path,source);
2083                 strcatW(source,cab);
2084                 /* extract the cab file into a folder in the temp folder */
2085                 sz = MAX_PATH;
2086                 if (MsiGetPropertyW(hPackage, cszTempFolder,path, &sz) 
2087                                     != ERROR_SUCCESS)
2088                     GetTempPathW(MAX_PATH,path);
2089             }
2090         }
2091         rc = !extract_cabinet_file(source,path);
2092     }
2093     MsiCloseHandle(row);
2094     MsiViewClose(view);
2095     MsiCloseHandle(view);
2096     return rc;
2097 }
2098
2099 inline static UINT create_component_directory (MSIHANDLE hPackage, MSIPACKAGE*
2100                                                package, INT component)
2101 {
2102     UINT rc;
2103     MSIFOLDER *folder;
2104     WCHAR install_path[MAX_PATH];
2105
2106     rc = resolve_folder(hPackage, package->components[component].Directory,
2107                         install_path, FALSE, FALSE, &folder);
2108
2109     if (rc != ERROR_SUCCESS)
2110         return rc; 
2111
2112     /* create the path */
2113     if (folder->State == 0)
2114     {
2115         create_full_pathW(install_path);
2116         folder->State = 2;
2117     }
2118
2119     return rc;
2120 }
2121
2122 static UINT ACTION_InstallFiles(MSIHANDLE hPackage)
2123 {
2124     UINT rc = ERROR_SUCCESS;
2125     INT index;
2126     MSIPACKAGE *package;
2127
2128     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2129
2130     if (!package)
2131         return ERROR_INVALID_HANDLE;
2132
2133     for (index = 0; index < package->loaded_files; index++)
2134     {
2135         WCHAR path_to_source[MAX_PATH];
2136         MSIFILE *file;
2137         
2138         file = &package->files[index];
2139
2140         if (file->Temporary)
2141             continue;
2142
2143         if (!package->components[file->ComponentIndex].Enabled ||
2144             !package->components[file->ComponentIndex].FeatureState)
2145         {
2146             TRACE("File %s is not scheduled for install\n",
2147                    debugstr_w(file->File));
2148             continue;
2149         }
2150
2151         if ((file->State == 1) || (file->State == 2))
2152         {
2153             TRACE("Installing %s\n",debugstr_w(file->File));
2154             rc = ready_media_for_file(hPackage,file->Sequence,path_to_source);
2155             /* 
2156              * WARNING!
2157              * our file table could change here because a new temp file
2158              * may have been created
2159              */
2160             file = &package->files[index];
2161             if (rc != ERROR_SUCCESS)
2162             {
2163                 ERR("Unable to ready media\n");
2164                 rc = ERROR_FUNCTION_FAILED;
2165                 break;
2166             }
2167
2168             create_component_directory(hPackage, package, file->ComponentIndex);
2169
2170             strcpyW(file->SourcePath, path_to_source);
2171             strcatW(file->SourcePath, file->File);
2172
2173             TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
2174                   debugstr_w(file->TargetPath));
2175
2176             progress_message(hPackage,2,1,0,0);
2177             rc = !MoveFileW(file->SourcePath,file->TargetPath);
2178             if (rc)
2179                 ERR("Unable to move file\n");
2180             else
2181                 file->State = 4;
2182         }
2183     }
2184
2185     return rc;
2186 }
2187
2188 inline static UINT get_file_target(MSIHANDLE hPackage, LPCWSTR file_key, 
2189                                    LPWSTR file_source)
2190 {
2191     MSIPACKAGE *package;
2192     INT index;
2193
2194     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2195     if (!package)
2196         return ERROR_INVALID_HANDLE;
2197
2198     for (index = 0; index < package->loaded_files; index ++)
2199     {
2200         if (strcmpW(file_key,package->files[index].File)==0)
2201         {
2202             if (package->files[index].State >= 3)
2203             {
2204                 strcpyW(file_source,package->files[index].TargetPath);
2205                 return ERROR_SUCCESS;
2206             }
2207             else
2208                 return ERROR_FILE_NOT_FOUND;
2209         }
2210     }
2211
2212     return ERROR_FUNCTION_FAILED;
2213 }
2214
2215 static UINT ACTION_DuplicateFiles(MSIHANDLE hPackage)
2216 {
2217     UINT rc;
2218     MSIHANDLE view;
2219     MSIHANDLE row = 0;
2220     static const CHAR *ExecSeqQuery = "select * from DuplicateFile";
2221     MSIPACKAGE* package;
2222
2223     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2224     if (!package)
2225         return ERROR_INVALID_HANDLE;
2226
2227     rc = MsiDatabaseOpenViewA(package->db, ExecSeqQuery, &view);
2228
2229     if (rc != ERROR_SUCCESS)
2230         return rc;
2231
2232     rc = MsiViewExecute(view, 0);
2233     if (rc != ERROR_SUCCESS)
2234     {
2235         MsiViewClose(view);
2236         MsiCloseHandle(view);
2237         return rc;
2238     }
2239
2240     while (1)
2241     {
2242         WCHAR file_key[0x100];
2243         WCHAR file_source[MAX_PATH];
2244         WCHAR dest_name[0x100];
2245         WCHAR dest_path[MAX_PATH];
2246         WCHAR component[0x100];
2247         INT component_index;
2248
2249         DWORD sz=0x100;
2250
2251         rc = MsiViewFetch(view,&row);
2252         if (rc != ERROR_SUCCESS)
2253         {
2254             rc = ERROR_SUCCESS;
2255             break;
2256         }
2257
2258         sz=0x100;
2259         rc = MsiRecordGetStringW(row,2,component,&sz);
2260         if (rc != ERROR_SUCCESS)
2261         {
2262             ERR("Unable to get component\n");
2263             MsiCloseHandle(row);
2264             break;
2265         }
2266
2267         component_index = get_loaded_component(package,component);
2268         if (!package->components[component_index].Enabled ||
2269             !package->components[component_index].FeatureState)
2270         {
2271             TRACE("Skipping copy due to disabled component\n");
2272             MsiCloseHandle(row);
2273             continue;
2274         }
2275
2276         sz=0x100;
2277         rc = MsiRecordGetStringW(row,3,file_key,&sz);
2278         if (rc != ERROR_SUCCESS)
2279         {
2280             ERR("Unable to get file key\n");
2281             MsiCloseHandle(row);
2282             break;
2283         }
2284
2285         rc = get_file_target(hPackage,file_key,file_source);
2286
2287         if (rc != ERROR_SUCCESS)
2288         {
2289             ERR("Original file unknown %s\n",debugstr_w(file_key));
2290             MsiCloseHandle(row);
2291             break;
2292         }
2293
2294         if (MsiRecordIsNull(row,4))
2295         {
2296             strcpyW(dest_name,strrchrW(file_source,'\\')+1);
2297         }
2298         else
2299         {
2300             sz=0x100;
2301             MsiRecordGetStringW(row,4,dest_name,&sz);
2302             reduce_to_longfilename(dest_name);
2303          }
2304
2305         if (MsiRecordIsNull(row,5))
2306         {
2307             strcpyW(dest_path,file_source);
2308             *strrchrW(dest_path,'\\')=0;
2309         }
2310         else
2311         {
2312             WCHAR destkey[0x100];
2313             sz=0x100;
2314             MsiRecordGetStringW(row,5,destkey,&sz);
2315             sz = 0x100;
2316             rc = resolve_folder(hPackage, destkey, dest_path,FALSE,FALSE,NULL);
2317             if (rc != ERROR_SUCCESS)
2318             {
2319                 ERR("Unable to get destination folder\n");
2320                 MsiCloseHandle(row);
2321                 break;
2322             }
2323         }
2324
2325         strcatW(dest_path,dest_name);
2326            
2327         TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
2328               debugstr_w(dest_path)); 
2329         
2330         if (strcmpW(file_source,dest_path))
2331             rc = !CopyFileW(file_source,dest_path,TRUE);
2332         else
2333             rc = ERROR_SUCCESS;
2334         
2335         if (rc != ERROR_SUCCESS)
2336             ERR("Failed to copy file\n");
2337
2338         FIXME("We should track these duplicate files as well\n");   
2339  
2340         MsiCloseHandle(row);
2341     }
2342     MsiViewClose(view);
2343     MsiCloseHandle(view);
2344     return rc;
2345
2346 }
2347
2348
2349 /* OK this value is "interpretted" and then formatted based on the 
2350    first few characters */
2351 static LPSTR parse_value(MSIHANDLE hPackage, WCHAR *value, DWORD *type, 
2352                          DWORD *size)
2353 {
2354     LPSTR data = NULL;
2355     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2356     {
2357         if (value[1]=='x')
2358         {
2359             LPWSTR ptr;
2360             CHAR byte[5];
2361             LPWSTR deformated;
2362             int count;
2363
2364             deformat_string(hPackage, &value[2], &deformated);
2365
2366             /* binary value type */
2367             ptr = deformated; 
2368             *type=REG_BINARY;
2369             *size = strlenW(ptr)/2;
2370             data = HeapAlloc(GetProcessHeap(),0,*size);
2371           
2372             byte[0] = '0'; 
2373             byte[1] = 'x'; 
2374             byte[4] = 0; 
2375             count = 0;
2376             while (*ptr)
2377             {
2378                 byte[2]= *ptr;
2379                 ptr++;
2380                 byte[3]= *ptr;
2381                 ptr++;
2382                 data[count] = (BYTE)strtol(byte,NULL,0);
2383                 count ++;
2384             }
2385             HeapFree(GetProcessHeap(),0,deformated);
2386
2387             TRACE("Data %li bytes(%i)\n",*size,count);
2388         }
2389         else
2390         {
2391             LPWSTR deformated;
2392             deformat_string(hPackage, &value[1], &deformated);
2393
2394             *type=REG_DWORD; 
2395             *size = sizeof(DWORD);
2396             data = HeapAlloc(GetProcessHeap(),0,*size);
2397             *(LPDWORD)data = atoiW(deformated); 
2398             TRACE("DWORD %i\n",*data);
2399
2400             HeapFree(GetProcessHeap(),0,deformated);
2401         }
2402     }
2403     else
2404     {
2405         WCHAR *ptr;
2406         *type=REG_SZ;
2407
2408         if (value[0]=='#')
2409         {
2410             if (value[1]=='%')
2411             {
2412                 ptr = &value[2];
2413                 *type=REG_EXPAND_SZ;
2414             }
2415             else
2416                 ptr = &value[1];
2417          }
2418          else
2419             ptr=value;
2420
2421         *size = deformat_string(hPackage, ptr,(LPWSTR*)&data);
2422     }
2423     return data;
2424 }
2425
2426 static UINT ACTION_WriteRegistryValues(MSIHANDLE hPackage)
2427 {
2428     UINT rc;
2429     MSIHANDLE view;
2430     MSIHANDLE row = 0;
2431     static const CHAR *ExecSeqQuery = "select * from Registry";
2432     MSIPACKAGE *package;
2433
2434     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2435     if (!package)
2436         return ERROR_INVALID_HANDLE;
2437
2438     rc = MsiDatabaseOpenViewA(package->db, ExecSeqQuery, &view);
2439
2440     if (rc != ERROR_SUCCESS)
2441         return rc;
2442
2443     rc = MsiViewExecute(view, 0);
2444     if (rc != ERROR_SUCCESS)
2445     {
2446         MsiViewClose(view);
2447         MsiCloseHandle(view);
2448         return rc;
2449     }
2450
2451     while (1)
2452     {
2453         WCHAR key[0x100];
2454         WCHAR name[0x100];
2455         LPWSTR value;
2456         LPSTR value_data = NULL;
2457         HKEY  root_key, hkey;
2458         DWORD type,size;
2459         WCHAR component[0x100];
2460         INT component_index;
2461
2462         INT   root;
2463         DWORD sz=0x100;
2464
2465         rc = MsiViewFetch(view,&row);
2466         if (rc != ERROR_SUCCESS)
2467         {
2468             rc = ERROR_SUCCESS;
2469             break;
2470         }
2471
2472         sz= 0x100;
2473         MsiRecordGetStringW(row,6,component,&sz);
2474         component_index = get_loaded_component(package,component);
2475
2476         if (!package->components[component_index].Enabled ||
2477             !package->components[component_index].FeatureState)
2478         {
2479             TRACE("Skipping write due to disabled component\n");
2480             MsiCloseHandle(row);
2481             continue;
2482         }
2483
2484         /* null values have special meanings during uninstalls and such */
2485         
2486         if(MsiRecordIsNull(row,5))
2487         {
2488             MsiCloseHandle(row);
2489             continue;
2490         }
2491
2492         root = MsiRecordGetInteger(row,2);
2493         sz = 0x100;
2494         MsiRecordGetStringW(row,3,key,&sz);
2495       
2496         sz = 0x100; 
2497         if (MsiRecordIsNull(row,4))
2498             name[0]=0;
2499         else
2500             MsiRecordGetStringW(row,4,name,&sz);
2501    
2502
2503         /* get the root key */
2504         switch (root)
2505         {
2506             case 0:  root_key = HKEY_CLASSES_ROOT; break;
2507             case 1:  root_key = HKEY_CURRENT_USER; break;
2508             case 2:  root_key = HKEY_LOCAL_MACHINE; break;
2509             case 3:  root_key = HKEY_USERS; break;
2510             default:
2511                  ERR("Unknown root %i\n",root);
2512                  root_key=NULL;
2513                  break;
2514         }
2515         if (!root_key)
2516         {
2517             MsiCloseHandle(row);
2518             continue;
2519         }
2520
2521         if (RegCreateKeyW( root_key, key, &hkey))
2522         {
2523             ERR("Could not create key %s\n",debugstr_w(key));
2524             MsiCloseHandle(row);
2525             continue;
2526         }
2527
2528         sz = 0;
2529         MsiRecordGetStringW(row,5,NULL,&sz);
2530         sz++;
2531         value = HeapAlloc(GetProcessHeap(),0,sz * sizeof(WCHAR));
2532         MsiRecordGetStringW(row,5,value,&sz);
2533         value_data = parse_value(hPackage, value, &type, &size); 
2534         HeapFree(GetProcessHeap(),0,value);
2535
2536         if (value_data)
2537         {
2538             TRACE("Setting value %s\n",debugstr_w(name));
2539             RegSetValueExW(hkey, name, 0, type, value_data, size);
2540             HeapFree(GetProcessHeap(),0,value_data);
2541         }
2542         progress_message(hPackage,2,1,0,0);
2543
2544         MsiCloseHandle(row);
2545     }
2546     MsiViewClose(view);
2547     MsiCloseHandle(view);
2548     return rc;
2549 }
2550
2551 /*
2552  * This helper function should probably go alot of places
2553  *
2554  * Thinking about this, maybe this should become yet another Bison file
2555  */
2556 static DWORD deformat_string(MSIHANDLE hPackage, WCHAR* ptr,WCHAR** data)
2557 {
2558     WCHAR* mark=NULL;
2559     DWORD size=0;
2560     DWORD chunk=0;
2561     WCHAR key[0x100];
2562     WCHAR value[0x100];
2563     DWORD sz;
2564
2565     /* scan for special characters */
2566     if (!strchrW(ptr,'[') || (strchrW(ptr,'[') && !strchrW(ptr,']')))
2567     {
2568         /* not formatted */
2569         size = (strlenW(ptr)+1) * sizeof(WCHAR);
2570         *data = HeapAlloc(GetProcessHeap(),0,size);
2571         strcpyW(*data,ptr);
2572         return size;
2573     }
2574    
2575     /* formatted string located */ 
2576     mark = strchrW(ptr,'[');
2577     if (mark != ptr)
2578     {
2579         INT cnt = (mark - ptr);
2580         TRACE("%i  (%i) characters before marker\n",cnt,(mark-ptr));
2581         size = cnt * sizeof(WCHAR);
2582         size += sizeof(WCHAR);
2583         *data = HeapAlloc(GetProcessHeap(),0,size);
2584         strncpyW(*data,ptr,cnt);
2585         (*data)[cnt]=0;
2586     }
2587     else
2588     {
2589         size = sizeof(WCHAR);
2590         *data = HeapAlloc(GetProcessHeap(),0,size);
2591         (*data)[0]=0;
2592     }
2593     mark++;
2594     strcpyW(key,mark);
2595     *strchrW(key,']')=0;
2596     mark = strchrW(mark,']');
2597     mark++;
2598     TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
2599     sz = 0x100;
2600     if (MsiGetPropertyW(hPackage, key, value,&sz) == ERROR_SUCCESS)
2601     {
2602         LPWSTR newdata;
2603         chunk = (strlenW(value)+1) * sizeof(WCHAR);
2604         size+=chunk;   
2605         newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
2606         *data = newdata;
2607         strcatW(*data,value);
2608     }
2609     TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
2610     if (*mark!=0)
2611     {
2612         LPWSTR newdata;
2613         chunk = (strlenW(mark)+1) * sizeof(WCHAR);
2614         size+=chunk;
2615         newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
2616         *data = newdata;
2617         strcatW(*data,mark);
2618     }
2619     (*data)[strlenW(*data)]=0;
2620     TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
2621
2622     /* recursively do this to clean up */
2623     mark = HeapAlloc(GetProcessHeap(),0,size);
2624     strcpyW(mark,*data);
2625     TRACE("String at this point %s\n",debugstr_w(mark));
2626     size = deformat_string(hPackage,mark,data);
2627     HeapFree(GetProcessHeap(),0,mark);
2628     return size;
2629 }
2630
2631 static UINT ACTION_InstallInitialize(MSIHANDLE hPackage)
2632 {
2633     CHAR level[10000];
2634     INT install_level;
2635     DWORD sz;
2636     MSIPACKAGE *package; 
2637     INT i,j;
2638     /* I do not know if this is where it should happen.. but */
2639
2640     TRACE("Checking Install Level\n");
2641     FIXME("The Attributes of the feature OVERRIDE THIS... unimplemented\n");
2642     FIXME("As does the ALLLOCAL and such propertys\n");
2643
2644     sz = 10000;
2645     if (MsiGetPropertyA(hPackage,"INSTALLLEVEL",level,&sz)==ERROR_SUCCESS)
2646         install_level = atoi(level);
2647     else
2648         install_level = 1;
2649    
2650     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2651     if (!package)
2652         return ERROR_INVALID_HANDLE;
2653
2654     /*
2655      * components FeatureState defaults to FALSE. the idea is we want to 
2656      * enable the component is ANY feature that uses it is enabled to install
2657      */
2658     for(i = 0; i < package->loaded_features; i++)
2659     {
2660         BOOL feature_state= ((package->features[i].Level > 0) &&
2661                              (package->features[i].Level <= install_level));
2662         TRACE("Feature %s has a state of %i\n",
2663                debugstr_w(package->features[i].Feature), feature_state);
2664         for( j = 0; j < package->features[i].ComponentCount; j++)
2665         {
2666             package->components[package->features[i].Components[j]].FeatureState
2667             |= feature_state;
2668         }
2669     } 
2670     /* 
2671      * so basically we ONLY want to install a component if its Enabled AND
2672      * FeatureState are both TRUE 
2673      */
2674     return ERROR_SUCCESS;
2675 }
2676
2677 static UINT ACTION_InstallValidate(MSIHANDLE hPackage)
2678 {
2679     DWORD progress = 0;
2680     static const CHAR q1[]="SELECT * FROM Registry";
2681     static const CHAR q2[]=
2682 "select Action from InstallExecuteSequence where Sequence > 0 order by Sequence";
2683     UINT rc;
2684     MSIHANDLE view;
2685     MSIHANDLE row = 0;
2686     MSIHANDLE db;
2687     BOOL flipit= FALSE;
2688     MSIPACKAGE* package;
2689
2690     TRACE(" InstallValidate \n");
2691
2692     db = MsiGetActiveDatabase(hPackage);
2693     rc = MsiDatabaseOpenViewA(db, q2, &view);
2694     rc = MsiViewExecute(view, 0);
2695
2696     while (1)
2697     {
2698         rc = MsiViewFetch(view,&row);
2699         if (rc != ERROR_SUCCESS)
2700         {
2701             rc = ERROR_SUCCESS;
2702             break;
2703         }
2704         if (!flipit)
2705         {
2706             CHAR buf[0x100];
2707             DWORD sz=0x100;
2708             MsiRecordGetStringA(row,1,buf,&sz);
2709             if (strcmp(buf,"InstallValidate")==0)
2710                 flipit=TRUE;
2711         }
2712         else
2713             progress +=25;
2714
2715         MsiCloseHandle(row);
2716     }
2717     MsiViewClose(view);
2718     MsiCloseHandle(view);
2719
2720     rc = MsiDatabaseOpenViewA(db, q1, &view);
2721     rc = MsiViewExecute(view, 0);
2722     while (1)
2723     {
2724         rc = MsiViewFetch(view,&row);
2725         if (rc != ERROR_SUCCESS)
2726         {
2727             rc = ERROR_SUCCESS;
2728             break;
2729         }
2730         progress +=1;
2731
2732         MsiCloseHandle(row);
2733     }
2734     MsiViewClose(view);
2735     MsiCloseHandle(view);
2736     MsiCloseHandle(db);
2737
2738     package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
2739     progress_message(hPackage,0,progress+package->loaded_files,0,0);
2740
2741     return ERROR_SUCCESS;
2742 }
2743
2744
2745
2746
2747
2748 /* Msi functions that seem approperate here */
2749 UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
2750 {
2751     LPWSTR szwAction;
2752     UINT len,rc;
2753
2754     TRACE(" exteral attempt at action %s\n",szAction);
2755
2756     if (!szAction)
2757         return ERROR_FUNCTION_FAILED;
2758     if (hInstall == 0)
2759         return ERROR_FUNCTION_FAILED;
2760
2761     len = MultiByteToWideChar( CP_ACP, 0, szAction, -1, NULL, 0);
2762     szwAction = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
2763
2764     if (!szwAction)
2765         return ERROR_FUNCTION_FAILED; 
2766
2767     MultiByteToWideChar( CP_ACP, 0, szAction, -1, szwAction, len);
2768
2769     rc = MsiDoActionW(hInstall, szwAction);
2770     HeapFree(GetProcessHeap(),0,szwAction);
2771     return rc;
2772 }
2773
2774 UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction )
2775 {
2776     TRACE(" exteral attempt at action %s \n",debugstr_w(szAction));
2777     return ACTION_PerformAction(hInstall,szAction);
2778 }
2779
2780 UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder, 
2781                                LPSTR szPathBuf, DWORD* pcchPathBuf) 
2782 {
2783     LPWSTR szwFolder;
2784     LPWSTR szwPathBuf;
2785     UINT len,rc;
2786
2787     TRACE("getting folder %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
2788
2789     if (!szFolder)
2790         return ERROR_FUNCTION_FAILED;
2791     if (hInstall == 0)
2792         return ERROR_FUNCTION_FAILED;
2793
2794     len = MultiByteToWideChar( CP_ACP, 0, szFolder, -1, NULL, 0);
2795     szwFolder= HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
2796
2797     if (!szwFolder)
2798         return ERROR_FUNCTION_FAILED; 
2799
2800     szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
2801
2802     MultiByteToWideChar( CP_ACP, 0, szFolder, -1, szwFolder, len);
2803
2804     rc = MsiGetTargetPathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
2805
2806     WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
2807                          *pcchPathBuf, NULL, NULL );
2808
2809     HeapFree(GetProcessHeap(),0,szwFolder);
2810     HeapFree(GetProcessHeap(),0,szwPathBuf);
2811
2812     return rc;
2813 }
2814
2815 UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
2816                                 szPathBuf, DWORD* pcchPathBuf) 
2817 {
2818     WCHAR path[MAX_PATH];
2819     UINT rc;
2820
2821     TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
2822
2823     rc = resolve_folder(hInstall, szFolder, path, FALSE, FALSE, NULL);
2824
2825     if (rc == ERROR_SUCCESS && strlenW(path) > *pcchPathBuf)
2826     {
2827         *pcchPathBuf = strlenW(path)+1;
2828         return ERROR_MORE_DATA;
2829     }
2830     else if (rc == ERROR_SUCCESS)
2831     {
2832         *pcchPathBuf = strlenW(path)+1;
2833         strcpyW(szPathBuf,path);
2834         TRACE("Returning Path %s\n",debugstr_w(path));
2835     }
2836     
2837     return rc;
2838 }
2839
2840
2841 UINT WINAPI MsiGetSourcePathA( MSIHANDLE hInstall, LPCSTR szFolder, 
2842                                LPSTR szPathBuf, DWORD* pcchPathBuf) 
2843 {
2844     LPWSTR szwFolder;
2845     LPWSTR szwPathBuf;
2846     UINT len,rc;
2847
2848     TRACE("getting source %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
2849
2850     if (!szFolder)
2851         return ERROR_FUNCTION_FAILED;
2852     if (hInstall == 0)
2853         return ERROR_FUNCTION_FAILED;
2854
2855     len = MultiByteToWideChar( CP_ACP, 0, szFolder, -1, NULL, 0);
2856     szwFolder= HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
2857
2858     if (!szwFolder)
2859         return ERROR_FUNCTION_FAILED; 
2860
2861     szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
2862
2863     MultiByteToWideChar( CP_ACP, 0, szFolder, -1, szwFolder, len);
2864
2865     rc = MsiGetSourcePathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
2866
2867     WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
2868                          *pcchPathBuf, NULL, NULL );
2869
2870     HeapFree(GetProcessHeap(),0,szwFolder);
2871     HeapFree(GetProcessHeap(),0,szwPathBuf);
2872
2873     return rc;
2874 }
2875
2876 UINT WINAPI MsiGetSourcePathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
2877                                 szPathBuf, DWORD* pcchPathBuf) 
2878 {
2879     WCHAR path[MAX_PATH];
2880     UINT rc;
2881
2882     TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
2883     rc = resolve_folder(hInstall, szFolder, path, TRUE, FALSE, NULL);
2884
2885     if (rc == ERROR_SUCCESS && strlenW(path) > *pcchPathBuf)
2886     {
2887         *pcchPathBuf = strlenW(path)+1;
2888         return ERROR_MORE_DATA;
2889     }
2890     else if (rc == ERROR_SUCCESS)
2891     {
2892         *pcchPathBuf = strlenW(path)+1;
2893         strcpyW(szPathBuf,path);
2894         TRACE("Returning Path %s\n",debugstr_w(path));
2895     }
2896     
2897     return rc;
2898 }
2899
2900
2901 UINT WINAPI MsiSetTargetPathA(MSIHANDLE hInstall, LPCSTR szFolder, 
2902                              LPCSTR szFolderPath)
2903 {
2904     LPWSTR szwFolder;
2905     LPWSTR szwFolderPath;
2906     UINT rc,len;
2907
2908     if (!szFolder)
2909         return ERROR_FUNCTION_FAILED;
2910     if (hInstall == 0)
2911         return ERROR_FUNCTION_FAILED;
2912
2913     len = MultiByteToWideChar( CP_ACP, 0, szFolder, -1, NULL, 0);
2914     szwFolder= HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
2915
2916     if (!szwFolder)
2917         return ERROR_FUNCTION_FAILED; 
2918
2919     MultiByteToWideChar( CP_ACP, 0, szFolder, -1, szwFolder, len);
2920
2921     len = MultiByteToWideChar( CP_ACP, 0, szFolderPath, -1, NULL, 0);
2922     szwFolderPath= HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
2923
2924     if (!szwFolderPath)
2925     {
2926         HeapFree(GetProcessHeap(),0,szwFolder);
2927         return ERROR_FUNCTION_FAILED; 
2928     }
2929
2930     MultiByteToWideChar( CP_ACP, 0, szFolderPath, -1, szwFolderPath, len);
2931
2932     rc = MsiSetTargetPathW(hInstall, szwFolder, szwFolderPath);
2933
2934     HeapFree(GetProcessHeap(),0,szwFolder);
2935     HeapFree(GetProcessHeap(),0,szwFolderPath);
2936
2937     return rc;
2938 }
2939
2940 UINT WINAPI MsiSetTargetPathW(MSIHANDLE hInstall, LPCWSTR szFolder, 
2941                              LPCWSTR szFolderPath)
2942 {
2943     MSIPACKAGE *package;
2944     INT i;
2945     WCHAR path[MAX_PATH];
2946     MSIFOLDER *folder;
2947
2948     TRACE("(%s %s)\n",debugstr_w(szFolder),debugstr_w(szFolderPath));
2949
2950     if (szFolderPath[0]==0)
2951         return ERROR_FUNCTION_FAILED;
2952
2953     if (GetFileAttributesW(szFolderPath) == INVALID_FILE_ATTRIBUTES)
2954         return ERROR_FUNCTION_FAILED;
2955
2956     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
2957
2958     if (package==NULL)
2959         return ERROR_INVALID_HANDLE;
2960
2961     resolve_folder(hInstall,szFolder,path,FALSE,FALSE,&folder);
2962
2963     if (!folder)
2964         return ERROR_INVALID_PARAMETER;
2965
2966     strcpyW(folder->Property,szFolderPath);
2967
2968     for (i = 0; i < package->loaded_folders; i++)
2969         package->folders[i].ResolvedTarget[0]=0;
2970
2971     for (i = 0; i < package->loaded_folders; i++)
2972         resolve_folder(hInstall, package->folders[i].Directory, path, FALSE,
2973                        TRUE, NULL);
2974
2975     return ERROR_SUCCESS;
2976 }
2977
2978 BOOL WINAPI MsiGetMode(MSIHANDLE hInstall, DWORD iRunMode)
2979 {
2980     FIXME("STUB (%li)\n",iRunMode);
2981     return FALSE;
2982 }
2983
2984 #if 0
2985 static UINT ACTION_Template(MSIHANDLE hPackage)
2986 {
2987     UINT rc;
2988     MSIHANDLE view;
2989     MSIHANDLE row = 0;
2990     static const CHAR *ExecSeqQuery;
2991     MSIHANDLE db;
2992
2993     db = MsiGetActiveDatabase(hPackage);
2994     rc = MsiDatabaseOpenViewA(db, ExecSeqQuery, &view);
2995     MsiCloseHandle(db);
2996
2997     if (rc != ERROR_SUCCESS)
2998         return rc;
2999
3000     rc = MsiViewExecute(view, 0);
3001     if (rc != ERROR_SUCCESS)
3002     {
3003         MsiViewClose(view);
3004         MsiCloseHandle(view);
3005         return rc;
3006     }
3007
3008     while (1)
3009     {
3010         rc = MsiViewFetch(view,&row);
3011         if (rc != ERROR_SUCCESS)
3012         {
3013             rc = ERROR_SUCCESS;
3014             break;
3015         }
3016
3017         MsiCloseHandle(row);
3018     }
3019     MsiViewClose(view);
3020     MsiCloseHandle(view);
3021     return rc;
3022 }
3023 #endif