Handle custom actions type 1 and 2.
[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
30 #include <stdarg.h>
31 #include <stdio.h>
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "wine/debug.h"
38 #include "msi.h"
39 #include "msiquery.h"
40 #include "objbase.h"
41 #include "objidl.h"
42 #include "msipriv.h"
43 #include "winnls.h"
44 #include "winuser.h"
45 #include "shlobj.h"
46 #include "wine/unicode.h"
47
48 #define CUSTOM_ACTION_TYPE_MASK 0x3F
49
50 /*
51  * These are hacks to get around the inability to write
52  * to the database and the inability to select on string values
53  * once those values are done then it will be possible to do
54  * all this inside the database.
55  */
56
57 #define MAX_PROP 1024
58
59 typedef struct {
60     WCHAR *prop_name;
61     WCHAR *prop_value;
62     } internal_property;
63
64 static internal_property PropTableHack[MAX_PROP];
65 static INT PropCount = -1;
66
67 WINE_DEFAULT_DEBUG_CHANNEL(msi);
68
69 /*
70  * Prototypes
71  */
72 UINT ACTION_PerformAction(MSIHANDLE hPackage, const WCHAR *action);
73 static UINT ACTION_CostInitialize(MSIHANDLE hPackage);
74 static UINT ACTION_CreateFolders(MSIHANDLE hPackage);
75 static UINT ACTION_CostFinalize(MSIHANDLE hPackage);
76 static UINT ACTION_InstallFiles(MSIHANDLE hPackage);
77 static UINT ACTION_DuplicateFiles(MSIHANDLE hPackage);
78 static UINT ACTION_WriteRegistryValues(MSIHANDLE hPackage);
79 static UINT ACTION_CustomAction(MSIHANDLE hPackage,const WCHAR *action);
80
81 static UINT HANDLE_CustomType1(MSIHANDLE hPackage, const LPWSTR source, 
82                                 const LPWSTR target, const INT type);
83 static UINT HANDLE_CustomType2(MSIHANDLE hPackage, const LPWSTR source, 
84                                 const LPWSTR target, const INT type);
85
86
87 static UINT set_property(MSIHANDLE hPackage, const WCHAR* prop, 
88                           const WCHAR* value);
89 UINT get_property(MSIHANDLE hPackage, const WCHAR* prop, WCHAR* value, 
90                   DWORD* size);
91 static VOID blitz_propertytable();
92 static VOID set_installer_properties(MSIHANDLE hPackage);
93 static DWORD deformat_string(MSIHANDLE hPackage, WCHAR* ptr,WCHAR** data);
94
95 /*
96  * consts and values used
97  */
98 static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
99 static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
100 static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
101
102 static const WCHAR cszlsb[]={'[',0};
103 static const WCHAR cszrsb[]={']',0};
104 static const WCHAR cszbs[]={'\\',0};
105
106
107 /******************************************************** 
108  * helper functions to get around current HACKS and such
109  ********************************************************/
110 inline static char *strdupWtoA( const WCHAR *str )
111 {
112     char *ret = NULL;
113     if (str)
114     {
115         DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL
116 );
117         if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
118             WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
119     }
120     return ret;
121 }
122
123 static VOID blitz_propertytable()
124 {
125     if (PropCount == -1)
126     {
127         PropCount = 0;
128         memset(&PropTableHack,0,sizeof(PropTableHack));
129     }
130     else if (PropCount > 0)
131     {
132         int i;
133         TRACE("Clearing %i properties\n",PropCount);
134         for (i = 0; i < PropCount; i++)
135         {
136             HeapFree(GetProcessHeap(), 0, PropTableHack[i].prop_name);
137             HeapFree(GetProcessHeap(), 0, PropTableHack[i].prop_value);
138         }
139         memset(&PropTableHack,0,sizeof(PropTableHack));
140         PropCount = 0;
141     }
142 }
143
144 UINT get_property(MSIHANDLE hPackage, const WCHAR* prop, WCHAR* value, 
145                   DWORD* size)
146 {
147     UINT rc = 1;
148     int index = 0;
149     WCHAR* pName = PropTableHack[0].prop_name;
150
151     TRACE("Looking for property %s\n",debugstr_w(prop));
152
153     /* prop table hacks take presidence */
154
155     while (pName && strcmpW(pName,prop) && index < PropCount)
156     {
157         index ++;
158         pName = PropTableHack[index].prop_name;
159     }
160
161     if (pName && index < PropCount)
162     {
163         if (*size > strlenW(PropTableHack[index].prop_value))
164         {
165             *size = strlenW(PropTableHack[index].prop_value)+1;
166             TRACE("    index %i\n", index);
167             strcpyW(value , PropTableHack[index].prop_value);
168             TRACE("    found value %s\n",debugstr_w(value));
169             return 0;
170         }
171         else
172         {
173             *size = strlenW(PropTableHack[index].prop_value);
174             return ERROR_MORE_DATA;
175         }
176     }
177
178     rc = MsiGetPropertyW(hPackage,prop,value,size);
179
180     if (rc == ERROR_SUCCESS)
181         TRACE("    found value %s\n",debugstr_w(value));
182     else
183         TRACE("    value not found\n");
184
185     return rc;
186 }
187
188 static UINT set_property(MSIHANDLE hPackage, const WCHAR* prop, 
189                           const WCHAR* value)
190 {
191     /* prop table hacks take precedence */
192     UINT rc;
193     int index = 0;
194     WCHAR* pName;
195
196     if (PropCount == -1)
197         blitz_propertytable();
198
199     pName = PropTableHack[0].prop_name;
200
201     TRACE("Setting property %s to %s\n",debugstr_w(prop),debugstr_w(value));
202
203     while (pName  && strcmpW(pName,prop) &&  index < MAX_PROP)
204     {
205         index ++;
206         pName = PropTableHack[index].prop_name;
207     }
208
209     if (pName && index < MAX_PROP)
210     {
211         TRACE("property index %i\n",index);
212         strcpyW(PropTableHack[index].prop_value,value);
213         return 0;
214     }
215     else
216     {
217         if (index >= MAX_PROP)
218         {
219             ERR("EXCEEDING MAX PROP!!!!\n");
220             return ERROR_FUNCTION_FAILED;
221         }
222         PropTableHack[index].prop_name = HeapAlloc(GetProcessHeap(),0,1024);
223         PropTableHack[index].prop_value= HeapAlloc(GetProcessHeap(),0,1024);
224         strcpyW(PropTableHack[index].prop_name,prop);
225         strcpyW(PropTableHack[index].prop_value,value);
226         PropCount++;
227         TRACE("new property index %i (%i)\n",index,PropCount);
228         return 0;
229     }
230
231     /* currently unreachable */
232     rc = MsiSetPropertyW(hPackage,prop,value);
233     return rc;
234 }
235
236 /*
237  * There are a whole slew of these we need to set
238  *
239  *
240 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp
241  */
242
243 static VOID set_installer_properties(MSIHANDLE hPackage)
244 {
245     WCHAR pth[MAX_PATH];
246
247     static const WCHAR c_col[] = 
248 {'C',':','\\',0};
249     static const WCHAR CFF[] = 
250 {'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0};
251     static const WCHAR PFF[] = 
252 {'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0};
253     static const WCHAR CADF[] = 
254 {'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
255     static const WCHAR ATF[] = 
256 {'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0};
257     static const WCHAR ADF[] = 
258 {'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
259     static const WCHAR SF[] = 
260 {'S','y','s','t','e','m','F','o','l','d','e','r',0};
261     static const WCHAR LADF[] = 
262 {'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
263     static const WCHAR MPF[] = 
264 {'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0};
265     static const WCHAR PF[] = 
266 {'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0};
267     static const WCHAR WF[] = 
268 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
269     static const WCHAR TF[]=
270 {'T','e','m','p','F','o','l','d','e','r',0};
271
272 /* Not yet set ...  but needed by iTunes
273  *
274     static const WCHAR DF[] = 
275 {'D','e','s','k','t','o','p','F','o','l','d','e','r',0};
276     static const WCHAR FF[] = 
277 {'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0};
278     static const WCHAR FoF[] = 
279 {'F','o','n','t','s','F','o','l','d','e','r',0};
280 PrimaryVolumePath
281 ProgramFiles64Folder
282 ProgramMenuFolder
283 SendToFolder
284 StartMenuFolder
285 StartupFolder
286 System16Folder
287 System64Folder
288 TemplateFolder
289  */
290
291 /* asked for by iTunes ... but are they installer set? 
292  *
293  *  GlobalAssemblyCache
294  */
295
296     set_property(hPackage, cszRootDrive, c_col);
297
298     SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth);
299     strcatW(pth,cszbs);
300     set_property(hPackage, CFF, pth);
301
302     SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth);
303     strcatW(pth,cszbs);
304     set_property(hPackage, PFF, pth);
305
306     SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth);
307     strcatW(pth,cszbs);
308     set_property(hPackage, CADF, pth);
309
310     SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth);
311     strcatW(pth,cszbs);
312     set_property(hPackage, ATF, pth);
313
314     SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth);
315     strcatW(pth,cszbs);
316     set_property(hPackage, ADF, pth);
317
318     SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth);
319     strcatW(pth,cszbs);
320     set_property(hPackage, SF, pth);
321
322     SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth);
323     strcatW(pth,cszbs);
324     set_property(hPackage, LADF, pth);
325
326     SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth);
327     strcatW(pth,cszbs);
328     set_property(hPackage, MPF, pth);
329
330     SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth);
331     strcatW(pth,cszbs);
332     set_property(hPackage, PF, pth);
333
334     SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
335     strcatW(pth,cszbs);
336     set_property(hPackage, WF, pth);
337
338     GetTempPathW(MAX_PATH,pth);
339     set_property(hPackage, TF, pth);
340 }
341
342
343 /****************************************************
344  * TOP level entry points 
345  *****************************************************/
346
347 UINT ACTION_DoTopLevelINSTALL(MSIHANDLE hPackage, LPCWSTR szPackagePath,
348                               LPCWSTR szCommandLine)
349 {
350     MSIHANDLE view;
351     UINT rc;
352     static const CHAR *ExecSeqQuery = 
353 "select * from InstallExecuteSequence where Sequence > 0 order by Sequence";
354
355     FIXME("****We do not do any of the UI level stuff yet***\n");
356
357     /* reset our properties */
358     blitz_propertytable();
359
360     if (szPackagePath)   
361     {
362         static const WCHAR OriginalDatabase[] =
363 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
364         LPWSTR p;
365         WCHAR check[MAX_PATH];
366         WCHAR pth[MAX_PATH];
367         DWORD size;
368  
369         set_property(hPackage, OriginalDatabase, szPackagePath);
370
371         strcpyW(pth,szPackagePath);
372         p = strrchrW(pth,'\\');    
373         if (p)
374         {
375             p++;
376             *p=0;
377         }
378
379         size = MAX_PATH;
380         if (get_property(hPackage,cszSourceDir,check,&size) != ERROR_SUCCESS )
381             set_property(hPackage, cszSourceDir, pth);
382     }
383
384     rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
385     
386     if (rc == ERROR_SUCCESS)
387     {
388         rc = MsiViewExecute(view, 0);
389
390         if (rc != ERROR_SUCCESS)
391         {
392             MsiViewClose(view);
393             MsiCloseHandle(view);
394             goto end;
395         }
396        
397         TRACE("Running the actions \n"); 
398
399         while (1)
400         {
401             WCHAR buffer[0x100];
402             DWORD sz = 0x100;
403             MSIHANDLE row = 0;
404
405             rc = MsiViewFetch(view,&row);
406             if (rc != ERROR_SUCCESS)
407             {
408                 rc = ERROR_SUCCESS;
409                 break;
410             }
411
412             /* check conditions */
413             if (!MsiRecordIsNull(row,2))
414             {
415                 sz=0x100;
416                 rc = MsiRecordGetStringW(row,2,buffer,&sz);
417                 if (rc != ERROR_SUCCESS)
418                 {
419                     MsiCloseHandle(row);
420                     break;
421                 }
422
423                 /* this is a hack to skip errors in the condition code */
424                 if (MsiEvaluateConditionW(hPackage, buffer) ==
425                     MSICONDITION_FALSE)
426                 {
427                     MsiCloseHandle(row);
428                     continue; 
429                 }
430
431             }
432
433             sz=0x100;
434             rc =  MsiRecordGetStringW(row,1,buffer,&sz);
435             if (rc != ERROR_SUCCESS)
436             {
437                 ERR("Error is %x\n",rc);
438                 MsiCloseHandle(row);
439                 break;
440             }
441
442             rc = ACTION_PerformAction(hPackage,buffer);
443
444             if (rc != ERROR_SUCCESS)
445             {
446                 ERR("Execution halted due to error (%i)\n",rc);
447                 MsiCloseHandle(row);
448                 break;
449             }
450
451             MsiCloseHandle(row);
452         }
453
454         MsiViewClose(view);
455         MsiCloseHandle(view);
456     }
457
458 end:
459     blitz_propertytable();
460     return rc;
461 }
462
463
464 /********************************************************
465  * ACTION helper functions and functions that perform the actions
466  *******************************************************/
467
468 /* 
469  * Alot of actions are really important even if they don't do anything
470  * explicit.. Lots of properties are set at the beginning of the installation
471  * CostFinalize does a bunch of work to translated the directorys and such
472  * 
473  * But until I get write access to the database that is hard. so I am going to
474  * hack it to see if I can get something to run.
475  */
476 UINT ACTION_PerformAction(MSIHANDLE hPackage, const WCHAR *action)
477 {
478     const static WCHAR szCreateFolders[] = 
479         {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
480     const static WCHAR szCostFinalize[] = 
481         {'C','o','s','t','F','i','n','a','l','i','z','e',0};
482     const static WCHAR szInstallFiles[] = 
483         {'I','n','s','t','a','l','l','F','i','l','e','s',0};
484     const static WCHAR szDuplicateFiles[] = 
485         {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
486     const static WCHAR szWriteRegistryValues[] = 
487 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
488     const static WCHAR szCostInitialize[] = 
489         {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
490
491     TRACE("Performing action (%s)\n",debugstr_w(action));
492
493     if (strcmpW(action,szCostInitialize)==0)
494         return ACTION_CostInitialize(hPackage);
495     if (strcmpW(action,szCreateFolders)==0)
496         return ACTION_CreateFolders(hPackage);
497     if (strcmpW(action,szCostFinalize)==0)
498         return ACTION_CostFinalize(hPackage);
499     if (strcmpW(action,szInstallFiles)==0)
500         return ACTION_InstallFiles(hPackage);
501     if (strcmpW(action,szDuplicateFiles)==0)
502         return ACTION_DuplicateFiles(hPackage);
503     if (strcmpW(action,szWriteRegistryValues)==0)
504         return ACTION_WriteRegistryValues(hPackage);
505     /*
506      Current called during itunes but unimplemented
507
508      AppSearch
509      LaunchConditions
510      FindRelatedProducts
511      CostInitialize
512      MigrateFeatureStates
513      ResolveSource  (sets SourceDir)
514      FileCost
515      ValidateProductID (sets ProductID)
516      IsolateComponents (Empty)
517      SetODBCFolders 
518      MigrateFeatureStates
519      InstallValidate 
520      RemoveExistingProducts
521      InstallInitialize
522      AllocateRegistrySpace
523      ProcessComponents
524      UnpublishComponents
525      UnpublishFeatures
526      StopServices
527      DeleteServices
528      UnregisterComPlus
529      SelfUnregModules (Empty)
530      UnregisterTypeLibraries
531      RemoveODBC
532      UnregisterFonts
533      RemoveRegistryValues
534      UnregisterClassInfo
535      UnregisterExtensionInfo
536      UnregisterProgIdInfo
537      UnregisterMIMEInfo
538      RemoveIniValues
539      RemoveShortcuts
540      RemoveEnviromentStrings
541      RemoveDuplicateFiles
542      RemoveFiles (Empty)
543      MoveFiles (Empty)
544      RemoveRegistryValues (Empty)
545      SelfRegModules (Empty)
546      RemoveFolders
547      PatchFiles
548      BindImage (Empty)
549      CreateShortcuts (would be nice to have soon)
550      RegisterClassInfo
551      RegisterExtensionInfo (Empty)
552      RegisterProgIdInfo (Lots to do)
553      RegisterMIMEInfo (Empty)
554      WriteIniValues (Empty)
555      WriteEnvironmentStrings (Empty)
556      RegisterFonts(Empty)
557      InstallODBC
558      RegisterTypeLibraries
559      SelfRegModules
560      RegisterComPlus
561      RegisterUser
562      RegisterProduct
563      PublishComponents
564      PublishFeatures
565      PublishProduct
566      InstallFinalize
567      .
568      */
569      if (ACTION_CustomAction(hPackage,action) != ERROR_SUCCESS)
570         ERR("UNHANDLED MSI ACTION %s\n",debugstr_w(action));
571
572     return ERROR_SUCCESS;
573 }
574
575
576 static UINT ACTION_CustomAction(MSIHANDLE hPackage,const WCHAR *action)
577 {
578     UINT rc;
579     MSIHANDLE view;
580     MSIHANDLE row = 0;
581     WCHAR ExecSeqQuery[1024] = 
582     {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','u','s','t','o'
583 ,'m','A','c','t','i','o','n',' ','w','h','e','r','e',' ','`','A','c','t','i'
584 ,'o','n','`',' ','=',' ','`',0};
585     static const WCHAR end[]={'`',0};
586     UINT type;
587     DWORD sz;
588     WCHAR source[0x100];
589     WCHAR target[0x200];
590     WCHAR *deformated=NULL;
591
592     strcatW(ExecSeqQuery,action);
593     strcatW(ExecSeqQuery,end);
594     rc = MsiDatabaseOpenViewW(hPackage, ExecSeqQuery, &view);
595     if (rc != ERROR_SUCCESS)
596         return rc;
597
598     rc = MsiViewExecute(view, 0);
599     if (rc != ERROR_SUCCESS)
600     {
601         MsiViewClose(view);
602         MsiCloseHandle(view);
603         return rc;
604     }
605
606     rc = MsiViewFetch(view,&row);
607     if (rc != ERROR_SUCCESS)
608     {
609         MsiViewClose(view);
610         MsiCloseHandle(view);
611         return rc;
612     }
613
614     type = MsiRecordGetInteger(row,2);
615
616     sz=0x100;
617     MsiRecordGetStringW(row,3,source,&sz);
618     sz=0x200;
619     MsiRecordGetStringW(row,4,target,&sz);
620
621     TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
622           debugstr_w(source), debugstr_w(target));
623
624     /* we are ignoring ALOT of flags and important syncornication stuff */
625     switch (type & CUSTOM_ACTION_TYPE_MASK)
626     {
627         case 1: /* DLL file stored in a Binary table stream */
628             rc = HANDLE_CustomType1(hPackage,source,target,type);
629             break;
630         case 2: /* Exe file stored in a Binary table strem */
631             rc = HANDLE_CustomType2(hPackage,source,target,type);
632             break;
633         case 35: /* Directory set with formatted text. */
634         case 51: /* Property set with formatted text. */
635             deformat_string(hPackage,target,&deformated);
636             set_property(hPackage,source,deformated);
637             HeapFree(GetProcessHeap(),0,deformated);
638             break;
639         default:
640             ERR("UNHANDLED ACTION TYPE %i (%s %s)\n",
641              type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
642              debugstr_w(target));
643     }
644
645     MsiCloseHandle(row);
646     MsiViewClose(view);
647     MsiCloseHandle(view);
648     return rc;
649 }
650
651 static UINT store_binary_to_temp(MSIHANDLE hPackage, const LPWSTR source, 
652                                 LPWSTR tmp_file)
653 {
654     static const WCHAR TF[]= {'T','e','m','p','F','o','l','d','e','r',0};
655     DWORD sz=MAX_PATH;
656
657     if (get_property(hPackage, TF,tmp_file, &sz) != ERROR_SUCCESS)
658         GetTempPathW(MAX_PATH,tmp_file);
659
660     strcatW(tmp_file,source);
661
662     if (GetFileAttributesW(tmp_file) != INVALID_FILE_ATTRIBUTES)
663     {
664         TRACE("File already exists\n");
665         return ERROR_SUCCESS;
666     }
667     else
668     {
669         /* write out the file */
670         UINT rc;
671         MSIHANDLE view;
672         MSIHANDLE row = 0;
673         WCHAR Query[1024] =
674         {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','B','i'
675 ,'n','a','r','y',' ','w','h','e','r','e',' ','N','a','m','e','=','`',0};
676         static const WCHAR end[]={'`',0};
677         HANDLE the_file;
678         CHAR buffer[1024];
679
680         the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
681                            FILE_ATTRIBUTE_NORMAL, NULL);
682     
683         if (the_file == INVALID_HANDLE_VALUE)
684             return ERROR_FUNCTION_FAILED;
685
686         strcatW(Query,source);
687         strcatW(Query,end);
688         rc = MsiDatabaseOpenViewW(hPackage, Query, &view);
689         if (rc != ERROR_SUCCESS)
690             return rc;
691
692         rc = MsiViewExecute(view, 0);
693         if (rc != ERROR_SUCCESS)
694         {
695             MsiViewClose(view);
696             MsiCloseHandle(view);
697             return rc;
698         }
699
700         rc = MsiViewFetch(view,&row);
701         if (rc != ERROR_SUCCESS)
702         {
703             MsiViewClose(view);
704             MsiCloseHandle(view);
705             return rc;
706         }
707
708         do 
709         {
710             DWORD write;
711             sz = 1024;
712             rc = MsiRecordReadStream(row,2,buffer,&sz);
713             if (rc != ERROR_SUCCESS)
714             {
715                 ERR("Failed to get stream\n");
716                 CloseHandle(the_file);  
717                 DeleteFileW(tmp_file);
718                 break;
719             }
720             WriteFile(the_file,buffer,sz,&write,NULL);
721         } while (sz == 1024);
722
723         CloseHandle(the_file);
724
725         MsiCloseHandle(row);
726         MsiViewClose(view);
727         MsiCloseHandle(view);
728     }
729
730     return ERROR_SUCCESS;
731 }
732
733
734 typedef UINT CustomEntry(MSIHANDLE);
735
736 static UINT HANDLE_CustomType1(MSIHANDLE hPackage, const LPWSTR source, 
737                                 const LPWSTR target, const INT type)
738 {
739     WCHAR tmp_file[MAX_PATH];
740     CustomEntry *fn;
741     HANDLE DLL;
742     LPSTR proc;
743
744     store_binary_to_temp(hPackage, source, tmp_file);
745
746     TRACE("Calling function %s from %s\n",debugstr_w(target),
747           debugstr_w(tmp_file));
748
749     if (type & 0xc0)
750     {
751         ERR("Asyncronious execution.. UNHANDLED\n");
752         return ERROR_SUCCESS;
753     }
754
755     DLL = LoadLibraryW(tmp_file);
756     if (DLL)
757     {
758         proc = strdupWtoA( target );
759         fn = (CustomEntry*)GetProcAddress(DLL,proc);
760         if (fn)
761         {
762             TRACE("Calling function\n");
763             fn(hPackage);
764         }
765         else
766             ERR("Cannot load functon\n");
767
768         HeapFree(GetProcessHeap(),0,proc);
769         FreeLibrary(DLL);
770     }
771     else
772         ERR("Unable to load library\n");
773
774     return ERROR_SUCCESS;
775 }
776
777 static UINT HANDLE_CustomType2(MSIHANDLE hPackage, const LPWSTR source, 
778                                 const LPWSTR target, const INT type)
779 {
780     WCHAR tmp_file[MAX_PATH*2];
781     STARTUPINFOW si;
782     PROCESS_INFORMATION info;
783     BOOL rc;
784     WCHAR *deformated;
785     static const WCHAR c_collen[] = {'C',':','\\',0};
786     static const WCHAR spc[] = {' ',0};
787
788     memset(&si,0,sizeof(STARTUPINFOW));
789     memset(&info,0,sizeof(PROCESS_INFORMATION));
790
791     store_binary_to_temp(hPackage, source, tmp_file);
792
793     strcatW(tmp_file,spc);
794     deformat_string(hPackage,target,&deformated);
795     strcatW(tmp_file,deformated);
796
797     HeapFree(GetProcessHeap(),0,deformated);
798
799     TRACE("executing exe %s \n",debugstr_w(tmp_file));
800
801     rc = CreateProcessW(NULL, tmp_file, NULL, NULL, FALSE, 0, NULL,
802                   c_collen, &si, &info);
803
804     if ( !rc )
805     {
806         ERR("Unable to execute command\n");
807         return ERROR_SUCCESS;
808     }
809
810     if (!(type & 0xc0))
811         WaitForSingleObject(info.hProcess,INFINITE);
812
813     return ERROR_SUCCESS;
814 }
815
816 /***********************************************************************
817  *            create_full_pathW
818  *
819  * Recursively create all directories in the path.
820  *
821  * shamelessly stolen from setupapi/queue.c
822  */
823 static BOOL create_full_pathW(const WCHAR *path)
824 {
825     BOOL ret = TRUE;
826     int len;
827     WCHAR *new_path;
828
829     new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) *
830 sizeof(WCHAR));
831     strcpyW(new_path, path);
832
833     while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
834     new_path[len - 1] = 0;
835
836     while(!CreateDirectoryW(new_path, NULL))
837     {
838     WCHAR *slash;
839     DWORD last_error = GetLastError();
840     if(last_error == ERROR_ALREADY_EXISTS)
841         break;
842
843     if(last_error != ERROR_PATH_NOT_FOUND)
844     {
845         ret = FALSE;
846         break;
847     }
848
849     if(!(slash = strrchrW(new_path, '\\')))
850     {
851         ret = FALSE;
852         break;
853     }
854
855     len = slash - new_path;
856     new_path[len] = 0;
857     if(!create_full_pathW(new_path))
858     {
859         ret = FALSE;
860         break;
861     }
862     new_path[len] = '\\';
863     }
864
865     HeapFree(GetProcessHeap(), 0, new_path);
866     return ret;
867 }
868
869 /*
870  * Also we cannot enable/disable components either, so for now I am just going 
871  * to do all the directorys for all the components.
872  */
873 static UINT ACTION_CreateFolders(MSIHANDLE hPackage)
874 {
875     static const CHAR *ExecSeqQuery = "select * from CreateFolder";
876     UINT rc;
877     MSIHANDLE view;
878
879     rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
880     if (rc != ERROR_SUCCESS)
881         return rc;
882
883     rc = MsiViewExecute(view, 0);
884     if (rc != ERROR_SUCCESS)
885     {
886         MsiViewClose(view);
887         MsiCloseHandle(view);
888         return rc;
889     }
890     
891     while (1)
892     {
893         WCHAR dir[0x100];
894         WCHAR full_path[MAX_PATH];
895         DWORD sz;
896         MSIHANDLE row = 0;
897
898         rc = MsiViewFetch(view,&row);
899         if (rc != ERROR_SUCCESS)
900         {
901             rc = ERROR_SUCCESS;
902             break;
903         }
904
905         sz=0x100;
906         rc = MsiRecordGetStringW(row,1,dir,&sz);
907
908         if (rc!= ERROR_SUCCESS)
909         {
910             ERR("Unable to get folder id \n");
911             MsiCloseHandle(row);
912             continue;
913         }
914
915         sz = MAX_PATH;
916         rc = get_property(hPackage, dir,full_path,&sz);
917
918         if (rc != ERROR_SUCCESS)
919         {
920             ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
921             MsiCloseHandle(row);
922             continue;
923         }
924
925         TRACE("Folder is %s\n",debugstr_w(full_path));
926         create_full_pathW(full_path);
927
928         MsiCloseHandle(row);
929     }
930     MsiViewClose(view);
931     MsiCloseHandle(view);
932    
933     return rc;
934 }
935
936 /*
937  * Workhorse function for creating the directories
938  * during Costing
939  */
940 static UINT resolve_directory(MSIHANDLE hPackage, const WCHAR* dir, 
941                            WCHAR* path, BOOL source)
942 {
943     static const WCHAR cszsrc[]={'_','S','o','u','r','c','e',0};
944     static const WCHAR cszsrcroot[]=
945         {'[','S','o','u','r','c','e','D','i','r',']',0};
946
947     WCHAR Query[1024] = 
948 {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','D','i','r','e','c',
949 't','o','r','y',' ','w','h','e','r','e',' ','`','D','i','r','e','c','t',
950 'o','r','y','`',' ','=',' ','`',0};
951     static const WCHAR end[]={'`',0};
952     UINT rc;
953     MSIHANDLE view;
954     WCHAR targetbuffer[0x100];
955     WCHAR *srcdir = NULL;
956     WCHAR *targetdir = NULL;
957     WCHAR buffer[0x100];
958     WCHAR parent[0x100];
959     WCHAR parent_path[MAX_PATH];
960     DWORD sz=0x100;
961     MSIHANDLE row = 0;
962     WCHAR full_path[MAX_PATH];
963     WCHAR name_source[0x100];
964
965     sz = MAX_PATH; 
966     if (get_property(hPackage,dir,path,&sz)==ERROR_SUCCESS)
967         return ERROR_SUCCESS;
968
969     TRACE("Working to resolve %s\n",debugstr_w(dir));
970
971     /* special case... root drive */       
972     if (strcmpW(dir,cszTargetDir)==0)
973     {
974         if (!source)
975         {
976             sz = 0x100;
977             if(!get_property(hPackage,cszRootDrive,buffer,&sz))
978             {
979                 set_property(hPackage,cszTargetDir,buffer);
980                 strcpyW(path,buffer);
981             }
982             else
983             {
984                 ERR("No RootDrive property defined disaster!\n");
985                 MsiCloseHandle(row);
986                 MsiViewClose(view);
987                 MsiCloseHandle(view);
988                 return ERROR_FUNCTION_FAILED;
989             }
990         }
991         else
992             strcpyW(path,cszsrcroot);
993
994         return ERROR_SUCCESS;
995     }
996
997     strcatW(Query,dir);
998     strcatW(Query,end);
999     rc = MsiDatabaseOpenViewW(hPackage, Query, &view);
1000     if (rc != ERROR_SUCCESS)
1001         return rc;
1002
1003     rc = MsiViewExecute(view, 0);
1004     if (rc != ERROR_SUCCESS)
1005     {
1006         MsiViewClose(view);
1007         MsiCloseHandle(view);
1008         return rc;
1009     }
1010
1011     rc = MsiViewFetch(view,&row);
1012     if (rc != ERROR_SUCCESS)
1013     {
1014         MsiViewClose(view);
1015         MsiCloseHandle(view);
1016         return rc;
1017     }
1018
1019     sz=0x100;
1020     MsiRecordGetStringW(row,3,targetbuffer,&sz);
1021     targetdir=targetbuffer;
1022
1023     /* split src and target dir */
1024     if (strchrW(targetdir,':'))
1025     {
1026         srcdir=strchrW(targetdir,':');
1027         *srcdir=0;
1028         srcdir ++;
1029     }
1030     else
1031         srcdir=NULL;
1032
1033     /* for now only pick long filename versions */
1034     if (strchrW(targetdir,'|'))
1035     {
1036         targetdir = strchrW(targetdir,'|'); 
1037         *targetdir = 0;
1038         targetdir ++;
1039     }
1040     if (srcdir && strchrW(srcdir,'|'))
1041     {
1042         srcdir= strchrW(srcdir,'|'); 
1043         *srcdir= 0;
1044         srcdir ++;
1045     }
1046
1047     /* now check for root dirs */
1048     if (targetdir[0] == '.' && targetdir[1] == 0)
1049         targetdir = NULL;
1050         
1051     if (srcdir && srcdir[0] == '.' && srcdir[1] == 0)
1052         srcdir = NULL;
1053
1054     if (MsiRecordIsNull(row,2))
1055         parent[0]=0;
1056     else
1057     {
1058             sz=0x100;
1059             MsiRecordGetStringW(row,2,parent,&sz);
1060     }
1061
1062     if (parent[0]) 
1063     {
1064         resolve_directory(hPackage,parent,parent_path,FALSE);
1065         strcpyW(full_path,parent_path);
1066         if (targetdir)
1067         {
1068             strcatW(full_path,targetdir);
1069             strcatW(full_path,cszbs);
1070         }
1071         set_property(hPackage,dir,full_path);
1072         if (!source)
1073             strcpyW(path,full_path);
1074
1075         resolve_directory(hPackage,parent,parent_path,TRUE);
1076         strcpyW(full_path,parent_path);
1077         if (srcdir)
1078         {
1079             strcatW(full_path,srcdir);
1080             strcatW(full_path,cszbs); 
1081         }
1082         else if (targetdir)
1083         {
1084             strcatW(full_path,targetdir);
1085             strcatW(full_path,cszbs);
1086         }
1087         
1088         strcpyW(name_source,dir);
1089         strcatW(name_source,cszsrc);
1090         set_property(hPackage,name_source,full_path);
1091         if (source)
1092             strcpyW(path,full_path);
1093     }
1094
1095     MsiCloseHandle(row);
1096     MsiViewClose(view);
1097     MsiCloseHandle(view);
1098     return rc;
1099 }
1100
1101 /*
1102  * This is the action where all the features and components are loaded into
1103  * memory... so when we start doing that that will be important.
1104  * 
1105  */
1106 static UINT ACTION_CostInitialize(MSIHANDLE hPackage)
1107 {
1108     return ERROR_SUCCESS;
1109 }
1110
1111 /* 
1112  * Alot is done in this function aside from just the costing.
1113  * The costing needs to be implemented at some point but for now i am going
1114  * to focus on the directory building
1115  *
1116  * Note about directory names: I know that directory names get processed into 
1117  * properties.  I am still very unclear where the name_source
1118  * part is used but I am preserving it just as a precaution
1119  */
1120 static UINT ACTION_CostFinalize(MSIHANDLE hPackage)
1121 {
1122     static const CHAR *ExecSeqQuery = "select * from Directory";
1123     UINT rc;
1124     MSIHANDLE view;
1125
1126     /* According to MSDN these properties are set when CostFinalize is run  
1127      * or MsiSetInstallLevel is called */
1128     TRACE("Setting installer properties\n");
1129     set_installer_properties(hPackage);    
1130
1131     TRACE("Building Directory properties\n");
1132
1133     rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
1134     if (rc != ERROR_SUCCESS)
1135         return rc;
1136
1137     rc = MsiViewExecute(view, 0);
1138     if (rc != ERROR_SUCCESS)
1139     {
1140         MsiViewClose(view);
1141         MsiCloseHandle(view);
1142         return rc;
1143     }
1144     
1145     while (1)
1146     {
1147         WCHAR name[0x100];
1148         WCHAR path[MAX_PATH];
1149         MSIHANDLE row = 0;
1150         DWORD sz;
1151
1152         rc = MsiViewFetch(view,&row);
1153
1154         if (rc != ERROR_SUCCESS)
1155         {
1156             rc = ERROR_SUCCESS;
1157             break;
1158         }
1159
1160         sz=0x100;
1161         MsiRecordGetStringW(row,1,name,&sz);
1162
1163         /* This helper function now does ALL the work */
1164         TRACE("Dir %s ...\n",debugstr_w(name));
1165         resolve_directory(hPackage,name,path,FALSE);
1166         TRACE("resolves to %s\n",debugstr_w(path));
1167
1168         MsiCloseHandle(row);
1169      }
1170     MsiViewClose(view);
1171     MsiCloseHandle(view);
1172
1173     return ERROR_SUCCESS;
1174 }
1175
1176 /*
1177  * This is a helper function for handling embedded cabinet media
1178  */
1179 static UINT writeout_cabinet_stream(MSIHANDLE hPackage, WCHAR* stream_name,
1180                                     WCHAR* source)
1181 {
1182     UINT rc;
1183     USHORT* data;
1184     UINT    size;
1185     DWORD   write;
1186     HANDLE  the_file;
1187
1188     rc = read_raw_stream_data(hPackage,stream_name,&data,&size); 
1189
1190     if (rc != ERROR_SUCCESS)
1191         return rc;
1192
1193     write = 0x100;
1194     if (get_property(hPackage, cszSourceDir, source, &write))
1195     {
1196         ERR("No Source dir defined \n");
1197         rc = ERROR_FUNCTION_FAILED;
1198         goto end; 
1199     }
1200
1201     strcatW(source,stream_name);
1202     the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1203                            FILE_ATTRIBUTE_NORMAL, NULL);
1204
1205     if (the_file == INVALID_HANDLE_VALUE)
1206     {
1207         rc = ERROR_FUNCTION_FAILED;
1208         goto end;
1209     }
1210
1211     WriteFile(the_file,data,size,&write,NULL);
1212     CloseHandle(the_file);
1213     TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
1214 end:
1215     HeapFree(GetProcessHeap(),0,data);
1216     return rc;
1217 }
1218
1219
1220 /***********************************************************************
1221  *            extract_cabinet_file
1222  *
1223  * Extract a file from a .cab file.
1224  */
1225 static BOOL extract_cabinet_file( const WCHAR *cabinet, const WCHAR *root)
1226                                   
1227 {
1228     static const WCHAR extW[] = {'.','c','a','b',0};
1229
1230     /* from cabinet.h */
1231     typedef struct {
1232         long  result1;          /* 0x000 */
1233         long  unknown1[3];      /* 0x004 */
1234         void* filelist;         /* 0x010 */
1235         long  filecount;        /* 0x014 */
1236         long  unknown2;         /* 0x018 */
1237         char  directory[0x104]; /* 0x01c */
1238         char  lastfile[0x20c];  /* 0x120 */
1239     } EXTRACTdest;
1240     extern HRESULT Extract(EXTRACTdest*, LPCSTR);
1241
1242     char *cab_path, *src_path;
1243     int len = strlenW( cabinet );
1244     EXTRACTdest exd;
1245
1246     /* make sure the cabinet file has a .cab extension */
1247     if (len <= 4 || strcmpiW( cabinet + len - 4, extW )) return FALSE;
1248
1249     if (!(cab_path = strdupWtoA( cabinet ))) return FALSE;
1250     if (!(src_path = strdupWtoA( root ))) return FALSE;
1251
1252     memset(&exd,0,sizeof(exd));
1253     strcpy(exd.directory,src_path);
1254     Extract(&exd,cab_path);
1255     HeapFree( GetProcessHeap(), 0, cab_path );
1256     HeapFree( GetProcessHeap(), 0, src_path );
1257     return TRUE;
1258 }
1259
1260 static UINT ready_media_for_file(MSIHANDLE hPackage, UINT sequence, 
1261                                  WCHAR* path)
1262 {
1263     UINT rc;
1264     MSIHANDLE view;
1265     MSIHANDLE row = 0;
1266     WCHAR source[MAX_PATH];
1267     static const CHAR *ExecSeqQuery = 
1268         "select * from Media where LastSequence > %i order by LastSequence";
1269     CHAR Query[1024];
1270     WCHAR cab[0x100];
1271     DWORD sz=0x100;
1272     INT seq;
1273     static INT last_sequence = 0; 
1274
1275     if (sequence <= last_sequence)
1276     {
1277         TRACE("Media already ready (%i, %i)\n",sequence,last_sequence);
1278         return ERROR_SUCCESS;
1279     }
1280
1281     sprintf(Query,ExecSeqQuery,sequence);
1282
1283     rc = MsiDatabaseOpenViewA(hPackage, Query, &view);
1284     if (rc != ERROR_SUCCESS)
1285         return rc;
1286
1287     rc = MsiViewExecute(view, 0);
1288     if (rc != ERROR_SUCCESS)
1289     {
1290         MsiViewClose(view);
1291         MsiCloseHandle(view);
1292         return rc;
1293     }
1294
1295     rc = MsiViewFetch(view,&row);
1296     if (rc != ERROR_SUCCESS)
1297     {
1298         MsiViewClose(view);
1299         MsiCloseHandle(view);
1300         return rc;
1301     }
1302     seq = MsiRecordGetInteger(row,2);
1303     last_sequence = seq;
1304
1305     if (!MsiRecordIsNull(row,4))
1306     {
1307         sz=0x100;
1308         MsiRecordGetStringW(row,4,cab,&sz);
1309         /* the stream does not contain the # character */
1310         if (cab[0]=='#')
1311         {
1312             writeout_cabinet_stream(hPackage,&cab[1],source);
1313             strcpyW(path,source);
1314             *(strrchrW(path,'\\')+1)=0;
1315         }
1316         else
1317         {
1318             sz = 0x100;
1319             if (get_property(hPackage, cszSourceDir, source, &sz))
1320             {
1321                 ERR("No Source dir defined \n");
1322                 rc = ERROR_FUNCTION_FAILED;
1323             }
1324             else
1325             {
1326                 strcpyW(path,source);
1327                 strcatW(source,cab);
1328             }
1329         }
1330         rc = !extract_cabinet_file(source,path);
1331     }
1332     MsiCloseHandle(row);
1333     MsiViewClose(view);
1334     MsiCloseHandle(view);
1335     return rc;
1336 }
1337
1338 static void reduce_to_longfilename(WCHAR* filename)
1339 {
1340     if (strchrW(filename,'|'))
1341     {
1342         WCHAR newname[MAX_PATH];
1343         strcpyW(newname,strchrW(filename,'|')+1);
1344         strcpyW(filename,newname);
1345     }
1346 }
1347
1348 static UINT get_directory_for_component(MSIHANDLE hPackage, 
1349     const WCHAR* component, WCHAR* install_path)
1350 {
1351     UINT rc;
1352     MSIHANDLE view;
1353     MSIHANDLE row = 0;
1354     WCHAR ExecSeqQuery[1023] = 
1355 {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','o','m'
1356 ,'p','o','n','e','n','t',' ','w','h','e','r','e',' ','C','o','m'
1357 ,'p','o','n','e','n','t',' ','=',' ','`',0};
1358     static const WCHAR end[]={'`',0};
1359     WCHAR dir[0x100];
1360     DWORD sz=0x100;
1361
1362     strcatW(ExecSeqQuery,component);
1363     strcatW(ExecSeqQuery,end);
1364
1365     rc = MsiDatabaseOpenViewW(hPackage, ExecSeqQuery, &view);
1366
1367     if (rc != ERROR_SUCCESS)
1368         return rc;
1369
1370     rc = MsiViewExecute(view, 0);
1371     if (rc != ERROR_SUCCESS)
1372     {
1373         MsiViewClose(view);
1374         MsiCloseHandle(view);
1375         return rc;
1376     }
1377
1378     rc = MsiViewFetch(view,&row);
1379     if (rc != ERROR_SUCCESS)
1380     {
1381         MsiViewClose(view);
1382         MsiCloseHandle(view);
1383         MsiCloseHandle(row);
1384         return rc;
1385     }
1386
1387     sz=0x100;
1388     MsiRecordGetStringW(row,3,dir,&sz);
1389     sz=MAX_PATH;
1390     rc = get_property(hPackage, dir, install_path, &sz);
1391
1392     MsiCloseHandle(row);
1393     MsiViewClose(view);
1394     MsiCloseHandle(view);
1395     return rc;
1396 }
1397
1398 static UINT ACTION_InstallFiles(MSIHANDLE hPackage)
1399 {
1400     UINT rc;
1401     MSIHANDLE view;
1402     MSIHANDLE row = 0;
1403     static const CHAR *ExecSeqQuery = 
1404         "select * from File order by Sequence";
1405
1406     /* REALLY what we want to do is go through all the enabled
1407      * features and check all the components of that feature and
1408      * make sure that component is not already install and blah
1409      * blah blah... I will do it that way some day.. really
1410      * but for sheer gratification I am going to just brute force
1411      * install all the files
1412      */
1413
1414     rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
1415     if (rc != ERROR_SUCCESS)
1416         return rc;
1417
1418     rc = MsiViewExecute(view, 0);
1419     if (rc != ERROR_SUCCESS)
1420     {
1421         MsiViewClose(view);
1422         MsiCloseHandle(view);
1423         return rc;
1424     }
1425
1426     while (1)
1427     {
1428         INT seq = 0;
1429         WCHAR component[0x100];
1430         WCHAR install_path[MAX_PATH]; 
1431         WCHAR path_to_source[MAX_PATH];
1432         WCHAR src_path[MAX_PATH];
1433         WCHAR filename[0x100]; 
1434         WCHAR sourcename[0x100]; 
1435         DWORD sz=0x100;
1436
1437         rc = MsiViewFetch(view,&row);
1438         if (rc != ERROR_SUCCESS)
1439         {
1440             rc = ERROR_SUCCESS;
1441             break;
1442         }
1443
1444         seq = MsiRecordGetInteger(row,8);
1445         rc = ready_media_for_file(hPackage,seq,path_to_source);
1446         if (rc != ERROR_SUCCESS)
1447         {
1448             ERR("Unable to ready media\n");
1449             MsiCloseHandle(row);
1450             break;
1451         }
1452         sz=0x100;
1453         rc = MsiRecordGetStringW(row,2,component,&sz);
1454         if (rc != ERROR_SUCCESS)
1455         {
1456             ERR("Unable to read component\n");
1457             MsiCloseHandle(row);
1458             break;
1459         }
1460         rc = get_directory_for_component(hPackage,component,install_path);
1461         if (rc != ERROR_SUCCESS)
1462         {
1463             ERR("Unable to get directory\n");
1464             MsiCloseHandle(row);
1465             break;
1466         }
1467
1468         sz=0x100;
1469         rc = MsiRecordGetStringW(row,1,sourcename,&sz);
1470         if (rc != ERROR_SUCCESS)
1471         {
1472             ERR("Unable to get sourcename\n");
1473             MsiCloseHandle(row);
1474             break;
1475         }
1476         strcpyW(src_path,path_to_source);
1477         strcatW(src_path,sourcename);
1478
1479         sz=0x100;
1480         rc = MsiRecordGetStringW(row,3,filename,&sz);
1481         if (rc != ERROR_SUCCESS)
1482         {
1483             ERR("Unable to get filename\n");
1484             MsiCloseHandle(row);
1485             break;
1486         }
1487         reduce_to_longfilename(filename);
1488         strcatW(install_path,filename);
1489
1490         TRACE("Installing file %s to %s\n",debugstr_w(src_path),
1491               debugstr_w(install_path));
1492
1493         rc = !MoveFileW(src_path,install_path);
1494         if (rc)
1495         {
1496             ERR("Unable to move file\n");
1497         }
1498
1499         /* for future use lets keep track of this file and where it went */
1500         set_property(hPackage,sourcename,install_path);
1501
1502         MsiCloseHandle(row);
1503     }
1504     MsiViewClose(view);
1505     MsiCloseHandle(view);
1506
1507     return rc;
1508 }
1509
1510 static UINT ACTION_DuplicateFiles(MSIHANDLE hPackage)
1511 {
1512     UINT rc;
1513     MSIHANDLE view;
1514     MSIHANDLE row = 0;
1515     static const CHAR *ExecSeqQuery = "select * from DuplicateFile";
1516
1517
1518     /*
1519      * Yes we should only do this for componenets that are installed
1520      * but again I need to do that went I track components.
1521      */
1522
1523     rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
1524     if (rc != ERROR_SUCCESS)
1525         return rc;
1526
1527     rc = MsiViewExecute(view, 0);
1528     if (rc != ERROR_SUCCESS)
1529     {
1530         MsiViewClose(view);
1531         MsiCloseHandle(view);
1532         return rc;
1533     }
1534
1535     while (1)
1536     {
1537         WCHAR file_key[0x100];
1538         WCHAR file_source[MAX_PATH];
1539         WCHAR dest_name[0x100];
1540         WCHAR dest_path[MAX_PATH];
1541
1542         DWORD sz=0x100;
1543
1544         rc = MsiViewFetch(view,&row);
1545         if (rc != ERROR_SUCCESS)
1546         {
1547             rc = ERROR_SUCCESS;
1548             break;
1549         }
1550
1551         sz=0x100;
1552         rc = MsiRecordGetStringW(row,3,file_key,&sz);
1553         if (rc != ERROR_SUCCESS)
1554         {
1555             ERR("Unable to get file key\n");
1556             MsiCloseHandle(row);
1557             break;
1558         }
1559
1560         sz = 0x100;
1561         rc = get_property(hPackage,file_key,file_source,&sz);
1562         if (rc != ERROR_SUCCESS)
1563         {
1564             ERR("Original file unknown %s\n",debugstr_w(file_key));
1565             MsiCloseHandle(row);
1566             break;
1567         }
1568
1569         if (MsiRecordIsNull(row,4))
1570             strcpyW(dest_name,strrchrW(file_source,'\\')+1);
1571         else
1572         {
1573             sz=0x100;
1574             MsiRecordGetStringW(row,4,dest_name,&sz);
1575             reduce_to_longfilename(dest_name);
1576          }
1577
1578         if (MsiRecordIsNull(row,5))
1579         {
1580             strcpyW(dest_path,file_source);
1581             *strrchrW(dest_path,'\\')=0;
1582         }
1583         else
1584         {
1585             WCHAR destkey[0x100];
1586             sz=0x100;
1587             MsiRecordGetStringW(row,5,destkey,&sz);
1588             sz = 0x100;
1589             rc = get_property(hPackage, destkey, dest_path, &sz);
1590             if (rc != ERROR_SUCCESS)
1591             {
1592                 ERR("Unable to get destination folder\n");
1593                 MsiCloseHandle(row);
1594                 break;
1595             }
1596         }
1597
1598         strcatW(dest_path,dest_name);
1599            
1600         TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
1601               debugstr_w(dest_path)); 
1602         
1603         if (strcmpW(file_source,dest_path))
1604             rc = !CopyFileW(file_source,dest_path,TRUE);
1605         else
1606             rc = ERROR_SUCCESS;
1607         
1608         if (rc != ERROR_SUCCESS)
1609             ERR("Failed to copy file\n");
1610     
1611         MsiCloseHandle(row);
1612     }
1613     MsiViewClose(view);
1614     MsiCloseHandle(view);
1615     return rc;
1616
1617 }
1618
1619
1620 static LPSTR parse_value(MSIHANDLE hPackage, WCHAR *value, DWORD *type, 
1621                          DWORD *size)
1622 {
1623     LPSTR data = NULL;
1624     if (value[0]=='#' && value[1]!='#')
1625     {
1626         ERR("UNHANDLED VALUE TYPE\n"); 
1627     }
1628     else
1629     {
1630         WCHAR *ptr;
1631         if (value[0]=='#')
1632             ptr = &value[1];
1633         else
1634             ptr=value;
1635
1636         *type=REG_SZ;
1637         *size = deformat_string(hPackage, ptr,(LPWSTR*)&data);
1638     }
1639     return data;
1640 }
1641
1642 static UINT ACTION_WriteRegistryValues(MSIHANDLE hPackage)
1643 {
1644     UINT rc;
1645     MSIHANDLE view;
1646     MSIHANDLE row = 0;
1647     static const CHAR *ExecSeqQuery = "select * from Registry";
1648
1649     /* Again here we want to key off of the components being installed...
1650      * oh well
1651      */
1652     
1653     rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
1654     if (rc != ERROR_SUCCESS)
1655         return rc;
1656
1657     rc = MsiViewExecute(view, 0);
1658     if (rc != ERROR_SUCCESS)
1659     {
1660         MsiViewClose(view);
1661         MsiCloseHandle(view);
1662         return rc;
1663     }
1664
1665     while (1)
1666     {
1667         WCHAR key[0x100];
1668         WCHAR name[0x100];
1669         WCHAR value[0x100];
1670         LPSTR value_data = NULL;
1671         HKEY  root_key, hkey;
1672         DWORD type,size;
1673
1674         INT   root;
1675         DWORD sz=0x100;
1676
1677         rc = MsiViewFetch(view,&row);
1678         if (rc != ERROR_SUCCESS)
1679         {
1680             rc = ERROR_SUCCESS;
1681             break;
1682         }
1683
1684         /* null values have special meanings during uninstalls and such */
1685         
1686         if(MsiRecordIsNull(row,5))
1687         {
1688             MsiCloseHandle(row);
1689             continue;
1690         }
1691
1692         root = MsiRecordGetInteger(row,2);
1693         sz = 0x100;
1694         MsiRecordGetStringW(row,3,key,&sz);
1695       
1696         sz = 0x100; 
1697         if (MsiRecordIsNull(row,4))
1698             name[0]=0;
1699         else
1700             MsiRecordGetStringW(row,4,name,&sz);
1701    
1702         sz=0x100; 
1703         MsiRecordGetStringW(row,5,value,&sz);
1704
1705
1706         /* get the root key */
1707         switch (root)
1708         {
1709             case 0:  root_key = HKEY_CLASSES_ROOT; break;
1710             case 1:  root_key = HKEY_CURRENT_USER; break;
1711             case 2:  root_key = HKEY_LOCAL_MACHINE; break;
1712             case 3:  root_key = HKEY_USERS; break;
1713             default:
1714                  ERR("Unknown root %i\n",root);
1715                  root_key=NULL;
1716                  break;
1717         }
1718         if (!root_key)
1719         {
1720             MsiCloseHandle(row);
1721             continue;
1722         }
1723
1724         if (RegCreateKeyW( root_key, key, &hkey))
1725         {
1726             ERR("Could not create key %s\n",debugstr_w(key));
1727             MsiCloseHandle(row);
1728             continue;
1729          }
1730       
1731         value_data = parse_value(hPackage, value, &type, &size); 
1732
1733         if (value_data)
1734         {
1735             TRACE("Setting value %s\n",debugstr_w(name));
1736             RegSetValueExW(hkey, name, 0, type, value_data, size);
1737             HeapFree(GetProcessHeap(),0,value_data);
1738         }
1739
1740         MsiCloseHandle(row);
1741     }
1742     MsiViewClose(view);
1743     MsiCloseHandle(view);
1744     return rc;
1745 }
1746
1747 /*
1748  * This helper function should probably go alot of places
1749  */
1750 static DWORD deformat_string(MSIHANDLE hPackage, WCHAR* ptr,WCHAR** data)
1751 {
1752     WCHAR* mark=NULL;
1753     DWORD size=0;
1754     DWORD chunk=0;
1755     WCHAR key[0x100];
1756     WCHAR value[0x100];
1757     DWORD sz;
1758
1759     /* scan for special characters */
1760     if (!strchrW(ptr,'[') || (strchrW(ptr,'[') && !strchrW(ptr,']')))
1761     {
1762         /* not formatted */
1763         size = (strlenW(ptr)+1) * sizeof(WCHAR);
1764         *data = HeapAlloc(GetProcessHeap(),0,size);
1765         strcpyW(*data,ptr);
1766         return size;
1767     }
1768    
1769     /* formatted string located */ 
1770     mark = strchrW(ptr,'[');
1771     if (mark != ptr)
1772     {
1773         INT cnt = (mark - ptr);
1774         TRACE("%i  (%i) characters before marker\n",cnt,(mark-ptr));
1775         size = cnt * sizeof(WCHAR);
1776         size += sizeof(WCHAR);
1777         *data = HeapAlloc(GetProcessHeap(),0,size);
1778         strncpyW(*data,ptr,cnt);
1779         (*data)[cnt]=0;
1780     }
1781     else
1782     {
1783         size = sizeof(WCHAR);
1784         *data = HeapAlloc(GetProcessHeap(),0,size);
1785         (*data)[0]=0;
1786     }
1787     mark++;
1788     strcpyW(key,mark);
1789     *strchrW(key,']')=0;
1790     mark = strchrW(mark,']');
1791     mark++;
1792     TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
1793     sz = 0x100;
1794     if (get_property(hPackage, key, value,&sz) == ERROR_SUCCESS)
1795     {
1796         LPWSTR newdata;
1797         chunk = (strlenW(value)+1) * sizeof(WCHAR);
1798         size+=chunk;   
1799         newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
1800         *data = newdata;
1801         strcatW(*data,value);
1802     }
1803     TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
1804     if (*mark!=0)
1805     {
1806         LPWSTR newdata;
1807         chunk = (strlenW(mark)+1) * sizeof(WCHAR);
1808         size+=chunk;
1809         newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
1810         *data = newdata;
1811         strcatW(*data,mark);
1812     }
1813     (*data)[strlenW(*data)]=0;
1814     TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
1815
1816     /* recursivly do this to clean up */
1817     mark = HeapAlloc(GetProcessHeap(),0,size);
1818     strcpyW(mark,*data);
1819     TRACE("String at this point %s\n",debugstr_w(mark));
1820     size = deformat_string(hPackage,mark,data);
1821     HeapFree(GetProcessHeap(),0,mark);
1822     return size;
1823 }
1824
1825 /* Msi functions that seem approperate here */
1826 UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
1827 {
1828     LPWSTR szwAction;
1829     UINT len,rc;
1830
1831     TRACE(" exteral attempt at action %s\n",szAction);
1832
1833     if (!szAction)
1834         return ERROR_FUNCTION_FAILED;
1835     if (hInstall == 0)
1836         return ERROR_FUNCTION_FAILED;
1837
1838     len = MultiByteToWideChar( CP_ACP, 0, szAction, -1, NULL, 0);
1839     szwAction = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
1840
1841     if (!szwAction)
1842         return ERROR_FUNCTION_FAILED; 
1843
1844     MultiByteToWideChar( CP_ACP, 0, szAction, -1, szwAction, len);
1845
1846     rc = MsiDoActionW(hInstall, szwAction);
1847     HeapFree(GetProcessHeap(),0,szwAction);
1848     return rc;
1849 }
1850
1851 UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction )
1852 {
1853     TRACE(" exteral attempt at action %s \n",debugstr_w(szAction));
1854     return ACTION_PerformAction(hInstall,szAction);
1855 }
1856
1857 UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder, 
1858                                LPSTR szPathBuf, DWORD* pcchPathBuf) 
1859 {
1860     LPWSTR szwFolder;
1861     LPWSTR szwPathBuf;
1862     UINT len,rc;
1863
1864     TRACE("getting folder %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
1865
1866     if (!szFolder)
1867         return ERROR_FUNCTION_FAILED;
1868     if (hInstall == 0)
1869         return ERROR_FUNCTION_FAILED;
1870
1871     len = MultiByteToWideChar( CP_ACP, 0, szFolder, -1, NULL, 0);
1872     szwFolder= HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
1873
1874     if (!szwFolder)
1875         return ERROR_FUNCTION_FAILED; 
1876
1877     szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
1878
1879     MultiByteToWideChar( CP_ACP, 0, szFolder, -1, szwFolder, len);
1880
1881     rc = MsiGetTargetPathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
1882
1883     WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
1884                          *pcchPathBuf, NULL, NULL );
1885
1886     HeapFree(GetProcessHeap(),0,szwFolder);
1887     HeapFree(GetProcessHeap(),0,szwPathBuf);
1888
1889     return rc;
1890 }
1891
1892 UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
1893                                 szPathBuf, DWORD* pcchPathBuf) 
1894 {
1895     TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
1896     return get_property(hInstall,szFolder,szPathBuf,pcchPathBuf);
1897 }
1898
1899
1900 #if 0
1901 static UINT ACTION_Template(MSIHANDLE hPackage)
1902 {
1903     UINT rc;
1904     MSIHANDLE view;
1905     MSIHANDLE row = 0;
1906     static const CHAR *ExecSeqQuery;
1907
1908     rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
1909     if (rc != ERROR_SUCCESS)
1910         return rc;
1911
1912     rc = MsiViewExecute(view, 0);
1913     if (rc != ERROR_SUCCESS)
1914     {
1915         MsiViewClose(view);
1916         MsiCloseHandle(view);
1917         return rc;
1918     }
1919
1920     while (1)
1921     {
1922         rc = MsiViewFetch(view,&row);
1923         if (rc != ERROR_SUCCESS)
1924         {
1925             rc = ERROR_SUCCESS;
1926             break;
1927         }
1928
1929         MsiCloseHandle(row);
1930     }
1931     MsiViewClose(view);
1932     MsiCloseHandle(view);
1933     return rc;
1934 }
1935 #endif