- CustomAction 35 should call SetTargetPath not just set the property.
[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 #define COBJMACROS
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winerror.h"
37 #include "winreg.h"
38 #include "wine/debug.h"
39 #include "fdi.h"
40 #include "msi.h"
41 #include "msiquery.h"
42 #include "msvcrt/fcntl.h"
43 #include "objbase.h"
44 #include "objidl.h"
45 #include "msipriv.h"
46 #include "winnls.h"
47 #include "winuser.h"
48 #include "shlobj.h"
49 #include "wine/unicode.h"
50 #include "ver.h"
51
52 #define CUSTOM_ACTION_TYPE_MASK 0x3F
53 #define REG_PROGRESS_VALUE 13200
54 #define COMPONENT_PROGRESS_VALUE 24000
55
56 WINE_DEFAULT_DEBUG_CHANNEL(msi);
57
58 typedef struct tagMSIFEATURE
59 {
60     WCHAR Feature[96];
61     WCHAR Feature_Parent[96];
62     WCHAR Title[0x100];
63     WCHAR Description[0x100];
64     INT Display;
65     INT Level;
66     WCHAR Directory[96];
67     INT Attributes;
68     
69     INSTALLSTATE Installed;
70     INSTALLSTATE ActionRequest;
71     INSTALLSTATE Action;
72
73     INT ComponentCount;
74     INT Components[1024]; /* yes hardcoded limit.... I am bad */
75     INT Cost;
76 } MSIFEATURE;
77
78 typedef struct tagMSICOMPONENT
79 {
80     WCHAR Component[96];
81     WCHAR ComponentId[96];
82     WCHAR Directory[96];
83     INT Attributes;
84     WCHAR Condition[0x100];
85     WCHAR KeyPath[96];
86
87     INSTALLSTATE Installed;
88     INSTALLSTATE ActionRequest;
89     INSTALLSTATE Action;
90
91     BOOL Enabled;
92     INT  Cost;
93 } MSICOMPONENT;
94
95 typedef struct tagMSIFOLDER
96 {
97     LPWSTR Directory;
98     LPWSTR TargetDefault;
99     LPWSTR SourceDefault;
100
101     LPWSTR ResolvedTarget;
102     LPWSTR ResolvedSource;
103     LPWSTR Property;   /* initially set property */
104     INT   ParentIndex;
105     INT   State;
106         /* 0 = uninitialized */
107         /* 1 = existing */
108         /* 2 = created remove if empty */
109         /* 3 = created persist if empty */
110     INT   Cost;
111     INT   Space;
112 }MSIFOLDER;
113
114 typedef struct tagMSIFILE
115 {
116     LPWSTR File;
117     INT ComponentIndex;
118     LPWSTR FileName;
119     INT FileSize;
120     LPWSTR Version;
121     LPWSTR Language;
122     INT Attributes;
123     INT Sequence;   
124
125     INT State;
126        /* 0 = uninitialize */
127        /* 1 = not present */
128        /* 2 = present but replace */
129        /* 3 = present do not replace */
130        /* 4 = Installed */
131     LPWSTR  SourcePath;
132     LPWSTR  TargetPath;
133     BOOL    Temporary; 
134 }MSIFILE;
135
136 /*
137  * Prototypes
138  */
139 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
140 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
141
142 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq);
143 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action);
144
145 static UINT ACTION_LaunchConditions(MSIPACKAGE *package);
146 static UINT ACTION_CostInitialize(MSIPACKAGE *package);
147 static UINT ACTION_CreateFolders(MSIPACKAGE *package);
148 static UINT ACTION_CostFinalize(MSIPACKAGE *package);
149 static UINT ACTION_FileCost(MSIPACKAGE *package);
150 static UINT ACTION_InstallFiles(MSIPACKAGE *package);
151 static UINT ACTION_DuplicateFiles(MSIPACKAGE *package);
152 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package);
153 static UINT ACTION_CustomAction(MSIPACKAGE *package,const WCHAR *action);
154 static UINT ACTION_InstallInitialize(MSIPACKAGE *package);
155 static UINT ACTION_InstallValidate(MSIPACKAGE *package);
156 static UINT ACTION_ProcessComponents(MSIPACKAGE *package);
157 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package);
158 static UINT ACTION_RegisterClassInfo(MSIPACKAGE *package);
159 static UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package);
160 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package);
161 static UINT ACTION_PublishProduct(MSIPACKAGE *package);
162
163 static UINT HANDLE_CustomType1(MSIPACKAGE *package, const LPWSTR source, 
164                                 const LPWSTR target, const INT type);
165 static UINT HANDLE_CustomType2(MSIPACKAGE *package, const LPWSTR source, 
166                                 const LPWSTR target, const INT type);
167 static UINT HANDLE_CustomType18(MSIPACKAGE *package, const LPWSTR source,
168                                 const LPWSTR target, const INT type);
169 static UINT HANDLE_CustomType50(MSIPACKAGE *package, const LPWSTR source,
170                                 const LPWSTR target, const INT type);
171 static UINT HANDLE_CustomType34(MSIPACKAGE *package, const LPWSTR source,
172                                 const LPWSTR target, const INT type);
173
174 static DWORD deformat_string(MSIPACKAGE *package, WCHAR* ptr,WCHAR** data);
175 static LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name,
176                            BOOL source, BOOL set_prop, MSIFOLDER **folder);
177
178 static int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path);
179  
180 /*
181  * consts and values used
182  */
183 static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
184 static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
185 static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
186 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
187 static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
188 static const WCHAR c_collen[] = {'C',':','\\',0};
189  
190 static const WCHAR cszlsb[]={'[',0};
191 static const WCHAR cszrsb[]={']',0};
192 static const WCHAR cszbs[]={'\\',0};
193
194 const static WCHAR szCreateFolders[] =
195     {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
196 const static WCHAR szCostFinalize[] =
197     {'C','o','s','t','F','i','n','a','l','i','z','e',0};
198 const static WCHAR szInstallFiles[] =
199     {'I','n','s','t','a','l','l','F','i','l','e','s',0};
200 const static WCHAR szDuplicateFiles[] =
201     {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
202 const static WCHAR szWriteRegistryValues[] =
203 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
204 const static WCHAR szCostInitialize[] =
205     {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
206 const static WCHAR szFileCost[] = {'F','i','l','e','C','o','s','t',0};
207 const static WCHAR szInstallInitialize[] = 
208     {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
209 const static WCHAR szInstallValidate[] = 
210     {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
211 const static WCHAR szLaunchConditions[] = 
212     {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
213 const static WCHAR szProcessComponents[] = 
214     {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
215 const static WCHAR szRegisterTypeLibraries[] = 
216 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r',
217 'i','e','s',0};
218 const static WCHAR szRegisterClassInfo[] = 
219 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
220 const static WCHAR szRegisterProgIdInfo[] = 
221 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
222 const static WCHAR szCreateShortcuts[] = 
223 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
224 const static WCHAR szPublishProduct[] = 
225 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
226
227 /******************************************************** 
228  * helper functions to get around current HACKS and such
229  ********************************************************/
230 inline static void reduce_to_longfilename(WCHAR* filename)
231 {
232     LPWSTR p = strchrW(filename,'|');
233     if (p)
234         memmove(filename, p+1, (strlenW(p+1)+1)*sizeof(WCHAR));
235 }
236
237 inline static char *strdupWtoA( const WCHAR *str )
238 {
239     char *ret = NULL;
240     if (str)
241     {
242         DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL
243 );
244         if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
245             WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
246     }
247     return ret;
248 }
249
250 inline static WCHAR *strdupAtoW( const char *str )
251 {
252     WCHAR *ret = NULL;
253     if (str)
254     {
255         DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
256         if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
257             MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
258     }
259     return ret;
260 }
261
262 static LPWSTR dupstrW(LPCWSTR src)
263 {
264     LPWSTR dest;
265     if (!src) return NULL;
266     dest = HeapAlloc(GetProcessHeap(), 0, (strlenW(src)+1)*sizeof(WCHAR));
267     strcpyW(dest, src);
268     return dest;
269 }
270
271 inline static WCHAR *load_dynamic_stringW(MSIRECORD *row, INT index)
272 {
273     UINT rc;
274     DWORD sz;
275     LPWSTR ret;
276    
277     sz = 0; 
278     if (MSI_RecordIsNull(row,index))
279         return NULL;
280
281     rc = MSI_RecordGetStringW(row,index,NULL,&sz);
282
283     /* having an empty string is different than NULL */
284     if (sz == 0)
285     {
286         ret = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR));
287         ret[0] = 0;
288         return ret;
289     }
290
291     sz ++;
292     ret = HeapAlloc(GetProcessHeap(),0,sz * sizeof (WCHAR));
293     rc = MSI_RecordGetStringW(row,index,ret,&sz);
294     if (rc!=ERROR_SUCCESS)
295     {
296         ERR("Unable to load dynamic string\n");
297         HeapFree(GetProcessHeap(), 0, ret);
298         ret = NULL;
299     }
300     return ret;
301 }
302
303 inline static LPWSTR load_dynamic_property(MSIPACKAGE *package, LPCWSTR prop,
304                                            UINT* rc)
305 {
306     DWORD sz = 0;
307     LPWSTR str;
308     UINT r;
309
310     r = MSI_GetPropertyW(package, prop, NULL, &sz);
311     if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
312     {
313         if (rc)
314             *rc = r;
315         return NULL;
316     }
317     sz++;
318     str = HeapAlloc(GetProcessHeap(),0,sz*sizeof(WCHAR));
319     r = MSI_GetPropertyW(package, prop, str, &sz);
320     if (r != ERROR_SUCCESS)
321     {
322         HeapFree(GetProcessHeap(),0,str);
323         str = NULL;
324     }
325     if (rc)
326         *rc = r;
327     return str;
328 }
329
330 inline static int get_loaded_component(MSIPACKAGE* package, LPCWSTR Component )
331 {
332     int rc = -1;
333     DWORD i;
334
335     for (i = 0; i < package->loaded_components; i++)
336     {
337         if (strcmpW(Component,package->components[i].Component)==0)
338         {
339             rc = i;
340             break;
341         }
342     }
343     return rc;
344 }
345
346 inline static int get_loaded_feature(MSIPACKAGE* package, LPCWSTR Feature )
347 {
348     int rc = -1;
349     DWORD i;
350
351     for (i = 0; i < package->loaded_features; i++)
352     {
353         if (strcmpW(Feature,package->features[i].Feature)==0)
354         {
355             rc = i;
356             break;
357         }
358     }
359     return rc;
360 }
361
362 inline static int get_loaded_file(MSIPACKAGE* package, LPCWSTR file)
363 {
364     int rc = -1;
365     DWORD i;
366
367     for (i = 0; i < package->loaded_files; i++)
368     {
369         if (strcmpW(file,package->files[i].File)==0)
370         {
371             rc = i;
372             break;
373         }
374     }
375     return rc;
376 }
377
378
379 static int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path)
380 {
381     DWORD i;
382     DWORD index;
383
384     if (!package)
385         return -2;
386
387     for (i=0; i < package->loaded_files; i++)
388         if (strcmpW(package->files[i].File,name)==0)
389             return -1;
390
391     index = package->loaded_files;
392     package->loaded_files++;
393     if (package->loaded_files== 1)
394         package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
395     else
396         package->files = HeapReAlloc(GetProcessHeap(),0,
397             package->files , package->loaded_files * sizeof(MSIFILE));
398
399     memset(&package->files[index],0,sizeof(MSIFILE));
400
401     package->files[index].File = dupstrW(name);
402     package->files[index].TargetPath = dupstrW(path);
403     package->files[index].Temporary = TRUE;
404
405     TRACE("Tracking tempfile (%s)\n",debugstr_w(package->files[index].File));  
406
407     return 0;
408 }
409
410 void ACTION_remove_tracked_tempfiles(MSIPACKAGE* package)
411 {
412     DWORD i;
413
414     if (!package)
415         return;
416
417     for (i = 0; i < package->loaded_files; i++)
418     {
419         if (package->files[i].Temporary)
420         {
421             TRACE("Cleaning up %s\n",debugstr_w(package->files[i].TargetPath));
422             DeleteFileW(package->files[i].TargetPath);
423         }
424
425     }
426 }
427
428 /* Called when the package is being closed */
429 extern void ACTION_free_package_structures( MSIPACKAGE* package)
430 {
431     INT i;
432     
433     TRACE("Freeing package action data\n");
434
435     /* No dynamic buffers in features */
436     if (package->features && package->loaded_features > 0)
437         HeapFree(GetProcessHeap(),0,package->features);
438
439     for (i = 0; i < package->loaded_folders; i++)
440     {
441         HeapFree(GetProcessHeap(),0,package->folders[i].Directory);
442         HeapFree(GetProcessHeap(),0,package->folders[i].TargetDefault);
443         HeapFree(GetProcessHeap(),0,package->folders[i].SourceDefault);
444         HeapFree(GetProcessHeap(),0,package->folders[i].ResolvedTarget);
445         HeapFree(GetProcessHeap(),0,package->folders[i].ResolvedSource);
446         HeapFree(GetProcessHeap(),0,package->folders[i].Property);
447     }
448     if (package->folders && package->loaded_folders > 0)
449         HeapFree(GetProcessHeap(),0,package->folders);
450
451     /* no dynamic buffers in components */ 
452     if (package->components && package->loaded_components > 0)
453         HeapFree(GetProcessHeap(),0,package->components);
454
455     for (i = 0; i < package->loaded_files; i++)
456     {
457         HeapFree(GetProcessHeap(),0,package->files[i].File);
458         HeapFree(GetProcessHeap(),0,package->files[i].FileName);
459         HeapFree(GetProcessHeap(),0,package->files[i].Version);
460         HeapFree(GetProcessHeap(),0,package->files[i].Language);
461         HeapFree(GetProcessHeap(),0,package->files[i].SourcePath);
462         HeapFree(GetProcessHeap(),0,package->files[i].TargetPath);
463     }
464
465     if (package->files && package->loaded_files > 0)
466         HeapFree(GetProcessHeap(),0,package->files);
467 }
468
469 static UINT ACTION_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )
470 {
471     LPWSTR szQuery;
472     LPCWSTR p;
473     UINT sz, rc;
474     va_list va;
475
476     /* figure out how much space we need to allocate */
477     va_start(va, fmt);
478     sz = strlenW(fmt) + 1;
479     p = fmt;
480     while (*p)
481     {
482         p = strchrW(p, '%');
483         if (!p)
484             break;
485         p++;
486         switch (*p)
487         {
488         case 's':  /* a string */
489             sz += strlenW(va_arg(va,LPCWSTR));
490             break;
491         case 'd':
492         case 'i':  /* an integer -2147483648 seems to be longest */
493             sz += 3*sizeof(int);
494             (void)va_arg(va,int);
495             break;
496         case '%':  /* a single % - leave it alone */
497             break;
498         default:
499             FIXME("Unhandled character type %c\n",*p);
500         }
501         p++;
502     }
503     va_end(va);
504
505     /* construct the string */
506     szQuery = HeapAlloc(GetProcessHeap(), 0, sz*sizeof(WCHAR));
507     va_start(va, fmt);
508     vsnprintfW(szQuery, sz, fmt, va);
509     va_end(va);
510
511     /* perform the query */
512     rc = MSI_DatabaseOpenViewW(db, szQuery, view);
513     HeapFree(GetProcessHeap(), 0, szQuery);
514     return rc;
515 }
516
517 static void ui_progress(MSIPACKAGE *package, int a, int b, int c, int d )
518 {
519     MSIRECORD * row;
520
521     row = MSI_CreateRecord(4);
522     MSI_RecordSetInteger(row,1,a);
523     MSI_RecordSetInteger(row,2,b);
524     MSI_RecordSetInteger(row,3,c);
525     MSI_RecordSetInteger(row,4,d);
526     MSI_ProcessMessage(package, INSTALLMESSAGE_PROGRESS, row);
527     msiobj_release(&row->hdr);
528 }
529
530 static void ui_actiondata(MSIPACKAGE *package, LPCWSTR action, MSIRECORD * record)
531 {
532     static const WCHAR Query_t[] = 
533 {'S','E','L','E','C','T',' ','*',' ','f','r','o','m',' ','A','c','t','i','o',
534 'n','T','e','x','t',' ','w','h','e','r','e',' ','A','c','t','i','o','n',' ','=',
535 ' ','\'','%','s','\'',0};
536     WCHAR message[1024];
537     UINT rc;
538     MSIQUERY * view;
539     MSIRECORD * row = 0;
540     LPWSTR ptr;
541
542     if (!package->LastAction || strcmpW(package->LastAction,action))
543     {
544         rc = ACTION_OpenQuery(package->db, &view, Query_t, action);
545         if (rc != ERROR_SUCCESS)
546             return;
547
548         rc = MSI_ViewExecute(view, 0);
549         if (rc != ERROR_SUCCESS)
550         {
551             MSI_ViewClose(view);
552             return;
553         }
554         rc = MSI_ViewFetch(view,&row);
555         if (rc != ERROR_SUCCESS)
556         {
557             MSI_ViewClose(view);
558             return;
559         }
560
561         if (MSI_RecordIsNull(row,3))
562         {
563             msiobj_release(&row->hdr);
564             MSI_ViewClose(view);
565             msiobj_release(&view->hdr);
566             return;
567         }
568
569         /* update the cached actionformat */
570         if (package->ActionFormat)
571             HeapFree(GetProcessHeap(),0,package->ActionFormat);
572         package->ActionFormat = load_dynamic_stringW(row,3);
573
574         if (package->LastAction)
575             HeapFree(GetProcessHeap(),0,package->LastAction);
576         package->LastAction = dupstrW(action);
577
578         msiobj_release(&row->hdr);
579         MSI_ViewClose(view);
580         msiobj_release(&view->hdr);
581     }
582
583     message[0]=0;
584     ptr = package->ActionFormat;
585     while (*ptr)
586     {
587         LPWSTR ptr2;
588         LPWSTR data=NULL;
589         WCHAR tmp[1023];
590         INT field;
591
592         ptr2 = strchrW(ptr,'[');
593         if (ptr2)
594         {
595             strncpyW(tmp,ptr,ptr2-ptr);
596             tmp[ptr2-ptr]=0;
597             strcatW(message,tmp);
598             ptr2++;
599             field = atoiW(ptr2);
600             data = load_dynamic_stringW(record,field);
601             if (data)
602             {
603                 strcatW(message,data);
604                 HeapFree(GetProcessHeap(),0,data);
605             }
606             ptr=strchrW(ptr2,']');
607             ptr++;
608         }
609         else
610         {
611             strcatW(message,ptr);
612             break;
613         }
614     }
615
616     row = MSI_CreateRecord(1);
617     MSI_RecordSetStringW(row,1,message);
618  
619     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
620     msiobj_release(&row->hdr);
621 }
622
623
624 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
625 {
626     static const WCHAR template_s[]=
627 {'A','c','t','i','o','n',' ','%','s',':',' ','%','s','.',' ','%','s','.',0};
628     static const WCHAR format[] = 
629 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
630     static const WCHAR Query_t[] = 
631 {'S','E','L','E','C','T',' ','*',' ','f','r','o','m',' ','A','c','t','i','o',
632 'n','T','e','x','t',' ','w','h','e','r','e',' ','A','c','t','i','o','n',' ','=',
633 ' ','\'','%','s','\'',0};
634     WCHAR message[1024];
635     WCHAR timet[0x100];
636     UINT rc;
637     MSIQUERY * view;
638     MSIRECORD * row = 0;
639     WCHAR *ActionText=NULL;
640
641     GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
642
643     rc = ACTION_OpenQuery(package->db, &view, Query_t, action);
644     if (rc != ERROR_SUCCESS)
645         return;
646     rc = MSI_ViewExecute(view, 0);
647     if (rc != ERROR_SUCCESS)
648     {
649         MSI_ViewClose(view);
650         msiobj_release(&view->hdr);
651         return;
652     }
653     rc = MSI_ViewFetch(view,&row);
654     if (rc != ERROR_SUCCESS)
655     {
656         MSI_ViewClose(view);
657         msiobj_release(&view->hdr);
658         return;
659     }
660
661     ActionText = load_dynamic_stringW(row,2);
662     msiobj_release(&row->hdr);
663     MSI_ViewClose(view);
664     msiobj_release(&view->hdr);
665
666     sprintfW(message,template_s,timet,action,ActionText);
667
668     row = MSI_CreateRecord(1);
669     MSI_RecordSetStringW(row,1,message);
670  
671     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
672     msiobj_release(&row->hdr);
673     HeapFree(GetProcessHeap(),0,ActionText);
674 }
675
676 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, 
677                           UINT rc)
678 {
679     MSIRECORD * row;
680     static const WCHAR template_s[]=
681 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ','%','s',
682 '.',0};
683     static const WCHAR template_e[]=
684 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ','%','s',
685 '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ','%','i','.',0};
686     static const WCHAR format[] = 
687 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
688     WCHAR message[1024];
689     WCHAR timet[0x100];
690
691     GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
692     if (start)
693         sprintfW(message,template_s,timet,action);
694     else
695         sprintfW(message,template_e,timet,action,rc);
696     
697     row = MSI_CreateRecord(1);
698     MSI_RecordSetStringW(row,1,message);
699  
700     MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
701     msiobj_release(&row->hdr);
702 }
703
704 /*
705  *  build_directory_name()
706  *
707  *  This function is to save messing round with directory names
708  *  It handles adding backslashes between path segments, 
709  *   and can add \ at the end of the directory name if told to.
710  *
711  *  It takes a variable number of arguments.
712  *  It always allocates a new string for the result, so make sure
713  *   to free the return value when finished with it.
714  *
715  *  The first arg is the number of path segments that follow.
716  *  The arguments following count are a list of path segments.
717  *  A path segment may be NULL.
718  *
719  *  Path segments will be added with a \ seperating them.
720  *  A \ will not be added after the last segment, however if the
721  *    last segment is NULL, then the last character will be a \
722  * 
723  */
724 static LPWSTR build_directory_name(DWORD count, ...)
725 {
726     DWORD sz = 1, i;
727     LPWSTR dir;
728     va_list va;
729
730     va_start(va,count);
731     for(i=0; i<count; i++)
732     {
733         LPCWSTR str = va_arg(va,LPCWSTR);
734         if (str)
735             sz += strlenW(str) + 1;
736     }
737     va_end(va);
738
739     dir = HeapAlloc(GetProcessHeap(), 0, sz*sizeof(WCHAR));
740     dir[0]=0;
741
742     va_start(va,count);
743     for(i=0; i<count; i++)
744     {
745         LPCWSTR str = va_arg(va,LPCWSTR);
746         if (!str)
747             continue;
748         strcatW(dir, str);
749         if( ((i+1)!=count) && dir[strlenW(dir)-1]!='\\')
750             strcatW(dir, cszbs);
751     }
752     return dir;
753 }
754
755
756 /****************************************************
757  * TOP level entry points 
758  *****************************************************/
759
760 UINT ACTION_DoTopLevelINSTALL(MSIPACKAGE *package, LPCWSTR szPackagePath,
761                               LPCWSTR szCommandLine)
762 {
763     DWORD sz;
764     WCHAR buffer[10];
765     UINT rc;
766     static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
767
768     if (szPackagePath)   
769     {
770         LPWSTR p, check, path;
771  
772         path = dupstrW(szPackagePath);
773         p = strrchrW(path,'\\');    
774         if (p)
775         {
776             p++;
777             *p=0;
778         }
779
780         check = load_dynamic_property(package, cszSourceDir,NULL);
781         if (!check)
782             MSI_SetPropertyW(package, cszSourceDir, path);
783         else
784             HeapFree(GetProcessHeap(), 0, check);
785
786         HeapFree(GetProcessHeap(), 0, path);
787     }
788
789     if (szCommandLine)
790     {
791         LPWSTR ptr,ptr2;
792         ptr = (LPWSTR)szCommandLine;
793        
794         while (*ptr)
795         {
796             WCHAR *prop = NULL;
797             WCHAR *val = NULL;
798
799             TRACE("Looking at %s\n",debugstr_w(ptr));
800
801             ptr2 = strchrW(ptr,'=');
802             if (ptr2)
803             {
804                 BOOL quote=FALSE;
805                 DWORD len = 0;
806
807                 while (*ptr == ' ') ptr++;
808                 len = ptr2-ptr;
809                 prop = HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR));
810                 strncpyW(prop,ptr,len);
811                 prop[len]=0;
812                 ptr2++;
813            
814                 len = 0; 
815                 ptr = ptr2; 
816                 while (*ptr && (quote || (!quote && *ptr!=' ')))
817                 {
818                     if (*ptr == '"')
819                         quote = !quote;
820                     ptr++;
821                     len++;
822                 }
823                
824                 if (*ptr2=='"')
825                 {
826                     ptr2++;
827                     len -= 2;
828                 }
829                 val = HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR));
830                 strncpyW(val,ptr2,len);
831                 val[len] = 0;
832
833                 if (strlenW(prop) > 0)
834                 {
835                     TRACE("Found commandline property (%s) = (%s)\n", 
836                                        debugstr_w(prop), debugstr_w(val));
837                     MSI_SetPropertyW(package,prop,val);
838                 }
839                 HeapFree(GetProcessHeap(),0,val);
840                 HeapFree(GetProcessHeap(),0,prop);
841             }
842             ptr++;
843         }
844     }
845   
846     sz = 10; 
847     if (MSI_GetPropertyW(package,szUILevel,buffer,&sz) == ERROR_SUCCESS)
848     {
849         if (atoiW(buffer) >= INSTALLUILEVEL_REDUCED)
850         {
851             rc = ACTION_ProcessUISequence(package);
852             if (rc == ERROR_SUCCESS)
853                 rc = ACTION_ProcessExecSequence(package,TRUE);
854         }
855         else
856             rc = ACTION_ProcessExecSequence(package,FALSE);
857     }
858     else
859         rc = ACTION_ProcessExecSequence(package,FALSE);
860
861     /* process the ending type action */
862     if (rc == ERROR_SUCCESS)
863         rc = ACTION_PerformActionSequence(package,-1);
864     else if (rc == ERROR_FUNCTION_FAILED) 
865         rc = ACTION_PerformActionSequence(package,-3);
866     
867     return rc;
868 }
869
870 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
871 {
872     MSIQUERY * view;
873     UINT rc;
874     WCHAR buffer[0x100];
875     DWORD sz = 0x100;
876     MSIRECORD * row = 0;
877     static const WCHAR ExecSeqQuery[] =  {
878    's','e','l','e','c','t',' ','*',' ',
879    'f','r','o','m',' ',
880        'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
881        'S','e','q','u','e','n','c','e',' ',
882    'w','h','e','r','e',' ','S','e','q','u','e','n','c','e',' ',
883        '=',' ','%','i',0};
884
885     rc = ACTION_OpenQuery(package->db, &view, ExecSeqQuery, seq);
886
887     if (rc == ERROR_SUCCESS)
888     {
889         rc = MSI_ViewExecute(view, 0);
890
891         if (rc != ERROR_SUCCESS)
892         {
893             MSI_ViewClose(view);
894             msiobj_release(&view->hdr);
895             goto end;
896         }
897        
898         TRACE("Running the actions\n"); 
899
900         rc = MSI_ViewFetch(view,&row);
901         if (rc != ERROR_SUCCESS)
902         {
903             rc = ERROR_SUCCESS;
904             goto end;
905         }
906
907         /* check conditions */
908         if (!MSI_RecordIsNull(row,2))
909         {
910             LPWSTR cond = NULL;
911             cond = load_dynamic_stringW(row,2);
912
913             if (cond)
914             {
915                 /* this is a hack to skip errors in the condition code */
916                 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
917                 {
918                     HeapFree(GetProcessHeap(),0,cond);
919                     msiobj_release(&row->hdr);
920                     goto end;
921                 }
922                 else
923                     HeapFree(GetProcessHeap(),0,cond);
924             }
925         }
926
927         sz=0x100;
928         rc =  MSI_RecordGetStringW(row,1,buffer,&sz);
929         if (rc != ERROR_SUCCESS)
930         {
931             ERR("Error is %x\n",rc);
932             msiobj_release(&row->hdr);
933             goto end;
934         }
935
936         rc = ACTION_PerformAction(package,buffer);
937         msiobj_release(&row->hdr);
938 end:
939         MSI_ViewClose(view);
940         msiobj_release(&view->hdr);
941     }
942     else
943         rc = ERROR_SUCCESS;
944
945     return rc;
946 }
947
948 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
949 {
950     MSIQUERY * view;
951     UINT rc;
952     static const WCHAR ExecSeqQuery[] =  {
953        's','e','l','e','c','t',' ','*',' ',
954        'f','r','o','m',' ',
955            'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
956            'S','e','q','u','e','n','c','e',' ',
957        'w','h','e','r','e',' ','S','e','q','u','e','n','c','e',' ',
958            '>',' ','%','i',' ','o','r','d','e','r',' ',
959        'b','y',' ','S','e','q','u','e','n','c','e',0 };
960     MSIRECORD * row = 0;
961     static const WCHAR IVQuery[] = {
962        's','e','l','e','c','t',' ','S','e','q','u','e','n','c','e',' ',
963        'f','r','o','m',' ','I','n','s','t','a','l','l',
964            'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e',' ',
965        'w','h','e','r','e',' ','A','c','t','i','o','n',' ','=',' ',
966            '`','I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','`',
967        0};
968     INT seq = 0;
969
970     /* get the sequence number */
971     if (UIran)
972     {
973         rc = MSI_DatabaseOpenViewW(package->db, IVQuery, &view);
974         if (rc != ERROR_SUCCESS)
975             return rc;
976         rc = MSI_ViewExecute(view, 0);
977         if (rc != ERROR_SUCCESS)
978         {
979             MSI_ViewClose(view);
980             msiobj_release(&view->hdr);
981             return rc;
982         }
983         rc = MSI_ViewFetch(view,&row);
984         if (rc != ERROR_SUCCESS)
985         {
986             MSI_ViewClose(view);
987             msiobj_release(&view->hdr);
988             return rc;
989         }
990         seq = MSI_RecordGetInteger(row,1);
991         msiobj_release(&row->hdr);
992         MSI_ViewClose(view);
993         msiobj_release(&view->hdr);
994     }
995
996     rc = ACTION_OpenQuery(package->db, &view, ExecSeqQuery, seq);
997     if (rc == ERROR_SUCCESS)
998     {
999         rc = MSI_ViewExecute(view, 0);
1000
1001         if (rc != ERROR_SUCCESS)
1002         {
1003             MSI_ViewClose(view);
1004             msiobj_release(&view->hdr);
1005             goto end;
1006         }
1007        
1008         TRACE("Running the actions\n"); 
1009
1010         while (1)
1011         {
1012             WCHAR buffer[0x100];
1013             DWORD sz = 0x100;
1014
1015             rc = MSI_ViewFetch(view,&row);
1016             if (rc != ERROR_SUCCESS)
1017             {
1018                 rc = ERROR_SUCCESS;
1019                 break;
1020             }
1021
1022             /* check conditions */
1023             if (!MSI_RecordIsNull(row,2))
1024             {
1025                 LPWSTR cond = NULL;
1026                 cond = load_dynamic_stringW(row,2);
1027
1028                 if (cond)
1029                 {
1030                     /* this is a hack to skip errors in the condition code */
1031                     if (MSI_EvaluateConditionW(package, cond) ==
1032                             MSICONDITION_FALSE)
1033                     {
1034                         HeapFree(GetProcessHeap(),0,cond);
1035                         msiobj_release(&row->hdr);
1036                         continue; 
1037                     }
1038                     else
1039                         HeapFree(GetProcessHeap(),0,cond);
1040                 }
1041             }
1042
1043             sz=0x100;
1044             rc =  MSI_RecordGetStringW(row,1,buffer,&sz);
1045             if (rc != ERROR_SUCCESS)
1046             {
1047                 ERR("Error is %x\n",rc);
1048                 msiobj_release(&row->hdr);
1049                 break;
1050             }
1051
1052             rc = ACTION_PerformAction(package,buffer);
1053
1054             if (rc == ERROR_FUNCTION_NOT_CALLED)
1055                 rc = ERROR_SUCCESS;
1056
1057             if (rc != ERROR_SUCCESS)
1058             {
1059                 ERR("Execution halted due to error (%i)\n",rc);
1060                 msiobj_release(&row->hdr);
1061                 break;
1062             }
1063
1064             msiobj_release(&row->hdr);
1065         }
1066
1067         MSI_ViewClose(view);
1068         msiobj_release(&view->hdr);
1069     }
1070
1071 end:
1072     return rc;
1073 }
1074
1075
1076 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
1077 {
1078     MSIQUERY * view;
1079     UINT rc;
1080     static const WCHAR ExecSeqQuery [] = {
1081       's','e','l','e','c','t',' ','*',' ',
1082       'f','r','o','m',' ','I','n','s','t','a','l','l',
1083             'U','I','S','e','q','u','e','n','c','e',' ',
1084       'w','h','e','r','e',' ','S','e','q','u','e','n','c','e',' ', '>',' ','0',' ',
1085       'o','r','d','e','r',' ','b','y',' ','S','e','q','u','e','n','c','e',0};
1086     
1087     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1088     
1089     if (rc == ERROR_SUCCESS)
1090     {
1091         rc = MSI_ViewExecute(view, 0);
1092
1093         if (rc != ERROR_SUCCESS)
1094         {
1095             MSI_ViewClose(view);
1096             msiobj_release(&view->hdr);
1097             goto end;
1098         }
1099        
1100         TRACE("Running the actions \n"); 
1101
1102         while (1)
1103         {
1104             WCHAR buffer[0x100];
1105             DWORD sz = 0x100;
1106             MSIRECORD * row = 0;
1107
1108             rc = MSI_ViewFetch(view,&row);
1109             if (rc != ERROR_SUCCESS)
1110             {
1111                 rc = ERROR_SUCCESS;
1112                 break;
1113             }
1114
1115             /* check conditions */
1116             if (!MSI_RecordIsNull(row,2))
1117             {
1118                 LPWSTR cond = NULL;
1119                 cond = load_dynamic_stringW(row,2);
1120
1121                 if (cond)
1122                 {
1123                     /* this is a hack to skip errors in the condition code */
1124                     if (MSI_EvaluateConditionW(package, cond) ==
1125                             MSICONDITION_FALSE)
1126                     {
1127                         HeapFree(GetProcessHeap(),0,cond);
1128                         msiobj_release(&row->hdr);
1129                         continue; 
1130                     }
1131                     else
1132                         HeapFree(GetProcessHeap(),0,cond);
1133                 }
1134             }
1135
1136             sz=0x100;
1137             rc =  MSI_RecordGetStringW(row,1,buffer,&sz);
1138             if (rc != ERROR_SUCCESS)
1139             {
1140                 ERR("Error is %x\n",rc);
1141                 msiobj_release(&row->hdr);
1142                 break;
1143             }
1144
1145             rc = ACTION_PerformAction(package,buffer);
1146
1147             if (rc == ERROR_FUNCTION_NOT_CALLED)
1148                 rc = ERROR_SUCCESS;
1149
1150             if (rc != ERROR_SUCCESS)
1151             {
1152                 ERR("Execution halted due to error (%i)\n",rc);
1153                 msiobj_release(&row->hdr);
1154                 break;
1155             }
1156
1157             msiobj_release(&row->hdr);
1158         }
1159
1160         MSI_ViewClose(view);
1161         msiobj_release(&view->hdr);
1162     }
1163
1164 end:
1165     return rc;
1166 }
1167
1168 /********************************************************
1169  * ACTION helper functions and functions that perform the actions
1170  *******************************************************/
1171
1172 /* 
1173  * Alot of actions are really important even if they don't do anything
1174  * explicit.. Lots of properties are set at the beginning of the installation
1175  * CostFinalize does a bunch of work to translated the directories and such
1176  * 
1177  * But until I get write access to the database that is hard, so I am going to
1178  * hack it to see if I can get something to run.
1179  */
1180 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
1181 {
1182     UINT rc = ERROR_SUCCESS; 
1183
1184     TRACE("Performing action (%s)\n",debugstr_w(action));
1185     ui_actioninfo(package, action, TRUE, 0);
1186     ui_actionstart(package, action);
1187
1188     /* pre install, setup and configuration block */
1189     if (strcmpW(action,szLaunchConditions)==0)
1190         rc = ACTION_LaunchConditions(package);
1191     else if (strcmpW(action,szCostInitialize)==0)
1192         rc = ACTION_CostInitialize(package);
1193     else if (strcmpW(action,szFileCost)==0)
1194         rc = ACTION_FileCost(package);
1195     else if (strcmpW(action,szCostFinalize)==0)
1196         rc = ACTION_CostFinalize(package);
1197     else if (strcmpW(action,szInstallValidate)==0)
1198         rc = ACTION_InstallValidate(package);
1199
1200     /* install block */
1201     else if (strcmpW(action,szProcessComponents)==0)
1202         rc = ACTION_ProcessComponents(package);
1203     else if (strcmpW(action,szInstallInitialize)==0)
1204         rc = ACTION_InstallInitialize(package);
1205     else if (strcmpW(action,szCreateFolders)==0)
1206         rc = ACTION_CreateFolders(package);
1207     else if (strcmpW(action,szInstallFiles)==0)
1208         rc = ACTION_InstallFiles(package);
1209     else if (strcmpW(action,szDuplicateFiles)==0)
1210         rc = ACTION_DuplicateFiles(package);
1211     else if (strcmpW(action,szWriteRegistryValues)==0)
1212         rc = ACTION_WriteRegistryValues(package);
1213      else if (strcmpW(action,szRegisterTypeLibraries)==0)
1214         rc = ACTION_RegisterTypeLibraries(package);
1215      else if (strcmpW(action,szRegisterClassInfo)==0)
1216         rc = ACTION_RegisterClassInfo(package);
1217      else if (strcmpW(action,szRegisterProgIdInfo)==0)
1218         rc = ACTION_RegisterProgIdInfo(package);
1219      else if (strcmpW(action,szCreateShortcuts)==0)
1220         rc = ACTION_CreateShortcuts(package);
1221     else if (strcmpW(action,szPublishProduct)==0)
1222         rc = ACTION_PublishProduct(package);
1223
1224     /*
1225      Called during iTunes but unimplemented and seem important
1226
1227      ResolveSource  (sets SourceDir)
1228      RegisterProduct
1229      InstallFinalize
1230      */
1231      else if ((rc = ACTION_CustomAction(package,action)) != ERROR_SUCCESS)
1232      {
1233         FIXME("UNHANDLED MSI ACTION %s\n",debugstr_w(action));
1234         rc = ERROR_FUNCTION_NOT_CALLED;
1235      }
1236
1237     ui_actioninfo(package, action, FALSE, rc);
1238     return rc;
1239 }
1240
1241
1242 static UINT ACTION_CustomAction(MSIPACKAGE *package,const WCHAR *action)
1243 {
1244     UINT rc = ERROR_SUCCESS;
1245     MSIQUERY * view;
1246     MSIRECORD * row = 0;
1247     static const WCHAR ExecSeqQuery[] =
1248     {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','u','s','t','o'
1249         ,'m','A','c','t','i','o','n',' ','w','h','e','r','e',' ','`','A','c','t','i'
1250         ,'o','n','`',' ','=',' ','`','%','s','`',0};
1251     UINT type;
1252     LPWSTR source;
1253     LPWSTR target;
1254     WCHAR *deformated=NULL;
1255
1256     rc = ACTION_OpenQuery(package->db, &view, ExecSeqQuery, action);
1257     if (rc != ERROR_SUCCESS)
1258         return rc;
1259
1260     rc = MSI_ViewExecute(view, 0);
1261     if (rc != ERROR_SUCCESS)
1262     {
1263         MSI_ViewClose(view);
1264         msiobj_release(&view->hdr);
1265         return rc;
1266     }
1267
1268     rc = MSI_ViewFetch(view,&row);
1269     if (rc != ERROR_SUCCESS)
1270     {
1271         MSI_ViewClose(view);
1272         msiobj_release(&view->hdr);
1273         return rc;
1274     }
1275
1276     type = MSI_RecordGetInteger(row,2);
1277
1278     source = load_dynamic_stringW(row,3);
1279     target = load_dynamic_stringW(row,4);
1280
1281     TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
1282           debugstr_w(source), debugstr_w(target));
1283
1284     /* we are ignoring ALOT of flags and important synchronization stuff */
1285     switch (type & CUSTOM_ACTION_TYPE_MASK)
1286     {
1287         case 1: /* DLL file stored in a Binary table stream */
1288             rc = HANDLE_CustomType1(package,source,target,type);
1289             break;
1290         case 2: /* EXE file stored in a Binary table strem */
1291             rc = HANDLE_CustomType2(package,source,target,type);
1292             break;
1293         case 18: /*EXE file installed with package */
1294             rc = HANDLE_CustomType18(package,source,target,type);
1295             break;
1296         case 50: /*EXE file specified by a property value */
1297             rc = HANDLE_CustomType50(package,source,target,type);
1298             break;
1299         case 34: /*EXE to be run in specified directory */
1300             rc = HANDLE_CustomType34(package,source,target,type);
1301             break;
1302         case 35: /* Directory set with formatted text. */
1303             deformat_string(package,target,&deformated);
1304             MSI_SetTargetPathW(package, source, deformated);
1305             HeapFree(GetProcessHeap(),0,deformated);
1306             break;
1307         case 51: /* Property set with formatted text. */
1308             deformat_string(package,target,&deformated);
1309             rc = MSI_SetPropertyW(package,source,deformated);
1310             HeapFree(GetProcessHeap(),0,deformated);
1311             break;
1312         default:
1313             FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
1314              type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
1315              debugstr_w(target));
1316     }
1317
1318     HeapFree(GetProcessHeap(),0,source);
1319     HeapFree(GetProcessHeap(),0,target);
1320     msiobj_release(&row->hdr);
1321     MSI_ViewClose(view);
1322     msiobj_release(&view->hdr);
1323     return rc;
1324 }
1325
1326 static UINT store_binary_to_temp(MSIPACKAGE *package, const LPWSTR source, 
1327                                 LPWSTR tmp_file)
1328 {
1329     DWORD sz=MAX_PATH;
1330
1331     if (MSI_GetPropertyW(package, cszTempFolder, tmp_file, &sz) 
1332         != ERROR_SUCCESS)
1333         GetTempPathW(MAX_PATH,tmp_file);
1334
1335     strcatW(tmp_file,source);
1336
1337     if (GetFileAttributesW(tmp_file) != INVALID_FILE_ATTRIBUTES)
1338     {
1339         TRACE("File already exists\n");
1340         return ERROR_SUCCESS;
1341     }
1342     else
1343     {
1344         /* write out the file */
1345         UINT rc;
1346         MSIQUERY * view;
1347         MSIRECORD * row = 0;
1348         static const WCHAR fmt[] =
1349         {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','B','i'
1350 ,'n','a','r','y',' ','w','h','e','r','e',' ','N','a','m','e','=','`','%','s','`',0};
1351         HANDLE the_file;
1352         CHAR buffer[1024];
1353
1354         if (track_tempfile(package, source, tmp_file)!=0)
1355             FIXME("File Name in temp tracking collision\n");
1356
1357         the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1358                            FILE_ATTRIBUTE_NORMAL, NULL);
1359     
1360         if (the_file == INVALID_HANDLE_VALUE)
1361             return ERROR_FUNCTION_FAILED;
1362
1363         rc = ACTION_OpenQuery(package->db, &view, fmt, source);
1364         if (rc != ERROR_SUCCESS)
1365             return rc;
1366
1367         rc = MSI_ViewExecute(view, 0);
1368         if (rc != ERROR_SUCCESS)
1369         {
1370             MSI_ViewClose(view);
1371             msiobj_release(&view->hdr);
1372             return rc;
1373         }
1374
1375         rc = MSI_ViewFetch(view,&row);
1376         if (rc != ERROR_SUCCESS)
1377         {
1378             MSI_ViewClose(view);
1379             msiobj_release(&view->hdr);
1380             return rc;
1381         }
1382
1383         do 
1384         {
1385             DWORD write;
1386             sz = 1024;
1387             rc = MSI_RecordReadStream(row,2,buffer,&sz);
1388             if (rc != ERROR_SUCCESS)
1389             {
1390                 ERR("Failed to get stream\n");
1391                 CloseHandle(the_file);  
1392                 DeleteFileW(tmp_file);
1393                 break;
1394             }
1395             WriteFile(the_file,buffer,sz,&write,NULL);
1396         } while (sz == 1024);
1397
1398         CloseHandle(the_file);
1399
1400         msiobj_release(&row->hdr);
1401         MSI_ViewClose(view);
1402         msiobj_release(&view->hdr);
1403     }
1404
1405     return ERROR_SUCCESS;
1406 }
1407
1408 typedef UINT __stdcall CustomEntry(MSIHANDLE);
1409 typedef struct 
1410 {
1411         MSIPACKAGE *package;
1412         WCHAR *target;
1413         WCHAR *source;
1414 } thread_struct;
1415
1416 static DWORD WINAPI DllThread(LPVOID info)
1417 {
1418     HANDLE DLL;
1419     LPSTR proc;
1420     thread_struct *stuff;
1421     CustomEntry *fn;
1422      
1423     stuff = (thread_struct*)info;
1424
1425     TRACE("Asynchronous start (%s, %s) \n", debugstr_w(stuff->source),
1426           debugstr_w(stuff->target));
1427
1428     DLL = LoadLibraryW(stuff->source);
1429     if (DLL)
1430     {
1431         proc = strdupWtoA( stuff->target );
1432         fn = (CustomEntry*)GetProcAddress(DLL,proc);
1433         if (fn)
1434         {
1435             MSIHANDLE hPackage;
1436             MSIPACKAGE *package = stuff->package;
1437
1438             TRACE("Calling function\n");
1439             hPackage = msiobj_findhandle( &package->hdr );
1440             if( !hPackage )
1441                 ERR("Handle for object %p not found\n", package );
1442             fn(hPackage);
1443             msiobj_release( &package->hdr );
1444         }
1445         else
1446             ERR("Cannot load functon\n");
1447
1448         HeapFree(GetProcessHeap(),0,proc);
1449         FreeLibrary(DLL);
1450     }
1451     else
1452         ERR("Unable to load library\n");
1453     msiobj_release( &stuff->package->hdr );
1454     HeapFree(GetProcessHeap(),0,stuff->source);
1455     HeapFree(GetProcessHeap(),0,stuff->target);
1456     HeapFree(GetProcessHeap(), 0, stuff);
1457     return 0;
1458 }
1459
1460 static UINT HANDLE_CustomType1(MSIPACKAGE *package, const LPWSTR source, 
1461                                 const LPWSTR target, const INT type)
1462 {
1463     WCHAR tmp_file[MAX_PATH];
1464     CustomEntry *fn;
1465     HANDLE DLL;
1466     LPSTR proc;
1467
1468     store_binary_to_temp(package, source, tmp_file);
1469
1470     TRACE("Calling function %s from %s\n",debugstr_w(target),
1471           debugstr_w(tmp_file));
1472
1473     if (!strchrW(tmp_file,'.'))
1474     {
1475         static const WCHAR dot[]={'.',0};
1476         strcatW(tmp_file,dot);
1477     } 
1478
1479     if (type & 0xc0)
1480     {
1481         DWORD ThreadId;
1482         HANDLE ThreadHandle;
1483         thread_struct *info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) );
1484
1485         msiobj_addref( &package->hdr );
1486         info->package = package;
1487         info->target = dupstrW(target);
1488         info->source = dupstrW(tmp_file);
1489         TRACE("Start Asynchronous execution of dll\n");
1490         ThreadHandle = CreateThread(NULL,0,DllThread,(LPVOID)info,0,&ThreadId);
1491         CloseHandle(ThreadHandle);
1492         /* FIXME: release the package if the CreateThread fails */
1493         return ERROR_SUCCESS;
1494     }
1495  
1496     DLL = LoadLibraryW(tmp_file);
1497     if (DLL)
1498     {
1499         proc = strdupWtoA( target );
1500         fn = (CustomEntry*)GetProcAddress(DLL,proc);
1501         if (fn)
1502         {
1503             MSIHANDLE hPackage;
1504
1505             TRACE("Calling function\n");
1506             hPackage = msiobj_findhandle( &package->hdr );
1507             if( !hPackage )
1508                 ERR("Handle for object %p not found\n", package );
1509             fn(hPackage);
1510             msiobj_release( &package->hdr );
1511         }
1512         else
1513             ERR("Cannot load functon\n");
1514
1515         HeapFree(GetProcessHeap(),0,proc);
1516         FreeLibrary(DLL);
1517     }
1518     else
1519         ERR("Unable to load library\n");
1520
1521     return ERROR_SUCCESS;
1522 }
1523
1524 static UINT HANDLE_CustomType2(MSIPACKAGE *package, const LPWSTR source, 
1525                                 const LPWSTR target, const INT type)
1526 {
1527     WCHAR tmp_file[MAX_PATH];
1528     STARTUPINFOW si;
1529     PROCESS_INFORMATION info;
1530     BOOL rc;
1531     INT len;
1532     WCHAR *deformated;
1533     WCHAR *cmd;
1534     static const WCHAR spc[] = {' ',0};
1535
1536     memset(&si,0,sizeof(STARTUPINFOW));
1537
1538     store_binary_to_temp(package, source, tmp_file);
1539
1540     deformat_string(package,target,&deformated);
1541
1542     len = strlenW(tmp_file) + strlenW(deformated) + 2;
1543    
1544     cmd = (WCHAR*)HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*len);
1545
1546     strcpyW(cmd,tmp_file);
1547     strcatW(cmd,spc);
1548     strcatW(cmd,deformated);
1549
1550     HeapFree(GetProcessHeap(),0,deformated);
1551
1552     TRACE("executing exe %s \n",debugstr_w(cmd));
1553
1554     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
1555                   c_collen, &si, &info);
1556
1557     HeapFree(GetProcessHeap(),0,cmd);
1558
1559     if ( !rc )
1560     {
1561         ERR("Unable to execute command\n");
1562         return ERROR_SUCCESS;
1563     }
1564
1565     if (!(type & 0xc0))
1566         WaitForSingleObject(info.hProcess,INFINITE);
1567
1568     CloseHandle( info.hProcess );
1569     CloseHandle( info.hThread );
1570     return ERROR_SUCCESS;
1571 }
1572
1573 static UINT HANDLE_CustomType18(MSIPACKAGE *package, const LPWSTR source,
1574                                 const LPWSTR target, const INT type)
1575 {
1576     STARTUPINFOW si;
1577     PROCESS_INFORMATION info;
1578     BOOL rc;
1579     WCHAR *deformated;
1580     WCHAR *cmd;
1581     INT len;
1582     static const WCHAR spc[] = {' ',0};
1583     int index;
1584
1585     memset(&si,0,sizeof(STARTUPINFOW));
1586
1587     index = get_loaded_file(package,source);
1588
1589     len = strlenW(package->files[index].TargetPath);
1590
1591     deformat_string(package,target,&deformated);
1592     len += strlenW(deformated);
1593     len += 2;
1594
1595     cmd = (WCHAR*)HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR));
1596
1597     strcpyW(cmd, package->files[index].TargetPath);
1598     strcatW(cmd, spc);
1599     strcatW(cmd, deformated);
1600
1601     HeapFree(GetProcessHeap(),0,deformated);
1602
1603     TRACE("executing exe %s \n",debugstr_w(cmd));
1604
1605     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
1606                   c_collen, &si, &info);
1607
1608     HeapFree(GetProcessHeap(),0,cmd);
1609     
1610     if ( !rc )
1611     {
1612         ERR("Unable to execute command\n");
1613         return ERROR_SUCCESS;
1614     }
1615
1616     if (!(type & 0xc0))
1617         WaitForSingleObject(info.hProcess,INFINITE);
1618
1619     CloseHandle( info.hProcess );
1620     CloseHandle( info.hThread );
1621     return ERROR_SUCCESS;
1622 }
1623
1624 static UINT HANDLE_CustomType50(MSIPACKAGE *package, const LPWSTR source,
1625                                 const LPWSTR target, const INT type)
1626 {
1627     STARTUPINFOW si;
1628     PROCESS_INFORMATION info;
1629     WCHAR *prop;
1630     BOOL rc;
1631     WCHAR *deformated;
1632     WCHAR *cmd;
1633     INT len;
1634     UINT prc;
1635     static const WCHAR spc[] = {' ',0};
1636
1637     memset(&si,0,sizeof(STARTUPINFOW));
1638     memset(&info,0,sizeof(PROCESS_INFORMATION));
1639
1640     prop = load_dynamic_property(package,source,&prc);
1641     if (!prop)
1642         return prc;
1643
1644     deformat_string(package,target,&deformated);
1645     len = strlenW(prop) + strlenW(deformated) + 2;
1646     cmd = (WCHAR*)HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*len);
1647
1648     strcpyW(cmd,prop);
1649     strcatW(cmd,spc);
1650     strcatW(cmd,deformated);
1651
1652     HeapFree(GetProcessHeap(),0,deformated);
1653
1654     TRACE("executing exe %s \n",debugstr_w(cmd));
1655
1656     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
1657                   c_collen, &si, &info);
1658
1659     HeapFree(GetProcessHeap(),0,cmd);
1660     
1661     if ( !rc )
1662     {
1663         ERR("Unable to execute command\n");
1664         return ERROR_SUCCESS;
1665     }
1666
1667     if (!(type & 0xc0))
1668         WaitForSingleObject(info.hProcess,INFINITE);
1669
1670     CloseHandle( info.hProcess );
1671     CloseHandle( info.hThread );
1672     return ERROR_SUCCESS;
1673 }
1674
1675 static UINT HANDLE_CustomType34(MSIPACKAGE *package, const LPWSTR source,
1676                                 const LPWSTR target, const INT type)
1677 {
1678     LPWSTR filename, deformated;
1679     STARTUPINFOW si;
1680     PROCESS_INFORMATION info;
1681     BOOL rc;
1682
1683     memset(&si,0,sizeof(STARTUPINFOW));
1684
1685     filename = resolve_folder(package, source, FALSE, FALSE, NULL);
1686
1687     if (!filename)
1688         return ERROR_FUNCTION_FAILED;
1689
1690     SetCurrentDirectoryW(filename);
1691     HeapFree(GetProcessHeap(),0,filename);
1692
1693     deformat_string(package,target,&deformated);
1694
1695     TRACE("executing exe %s \n",debugstr_w(deformated));
1696
1697     rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
1698                   c_collen, &si, &info);
1699     HeapFree(GetProcessHeap(),0,deformated);
1700
1701     if ( !rc )
1702     {
1703         ERR("Unable to execute command\n");
1704         return ERROR_SUCCESS;
1705     }
1706
1707     if (!(type & 0xc0))
1708         WaitForSingleObject(info.hProcess,INFINITE);
1709
1710     CloseHandle( info.hProcess );
1711     CloseHandle( info.hThread );
1712     return ERROR_SUCCESS;
1713 }
1714
1715 /***********************************************************************
1716  *            create_full_pathW
1717  *
1718  * Recursively create all directories in the path.
1719  *
1720  * shamelessly stolen from setupapi/queue.c
1721  */
1722 static BOOL create_full_pathW(const WCHAR *path)
1723 {
1724     BOOL ret = TRUE;
1725     int len;
1726     WCHAR *new_path;
1727
1728     new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) *
1729                                               sizeof(WCHAR));
1730
1731     strcpyW(new_path, path);
1732
1733     while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
1734     new_path[len - 1] = 0;
1735
1736     while(!CreateDirectoryW(new_path, NULL))
1737     {
1738         WCHAR *slash;
1739         DWORD last_error = GetLastError();
1740         if(last_error == ERROR_ALREADY_EXISTS)
1741             break;
1742
1743         if(last_error != ERROR_PATH_NOT_FOUND)
1744         {
1745             ret = FALSE;
1746             break;
1747         }
1748
1749         if(!(slash = strrchrW(new_path, '\\')))
1750         {
1751             ret = FALSE;
1752             break;
1753         }
1754
1755         len = slash - new_path;
1756         new_path[len] = 0;
1757         if(!create_full_pathW(new_path))
1758         {
1759             ret = FALSE;
1760             break;
1761         }
1762         new_path[len] = '\\';
1763     }
1764
1765     HeapFree(GetProcessHeap(), 0, new_path);
1766     return ret;
1767 }
1768
1769 /*
1770  * Also we cannot enable/disable components either, so for now I am just going 
1771  * to do all the directories for all the components.
1772  */
1773 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1774 {
1775     static const WCHAR ExecSeqQuery[] = {
1776         's','e','l','e','c','t',' ','D','i','r','e','c','t','o','r','y','_',' ',
1777         'f','r','o','m',' ','C','r','e','a','t','e','F','o','l','d','e','r',0 };
1778     UINT rc;
1779     MSIQUERY *view;
1780     MSIFOLDER *folder;
1781
1782     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1783     if (rc != ERROR_SUCCESS)
1784         return ERROR_SUCCESS;
1785
1786     rc = MSI_ViewExecute(view, 0);
1787     if (rc != ERROR_SUCCESS)
1788     {
1789         MSI_ViewClose(view);
1790         msiobj_release(&view->hdr);
1791         return rc;
1792     }
1793     
1794     while (1)
1795     {
1796         WCHAR dir[0x100];
1797         LPWSTR full_path;
1798         DWORD sz;
1799         MSIRECORD *row = NULL, *uirow;
1800
1801         rc = MSI_ViewFetch(view,&row);
1802         if (rc != ERROR_SUCCESS)
1803         {
1804             rc = ERROR_SUCCESS;
1805             break;
1806         }
1807
1808         sz=0x100;
1809         rc = MSI_RecordGetStringW(row,1,dir,&sz);
1810
1811         if (rc!= ERROR_SUCCESS)
1812         {
1813             ERR("Unable to get folder id \n");
1814             msiobj_release(&row->hdr);
1815             continue;
1816         }
1817
1818         sz = MAX_PATH;
1819         full_path = resolve_folder(package,dir,FALSE,FALSE,&folder);
1820         if (!full_path)
1821         {
1822             ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1823             msiobj_release(&row->hdr);
1824             continue;
1825         }
1826
1827         TRACE("Folder is %s\n",debugstr_w(full_path));
1828
1829         /* UI stuff */
1830         uirow = MSI_CreateRecord(1);
1831         MSI_RecordSetStringW(uirow,1,full_path);
1832         ui_actiondata(package,szCreateFolders,uirow);
1833         msiobj_release( &uirow->hdr );
1834
1835         if (folder->State == 0)
1836             create_full_pathW(full_path);
1837
1838         folder->State = 3;
1839
1840         msiobj_release(&row->hdr);
1841         HeapFree(GetProcessHeap(),0,full_path);
1842     }
1843     MSI_ViewClose(view);
1844     msiobj_release(&view->hdr);
1845    
1846     return rc;
1847 }
1848
1849 static int load_component(MSIPACKAGE* package, MSIRECORD * row)
1850 {
1851     int index = package->loaded_components;
1852     DWORD sz;
1853
1854     /* fill in the data */
1855
1856     package->loaded_components++;
1857     if (package->loaded_components == 1)
1858         package->components = HeapAlloc(GetProcessHeap(),0,
1859                                         sizeof(MSICOMPONENT));
1860     else
1861         package->components = HeapReAlloc(GetProcessHeap(),0,
1862             package->components, package->loaded_components * 
1863             sizeof(MSICOMPONENT));
1864
1865     memset(&package->components[index],0,sizeof(MSICOMPONENT));
1866
1867     sz = 96;       
1868     MSI_RecordGetStringW(row,1,package->components[index].Component,&sz);
1869
1870     TRACE("Loading Component %s\n",
1871            debugstr_w(package->components[index].Component));
1872
1873     sz = 0x100;
1874     if (!MSI_RecordIsNull(row,2))
1875         MSI_RecordGetStringW(row,2,package->components[index].ComponentId,&sz);
1876             
1877     sz = 96;       
1878     MSI_RecordGetStringW(row,3,package->components[index].Directory,&sz);
1879
1880     package->components[index].Attributes = MSI_RecordGetInteger(row,4);
1881
1882     sz = 0x100;       
1883     MSI_RecordGetStringW(row,5,package->components[index].Condition,&sz);
1884
1885     sz = 96;       
1886     MSI_RecordGetStringW(row,6,package->components[index].KeyPath,&sz);
1887
1888     package->components[index].Installed = INSTALLSTATE_ABSENT;
1889     package->components[index].Action = INSTALLSTATE_UNKNOWN;
1890     package->components[index].ActionRequest = INSTALLSTATE_UNKNOWN;
1891
1892     package->components[index].Enabled = TRUE;
1893
1894     return index;
1895 }
1896
1897 static void load_feature(MSIPACKAGE* package, MSIRECORD * row)
1898 {
1899     int index = package->loaded_features;
1900     DWORD sz;
1901     static const WCHAR Query1[] = {'S','E','L','E','C','T',' ','C','o','m','p',
1902         'o','n','e','n','t','_',' ','F','R','O','M',' ','F','e','a','t','u','r','e',
1903         'C','o','m','p','o','n','e','n','t','s',' ','W','H','E','R','E',' ','F','e',
1904         'a','t','u','r','e','_','=','\'','%','s','\'',0};
1905     static const WCHAR Query2[] = {'S','E','L','E','C','T',' ','*',' ','F','R',
1906         'O','M',' ','C','o','m','p','o','n','e','n','t',' ','W','H','E','R','E',' ','C',
1907         'o','m','p','o','n','e','n','t','=','\'','%','s','\'',0};
1908     MSIQUERY * view;
1909     MSIQUERY * view2;
1910     MSIRECORD * row2;
1911     MSIRECORD * row3;
1912     UINT    rc;
1913
1914     /* fill in the data */
1915
1916     package->loaded_features ++;
1917     if (package->loaded_features == 1)
1918         package->features = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFEATURE));
1919     else
1920         package->features = HeapReAlloc(GetProcessHeap(),0,package->features,
1921                                 package->loaded_features * sizeof(MSIFEATURE));
1922
1923     memset(&package->features[index],0,sizeof(MSIFEATURE));
1924     
1925     sz = 96;       
1926     MSI_RecordGetStringW(row,1,package->features[index].Feature,&sz);
1927
1928     TRACE("Loading feature %s\n",debugstr_w(package->features[index].Feature));
1929
1930     sz = 96;
1931     if (!MSI_RecordIsNull(row,2))
1932         MSI_RecordGetStringW(row,2,package->features[index].Feature_Parent,&sz);
1933
1934     sz = 0x100;
1935      if (!MSI_RecordIsNull(row,3))
1936         MSI_RecordGetStringW(row,3,package->features[index].Title,&sz);
1937
1938      sz = 0x100;
1939      if (!MSI_RecordIsNull(row,4))
1940         MSI_RecordGetStringW(row,4,package->features[index].Description,&sz);
1941
1942     if (!MSI_RecordIsNull(row,5))
1943         package->features[index].Display = MSI_RecordGetInteger(row,5);
1944   
1945     package->features[index].Level= MSI_RecordGetInteger(row,6);
1946
1947      sz = 96;
1948      if (!MSI_RecordIsNull(row,7))
1949         MSI_RecordGetStringW(row,7,package->features[index].Directory,&sz);
1950
1951     package->features[index].Attributes= MSI_RecordGetInteger(row,8);
1952
1953     package->features[index].Installed = INSTALLSTATE_ABSENT;
1954     package->features[index].Action = INSTALLSTATE_UNKNOWN;
1955     package->features[index].ActionRequest = INSTALLSTATE_UNKNOWN;
1956
1957     /* load feature components */
1958
1959     rc = ACTION_OpenQuery(package->db, &view, Query1, package->features[index].Feature);
1960     if (rc != ERROR_SUCCESS)
1961         return;
1962     rc = MSI_ViewExecute(view,0);
1963     if (rc != ERROR_SUCCESS)
1964     {
1965         MSI_ViewClose(view);
1966         msiobj_release(&view->hdr);
1967         return;
1968     }
1969     while (1)
1970     {
1971         DWORD sz = 0x100;
1972         WCHAR buffer[0x100];
1973         DWORD rc;
1974         INT c_indx;
1975         INT cnt = package->features[index].ComponentCount;
1976
1977         rc = MSI_ViewFetch(view,&row2);
1978         if (rc != ERROR_SUCCESS)
1979             break;
1980
1981         sz = 0x100;
1982         MSI_RecordGetStringW(row2,1,buffer,&sz);
1983
1984         /* check to see if the component is already loaded */
1985         c_indx = get_loaded_component(package,buffer);
1986         if (c_indx != -1)
1987         {
1988             TRACE("Component %s already loaded at %i\n", debugstr_w(buffer),
1989                   c_indx);
1990             package->features[index].Components[cnt] = c_indx;
1991             package->features[index].ComponentCount ++;
1992             continue;
1993         }
1994
1995         rc = ACTION_OpenQuery(package->db, &view2, Query2, buffer);
1996         if (rc != ERROR_SUCCESS)
1997         {
1998             msiobj_release( &row2->hdr );
1999             continue;
2000         }
2001         rc = MSI_ViewExecute(view2,0);
2002         if (rc != ERROR_SUCCESS)
2003         {
2004             msiobj_release( &row2->hdr );
2005             MSI_ViewClose(view2);
2006             msiobj_release( &view2->hdr );  
2007             continue;
2008         }
2009         while (1)
2010         {
2011             DWORD rc;
2012
2013             rc = MSI_ViewFetch(view2,&row3);
2014             if (rc != ERROR_SUCCESS)
2015                 break;
2016             c_indx = load_component(package,row3);
2017             msiobj_release( &row3->hdr );
2018
2019             package->features[index].Components[cnt] = c_indx;
2020             package->features[index].ComponentCount ++;
2021             TRACE("Loaded new component to index %i\n",c_indx);
2022         }
2023         MSI_ViewClose(view2);
2024         msiobj_release( &view2->hdr );
2025         msiobj_release( &row2->hdr );
2026     }
2027     MSI_ViewClose(view);
2028     msiobj_release(&view->hdr);
2029 }
2030
2031 /*
2032  * I am not doing any of the costing functionality yet. 
2033  * Mostly looking at doing the Component and Feature loading
2034  *
2035  * The native MSI does ALOT of modification to tables here. Mostly adding alot
2036  * of temporary columns to the Feature and Component tables. 
2037  *
2038  *    note: native msi also tracks the short filename. but I am only going to
2039  *          track the long ones.  Also looking at this directory table
2040  *          it appears that the directory table does not get the parents
2041  *          resolved base on property only based on their entrys in the 
2042  *          directory table.
2043  */
2044 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
2045 {
2046     MSIQUERY * view;
2047     MSIRECORD * row;
2048     UINT rc;
2049     static const WCHAR Query_all[] = {
2050        'S','E','L','E','C','T',' ','*',' ',
2051        'F','R','O','M',' ','F','e','a','t','u','r','e',0};
2052     static const WCHAR szCosting[] = {
2053        'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2054     static const WCHAR szZero[] = { '0', 0 };
2055
2056     MSI_SetPropertyW(package, szCosting, szZero);
2057     MSI_SetPropertyW(package, cszRootDrive , c_collen);
2058
2059     rc = MSI_DatabaseOpenViewW(package->db,Query_all,&view);
2060     if (rc != ERROR_SUCCESS)
2061         return rc;
2062     rc = MSI_ViewExecute(view,0);
2063     if (rc != ERROR_SUCCESS)
2064     {
2065         MSI_ViewClose(view);
2066         msiobj_release(&view->hdr);
2067         return rc;
2068     }
2069     while (1)
2070     {
2071         DWORD rc;
2072
2073         rc = MSI_ViewFetch(view,&row);
2074         if (rc != ERROR_SUCCESS)
2075             break;
2076        
2077         load_feature(package,row); 
2078         msiobj_release(&row->hdr);
2079     }
2080     MSI_ViewClose(view);
2081     msiobj_release(&view->hdr);
2082
2083     return ERROR_SUCCESS;
2084 }
2085
2086 static UINT load_file(MSIPACKAGE* package, MSIRECORD * row)
2087 {
2088     DWORD index = package->loaded_files;
2089     DWORD i;
2090     LPWSTR buffer;
2091
2092     /* fill in the data */
2093
2094     package->loaded_files++;
2095     if (package->loaded_files== 1)
2096         package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
2097     else
2098         package->files = HeapReAlloc(GetProcessHeap(),0,
2099             package->files , package->loaded_files * sizeof(MSIFILE));
2100
2101     memset(&package->files[index],0,sizeof(MSIFILE));
2102  
2103     package->files[index].File = load_dynamic_stringW(row, 1);
2104     buffer = load_dynamic_stringW(row, 2);
2105
2106     package->files[index].ComponentIndex = -1;
2107     for (i = 0; i < package->loaded_components; i++)
2108         if (strcmpW(package->components[i].Component,buffer)==0)
2109         {
2110             package->files[index].ComponentIndex = i;
2111             break;
2112         }
2113     if (package->files[index].ComponentIndex == -1)
2114         ERR("Unfound Component %s\n",debugstr_w(buffer));
2115     HeapFree(GetProcessHeap(), 0, buffer);
2116
2117     package->files[index].FileName = load_dynamic_stringW(row,3);
2118
2119     reduce_to_longfilename(package->files[index].FileName);
2120     
2121     package->files[index].FileSize = MSI_RecordGetInteger(row,4);
2122     package->files[index].Version = load_dynamic_stringW(row, 5);
2123     package->files[index].Language = load_dynamic_stringW(row, 6);
2124     package->files[index].Attributes= MSI_RecordGetInteger(row,7);
2125     package->files[index].Sequence= MSI_RecordGetInteger(row,8);
2126
2127     package->files[index].Temporary = FALSE;
2128     package->files[index].State = 0;
2129
2130     TRACE("File Loaded (%s)\n",debugstr_w(package->files[index].File));  
2131  
2132     return ERROR_SUCCESS;
2133 }
2134
2135 static UINT ACTION_FileCost(MSIPACKAGE *package)
2136 {
2137     MSIQUERY * view;
2138     MSIRECORD * row;
2139     UINT rc;
2140     static const WCHAR Query[] = {
2141         'S','E','L','E','C','T',' ','*',' ',
2142         'F','R','O','M',' ','F','i','l','e',' ',
2143         'O','r','d','e','r',' ','b','y',' ','S','e','q','u','e','n','c','e', 0};
2144
2145     if (!package)
2146         return ERROR_INVALID_HANDLE;
2147
2148     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2149     if (rc != ERROR_SUCCESS)
2150         return ERROR_SUCCESS;
2151    
2152     rc = MSI_ViewExecute(view, 0);
2153     if (rc != ERROR_SUCCESS)
2154     {
2155         MSI_ViewClose(view);
2156         msiobj_release(&view->hdr);
2157         return ERROR_SUCCESS;
2158     }
2159
2160     while (1)
2161     {
2162         rc = MSI_ViewFetch(view,&row);
2163         if (rc != ERROR_SUCCESS)
2164         {
2165             rc = ERROR_SUCCESS;
2166             break;
2167         }
2168         load_file(package,row);
2169         msiobj_release(&row->hdr);
2170     }
2171     MSI_ViewClose(view);
2172     msiobj_release(&view->hdr);
2173
2174     return ERROR_SUCCESS;
2175 }
2176
2177 static INT load_folder(MSIPACKAGE *package, const WCHAR* dir)
2178
2179 {
2180     static const WCHAR Query[] =
2181         {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','D','i','r','e','c',
2182          't','o','r','y',' ','w','h','e','r','e',' ','`','D','i','r','e','c','t',
2183          'o','r','y','`',' ','=',' ','`','%','s','`',0};
2184     UINT rc;
2185     MSIQUERY * view;
2186     LPWSTR targetdir, parent, srcdir;
2187     MSIRECORD * row = 0;
2188     INT index = -1;
2189     DWORD i;
2190
2191     TRACE("Looking for dir %s\n",debugstr_w(dir));
2192
2193     for (i = 0; i < package->loaded_folders; i++)
2194     {
2195         if (strcmpW(package->folders[i].Directory,dir)==0)
2196         {
2197             TRACE(" %s retuning on index %lu\n",debugstr_w(dir),i);
2198             return i;
2199         }
2200     }
2201
2202     TRACE("Working to load %s\n",debugstr_w(dir));
2203
2204     index = package->loaded_folders++;
2205     if (package->loaded_folders==1)
2206         package->folders = HeapAlloc(GetProcessHeap(),0,
2207                                         sizeof(MSIFOLDER));
2208     else
2209         package->folders= HeapReAlloc(GetProcessHeap(),0,
2210             package->folders, package->loaded_folders* 
2211             sizeof(MSIFOLDER));
2212
2213     memset(&package->folders[index],0,sizeof(MSIFOLDER));
2214
2215     package->folders[index].Directory = dupstrW(dir);
2216
2217     rc = ACTION_OpenQuery(package->db, &view, Query, dir);
2218     if (rc != ERROR_SUCCESS)
2219         return -1;
2220
2221     rc = MSI_ViewExecute(view, 0);
2222     if (rc != ERROR_SUCCESS)
2223     {
2224         MSI_ViewClose(view);
2225         msiobj_release(&view->hdr);
2226         return -1;
2227     }
2228
2229     rc = MSI_ViewFetch(view,&row);
2230     if (rc != ERROR_SUCCESS)
2231     {
2232         MSI_ViewClose(view);
2233         msiobj_release(&view->hdr);
2234         return -1;
2235     }
2236
2237     targetdir = load_dynamic_stringW(row,3);
2238
2239     /* split src and target dir */
2240     if (strchrW(targetdir,':'))
2241     {
2242         srcdir=strchrW(targetdir,':');
2243         *srcdir=0;
2244         srcdir ++;
2245     }
2246     else
2247         srcdir=NULL;
2248
2249     /* for now only pick long filename versions */
2250     if (strchrW(targetdir,'|'))
2251     {
2252         targetdir = strchrW(targetdir,'|'); 
2253         *targetdir = 0;
2254         targetdir ++;
2255     }
2256     if (srcdir && strchrW(srcdir,'|'))
2257     {
2258         srcdir= strchrW(srcdir,'|'); 
2259         *srcdir= 0;
2260         srcdir ++;
2261     }
2262
2263     /* now check for root dirs */
2264     if (targetdir[0] == '.' && targetdir[1] == 0)
2265         targetdir = NULL;
2266         
2267     if (srcdir && srcdir[0] == '.' && srcdir[1] == 0)
2268         srcdir = NULL;
2269
2270     if (targetdir)
2271     {
2272         TRACE("   TargetDefault = %s\n",debugstr_w(targetdir));
2273         if (package->folders[index].TargetDefault)
2274             HeapFree(GetProcessHeap(),0, package->folders[index].TargetDefault);
2275         package->folders[index].TargetDefault = dupstrW(targetdir);
2276     }
2277
2278     if (srcdir)
2279        package->folders[index].SourceDefault = dupstrW(srcdir);
2280     else if (targetdir)
2281         package->folders[index].SourceDefault = dupstrW(targetdir);
2282     HeapFree(GetProcessHeap(), 0, targetdir);
2283
2284     parent = load_dynamic_stringW(row,2);
2285     if (parent) 
2286     {
2287         i = load_folder(package,parent);
2288         package->folders[index].ParentIndex = i;
2289         TRACE("Parent is index %i... %s %s\n",
2290                     package->folders[index].ParentIndex,
2291         debugstr_w(package->folders[package->folders[index].ParentIndex].Directory),
2292                     debugstr_w(parent));
2293     }
2294     else
2295         package->folders[index].ParentIndex = -2;
2296     HeapFree(GetProcessHeap(), 0, parent);
2297
2298     package->folders[index].Property = load_dynamic_property(package, dir,NULL);
2299
2300     msiobj_release(&row->hdr);
2301     MSI_ViewClose(view);
2302     msiobj_release(&view->hdr);
2303     TRACE(" %s retuning on index %i\n",debugstr_w(dir),index);
2304     return index;
2305 }
2306
2307
2308 static LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name,
2309                            BOOL source, BOOL set_prop, MSIFOLDER **folder)
2310 {
2311     DWORD i;
2312     LPWSTR p, path = NULL;
2313
2314     TRACE("Working to resolve %s\n",debugstr_w(name));
2315
2316     /* special resolving for Target and Source root dir */
2317     if (strcmpW(name,cszTargetDir)==0 || strcmpW(name,cszSourceDir)==0)
2318     {
2319         if (!source)
2320         {
2321             path = load_dynamic_property(package,cszTargetDir,NULL);
2322             if (!path)
2323             {
2324                 path = load_dynamic_property(package,cszRootDrive,NULL);
2325                 if (set_prop)
2326                     MSI_SetPropertyW(package,cszTargetDir,path);
2327             }
2328             if (folder)
2329             {
2330                 for (i = 0; i < package->loaded_folders; i++)
2331                 {
2332                     if (strcmpW(package->folders[i].Directory,name)==0)
2333                         break;
2334                 }
2335                 *folder = &(package->folders[i]);
2336             }
2337             return path;
2338         }
2339         else
2340         {
2341             path = load_dynamic_property(package,cszSourceDir,NULL);
2342             if (!path)
2343             {
2344                 path = load_dynamic_property(package,cszDatabase,NULL);
2345                 if (path)
2346                 {
2347                     p = strrchrW(path,'\\');
2348                     if (p)
2349                         *(p+1) = 0;
2350                 }
2351             }
2352             if (folder)
2353             {
2354                 for (i = 0; i < package->loaded_folders; i++)
2355                 {
2356                     if (strcmpW(package->folders[i].Directory,name)==0)
2357                         break;
2358                 }
2359                 *folder = &(package->folders[i]);
2360             }
2361             return path;
2362         }
2363     }
2364
2365     for (i = 0; i < package->loaded_folders; i++)
2366     {
2367         if (strcmpW(package->folders[i].Directory,name)==0)
2368             break;
2369     }
2370
2371     if (i >= package->loaded_folders)
2372         return NULL;
2373
2374     if (folder)
2375         *folder = &(package->folders[i]);
2376
2377     if (!source && package->folders[i].ResolvedTarget)
2378     {
2379         path = dupstrW(package->folders[i].ResolvedTarget);
2380         TRACE("   already resolved to %s\n",debugstr_w(path));
2381         return path;
2382     }
2383     else if (source && package->folders[i].ResolvedSource)
2384     {
2385         path = dupstrW(package->folders[i].ResolvedSource);
2386         return path;
2387     }
2388     else if (!source && package->folders[i].Property)
2389     {
2390         path = dupstrW(package->folders[i].Property);
2391         TRACE("   internally set to %s\n",debugstr_w(path));
2392         if (set_prop)
2393             MSI_SetPropertyW(package,name,path);
2394         return path;
2395     }
2396
2397     if (package->folders[i].ParentIndex >= 0)
2398     {
2399         LPWSTR parent = package->folders[package->folders[i].ParentIndex].Directory;
2400
2401         TRACE(" ! Parent is %s\n", debugstr_w(parent));
2402
2403         p = resolve_folder(package, parent, source, set_prop, NULL);
2404         if (!source)
2405         {
2406             TRACE("   TargetDefault = %s\n",debugstr_w(package->folders[i].TargetDefault));
2407             path = build_directory_name(3, p, package->folders[i].TargetDefault, NULL);
2408             package->folders[i].ResolvedTarget = dupstrW(path);
2409             TRACE("   resolved into %s\n",debugstr_w(path));
2410             if (set_prop)
2411                 MSI_SetPropertyW(package,name,path);
2412         }
2413         else 
2414         {
2415             path = build_directory_name(3, p, package->folders[i].SourceDefault, NULL);
2416             package->folders[i].ResolvedSource = dupstrW(path);
2417         }
2418         HeapFree(GetProcessHeap(),0,p);
2419     }
2420     return path;
2421 }
2422
2423 static UINT SetFeatureStates(MSIPACKAGE *package)
2424 {
2425     LPWSTR level;
2426     INT install_level;
2427     DWORD i;
2428     INT j;
2429     LPWSTR override = NULL;
2430     static const WCHAR all[]={'A','L','L',0};
2431     static const WCHAR szlevel[] = {
2432         'I','N','S','T','A','L','L','L','E','V','E','L',0};
2433     static const WCHAR szAddLocal[] = {
2434         'A','D','D','L','O','C','A','L',0};
2435
2436     /* I do not know if this is where it should happen.. but */
2437
2438     TRACE("Checking Install Level\n");
2439
2440     level = load_dynamic_property(package,szlevel,NULL);
2441     if (level)
2442     {
2443         install_level = atoiW(level);
2444         HeapFree(GetProcessHeap(), 0, level);
2445     }
2446     else
2447         install_level = 1;
2448
2449     /* ok hereis the rub
2450      * ADDLOCAL and its friend OVERRIDE INSTALLLEVLE
2451      * I have confirmed this if ADDLOCALis stated then the INSTALLLEVEL is
2452      * itnored for all the features. seems strange, epsecially since it is not
2453      * documented anywhere, but it is how it works. 
2454      */
2455     
2456     override = load_dynamic_property(package,szAddLocal,NULL);
2457   
2458     if (override)
2459     {
2460         for(i = 0; i < package->loaded_features; i++)
2461         {
2462             if (strcmpiW(override,all)==0)
2463             {
2464                 package->features[i].ActionRequest= INSTALLSTATE_LOCAL;
2465                 package->features[i].Action = INSTALLSTATE_LOCAL;
2466             }
2467             else
2468             {
2469                 LPWSTR ptr = override;
2470                 LPWSTR ptr2 = strchrW(override,',');
2471
2472                 while (ptr)
2473                 {
2474                     if ((ptr2 && 
2475                         strncmpW(ptr,package->features[i].Feature, ptr2-ptr)==0)
2476                         || (!ptr2 &&
2477                         strcmpW(ptr,package->features[i].Feature)==0))
2478                     {
2479                         package->features[i].ActionRequest= INSTALLSTATE_LOCAL;
2480                         package->features[i].Action = INSTALLSTATE_LOCAL;
2481                         break;
2482                     }
2483                     if (ptr2)
2484                     {
2485                         ptr=ptr2+1;
2486                         ptr2 = strchrW(ptr,',');
2487                     }
2488                     else
2489                         break;
2490                 }
2491             }
2492         }
2493         HeapFree(GetProcessHeap(),0,override);
2494     } 
2495     else
2496     {
2497         for(i = 0; i < package->loaded_features; i++)
2498         {
2499             BOOL feature_state= ((package->features[i].Level > 0) &&
2500                              (package->features[i].Level <= install_level));
2501
2502             if (feature_state)
2503             {
2504                 package->features[i].ActionRequest= INSTALLSTATE_LOCAL;
2505                 package->features[i].Action = INSTALLSTATE_LOCAL;
2506             }
2507         }
2508     }
2509
2510     /*
2511      * now we want to enable or disable components base on feature 
2512     */
2513
2514     for(i = 0; i < package->loaded_features; i++)
2515     {
2516         MSIFEATURE* feature = &package->features[i];
2517         TRACE("Examining Feature %s (Installed %i, Action %i, Request %i)\n",
2518             debugstr_w(feature->Feature), feature->Installed, feature->Action,
2519             feature->ActionRequest);
2520
2521         for( j = 0; j < feature->ComponentCount; j++)
2522         {
2523             MSICOMPONENT* component = &package->components[
2524                                                     feature->Components[j]];
2525
2526             if (!component->Enabled)
2527             {
2528                 component->Action = INSTALLSTATE_ABSENT;
2529                 component->ActionRequest = INSTALLSTATE_ABSENT;
2530             }
2531             else
2532             {
2533                 if (feature->Action == INSTALLSTATE_LOCAL)
2534                     component->Action = INSTALLSTATE_LOCAL;
2535                 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
2536                     component->ActionRequest = INSTALLSTATE_LOCAL;
2537             }
2538         }
2539     } 
2540
2541     for(i = 0; i < package->loaded_components; i++)
2542     {
2543         MSICOMPONENT* component= &package->components[i];
2544
2545         TRACE("Result: Component %s (Installed %i, Action %i, Request %i)\n",
2546             debugstr_w(component->Component), component->Installed, 
2547             component->Action, component->ActionRequest);
2548     }
2549
2550
2551     return ERROR_SUCCESS;
2552 }
2553
2554 /* 
2555  * Alot is done in this function aside from just the costing.
2556  * The costing needs to be implemented at some point but for now I am going
2557  * to focus on the directory building
2558  *
2559  */
2560 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2561 {
2562     static const WCHAR ExecSeqQuery[] = {
2563         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ',
2564         'D','i','r','e','c','t','o','r','y',0};
2565     static const WCHAR ConditionQuery[] = {
2566         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ',
2567         'C','o','n','d','i','t','i','o','n',0};
2568     static const WCHAR szCosting[] = {
2569        'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2570     static const WCHAR szlevel[] = {
2571         'I','N','S','T','A','L','L','L','E','V','E','L',0};
2572     static const WCHAR szOne[] = { '1', 0 };
2573     UINT rc;
2574     MSIQUERY * view;
2575     DWORD i;
2576     LPWSTR level;
2577
2578     TRACE("Building Directory properties\n");
2579
2580     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2581     if (rc == ERROR_SUCCESS)
2582     {
2583         rc = MSI_ViewExecute(view, 0);
2584         if (rc != ERROR_SUCCESS)
2585         {
2586             MSI_ViewClose(view);
2587             msiobj_release(&view->hdr);
2588             return rc;
2589         }
2590
2591         while (1)
2592         {
2593             WCHAR name[0x100];
2594             LPWSTR path;
2595             MSIRECORD * row = 0;
2596             DWORD sz;
2597
2598             rc = MSI_ViewFetch(view,&row);
2599             if (rc != ERROR_SUCCESS)
2600             {
2601                 rc = ERROR_SUCCESS;
2602                 break;
2603             }
2604
2605             sz=0x100;
2606             MSI_RecordGetStringW(row,1,name,&sz);
2607
2608             /* This helper function now does ALL the work */
2609             TRACE("Dir %s ...\n",debugstr_w(name));
2610             load_folder(package,name);
2611             path = resolve_folder(package,name,FALSE,TRUE,NULL);
2612             TRACE("resolves to %s\n",debugstr_w(path));
2613             HeapFree( GetProcessHeap(), 0, path);
2614
2615             msiobj_release(&row->hdr);
2616         }
2617         MSI_ViewClose(view);
2618         msiobj_release(&view->hdr);
2619     }
2620
2621     TRACE("File calculations %i files\n",package->loaded_files);
2622
2623     for (i = 0; i < package->loaded_files; i++)
2624     {
2625         MSICOMPONENT* comp = NULL;
2626         MSIFILE* file= NULL;
2627
2628         file = &package->files[i];
2629         if (file->ComponentIndex >= 0)
2630             comp = &package->components[file->ComponentIndex];
2631
2632         if (file->Temporary == TRUE)
2633             continue;
2634
2635         if (comp)
2636         {
2637             LPWSTR p;
2638
2639             /* calculate target */
2640             p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
2641
2642             if (file->TargetPath)
2643                 HeapFree(GetProcessHeap(),0,file->TargetPath);
2644
2645             TRACE("file %s is named %s\n",
2646                    debugstr_w(file->File),debugstr_w(file->FileName));       
2647
2648             file->TargetPath = build_directory_name(2, p, file->FileName);
2649
2650             HeapFree(GetProcessHeap(),0,p);
2651
2652             TRACE("file %s resolves to %s\n",
2653                    debugstr_w(file->File),debugstr_w(file->TargetPath));       
2654
2655             if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2656             {
2657                 file->State = 1;
2658                 comp->Cost += file->FileSize;
2659             }
2660             else
2661             {
2662                 if (file->Version)
2663                 {
2664                     DWORD handle;
2665                     DWORD versize;
2666                     UINT sz;
2667                     LPVOID version;
2668                     static const WCHAR name[] = 
2669                     {'\\',0};
2670                     static const WCHAR name_fmt[] = 
2671                     {'%','u','.','%','u','.','%','u','.','%','u',0};
2672                     WCHAR filever[0x100];
2673                     VS_FIXEDFILEINFO *lpVer;
2674
2675                     FIXME("Version comparison.. \n");
2676                     versize = GetFileVersionInfoSizeW(file->TargetPath,&handle);
2677                     version = HeapAlloc(GetProcessHeap(),0,versize);
2678                     GetFileVersionInfoW(file->TargetPath, 0, versize, version);
2679
2680                     VerQueryValueW(version, name, (LPVOID*)&lpVer, &sz);
2681
2682                     sprintfW(filever,name_fmt,
2683                         HIWORD(lpVer->dwFileVersionMS),
2684                         LOWORD(lpVer->dwFileVersionMS),
2685                         HIWORD(lpVer->dwFileVersionLS),
2686                         LOWORD(lpVer->dwFileVersionLS));
2687
2688                     TRACE("new %s old %s\n", debugstr_w(file->Version),
2689                           debugstr_w(filever));
2690                     if (strcmpiW(filever,file->Version)<0)
2691                     {
2692                         file->State = 2;
2693                         FIXME("cost should be diff in size\n");
2694                         comp->Cost += file->FileSize;
2695                     }
2696                     else
2697                         file->State = 3;
2698                     HeapFree(GetProcessHeap(),0,version);
2699                 }
2700                 else
2701                     file->State = 3;
2702             }
2703         } 
2704     }
2705
2706     TRACE("Evaluating Condition Table\n");
2707
2708     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2709     if (rc == ERROR_SUCCESS)
2710     {
2711         rc = MSI_ViewExecute(view, 0);
2712         if (rc != ERROR_SUCCESS)
2713         {
2714             MSI_ViewClose(view);
2715             msiobj_release(&view->hdr);
2716             return rc;
2717         }
2718     
2719         while (1)
2720         {
2721             WCHAR Feature[0x100];
2722             MSIRECORD * row = 0;
2723             DWORD sz;
2724             int feature_index;
2725
2726             rc = MSI_ViewFetch(view,&row);
2727
2728             if (rc != ERROR_SUCCESS)
2729             {
2730                 rc = ERROR_SUCCESS;
2731                 break;
2732             }
2733
2734             sz = 0x100;
2735             MSI_RecordGetStringW(row,1,Feature,&sz);
2736
2737             feature_index = get_loaded_feature(package,Feature);
2738             if (feature_index < 0)
2739                 ERR("FAILED to find loaded feature %s\n",debugstr_w(Feature));
2740             else
2741             {
2742                 LPWSTR Condition;
2743                 Condition = load_dynamic_stringW(row,3);
2744
2745                 if (MSI_EvaluateConditionW(package,Condition) == 
2746                     MSICONDITION_TRUE)
2747                 {
2748                     int level = MSI_RecordGetInteger(row,2);
2749                     TRACE("Reseting feature %s to level %i\n",
2750                            debugstr_w(Feature), level);
2751                     package->features[feature_index].Level = level;
2752                 }
2753                 HeapFree(GetProcessHeap(),0,Condition);
2754             }
2755
2756             msiobj_release(&row->hdr);
2757         }
2758         MSI_ViewClose(view);
2759         msiobj_release(&view->hdr);
2760     }
2761
2762     TRACE("Enabling or Disabling Components\n");
2763     for (i = 0; i < package->loaded_components; i++)
2764     {
2765         if (package->components[i].Condition[0])
2766         {
2767             if (MSI_EvaluateConditionW(package,
2768                 package->components[i].Condition) == MSICONDITION_FALSE)
2769             {
2770                 TRACE("Disabling component %s\n",
2771                       debugstr_w(package->components[i].Component));
2772                 package->components[i].Enabled = FALSE;
2773             }
2774         }
2775     }
2776
2777     MSI_SetPropertyW(package,szCosting,szOne);
2778     /* set default run level if not set */
2779     level = load_dynamic_property(package,szlevel,NULL);
2780     if (!level)
2781         MSI_SetPropertyW(package,szlevel, szOne);
2782     else
2783         HeapFree(GetProcessHeap(),0,level);
2784
2785     return SetFeatureStates(package);
2786
2787 }
2788
2789 /*
2790  * This is a helper function for handling embedded cabinet media
2791  */
2792 static UINT writeout_cabinet_stream(MSIPACKAGE *package, WCHAR* stream_name,
2793                                     WCHAR* source)
2794 {
2795     UINT rc;
2796     USHORT* data;
2797     UINT    size;
2798     DWORD   write;
2799     HANDLE  the_file;
2800     WCHAR tmp[MAX_PATH];
2801
2802     rc = read_raw_stream_data(package->db,stream_name,&data,&size); 
2803     if (rc != ERROR_SUCCESS)
2804         return rc;
2805
2806     write = MAX_PATH;
2807     if (MSI_GetPropertyW(package, cszTempFolder, tmp, &write))
2808         GetTempPathW(MAX_PATH,tmp);
2809
2810     GetTempFileNameW(tmp,stream_name,0,source);
2811
2812     track_tempfile(package,strrchrW(source,'\\'), source);
2813     the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
2814                            FILE_ATTRIBUTE_NORMAL, NULL);
2815
2816     if (the_file == INVALID_HANDLE_VALUE)
2817     {
2818         rc = ERROR_FUNCTION_FAILED;
2819         goto end;
2820     }
2821
2822     WriteFile(the_file,data,size,&write,NULL);
2823     CloseHandle(the_file);
2824     TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
2825 end:
2826     HeapFree(GetProcessHeap(),0,data);
2827     return rc;
2828 }
2829
2830
2831 /* Support functions for FDI functions */
2832 typedef struct
2833 {
2834     MSIPACKAGE* package;
2835     LPCSTR cab_path;
2836     LPCSTR file_name;
2837 } CabData;
2838
2839 static void * cabinet_alloc(ULONG cb)
2840 {
2841     return HeapAlloc(GetProcessHeap(), 0, cb);
2842 }
2843
2844 static void cabinet_free(void *pv)
2845 {
2846     HeapFree(GetProcessHeap(), 0, pv);
2847 }
2848
2849 static INT_PTR cabinet_open(char *pszFile, int oflag, int pmode)
2850 {
2851     DWORD dwAccess = 0;
2852     DWORD dwShareMode = 0;
2853     DWORD dwCreateDisposition = OPEN_EXISTING;
2854     switch (oflag & _O_ACCMODE)
2855     {
2856     case _O_RDONLY:
2857         dwAccess = GENERIC_READ;
2858         dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE;
2859         break;
2860     case _O_WRONLY:
2861         dwAccess = GENERIC_WRITE;
2862         dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
2863         break;
2864     case _O_RDWR:
2865         dwAccess = GENERIC_READ | GENERIC_WRITE;
2866         dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
2867         break;
2868     }
2869     if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL))
2870         dwCreateDisposition = CREATE_NEW;
2871     else if (oflag & _O_CREAT)
2872         dwCreateDisposition = CREATE_ALWAYS;
2873     return (INT_PTR)CreateFileA(pszFile, dwAccess, dwShareMode, NULL, dwCreateDisposition, 0, NULL);
2874 }
2875
2876 static UINT cabinet_read(INT_PTR hf, void *pv, UINT cb)
2877 {
2878     DWORD dwRead;
2879     if (ReadFile((HANDLE)hf, pv, cb, &dwRead, NULL))
2880         return dwRead;
2881     return 0;
2882 }
2883
2884 static UINT cabinet_write(INT_PTR hf, void *pv, UINT cb)
2885 {
2886     DWORD dwWritten;
2887     if (WriteFile((HANDLE)hf, pv, cb, &dwWritten, NULL))
2888         return dwWritten;
2889     return 0;
2890 }
2891
2892 static int cabinet_close(INT_PTR hf)
2893 {
2894     return CloseHandle((HANDLE)hf) ? 0 : -1;
2895 }
2896
2897 static long cabinet_seek(INT_PTR hf, long dist, int seektype)
2898 {
2899     /* flags are compatible and so are passed straight through */
2900     return SetFilePointer((HANDLE)hf, dist, NULL, seektype);
2901 }
2902
2903 static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
2904 {
2905     /* FIXME: try to do more processing in this function */
2906     switch (fdint)
2907     {
2908     case fdintCOPY_FILE:
2909     {
2910         CabData *data = (CabData*) pfdin->pv;
2911         ULONG len = strlen(data->cab_path) + strlen(pfdin->psz1);
2912         char *file;
2913
2914         LPWSTR trackname;
2915         LPWSTR trackpath;
2916         LPWSTR tracknametmp;
2917         static const WCHAR tmpprefix[] = {'C','A','B','T','M','P','_',0};
2918        
2919         if (data->file_name && strcmp(data->file_name,pfdin->psz1))
2920                 return 0;
2921         
2922         file = cabinet_alloc((len+1)*sizeof(char));
2923         strcpy(file, data->cab_path);
2924         strcat(file, pfdin->psz1);
2925
2926         TRACE("file: %s\n", debugstr_a(file));
2927
2928         /* track this file so it can be deleted if not installed */
2929         trackpath=strdupAtoW(file);
2930         tracknametmp=strdupAtoW(strrchr(file,'\\')+1);
2931         trackname = HeapAlloc(GetProcessHeap(),0,(strlenW(tracknametmp) + 
2932                                   strlenW(tmpprefix)+1) * sizeof(WCHAR));
2933
2934         strcpyW(trackname,tmpprefix);
2935         strcatW(trackname,tracknametmp);
2936
2937         track_tempfile(data->package, trackname, trackpath);
2938
2939         HeapFree(GetProcessHeap(),0,trackpath);
2940         HeapFree(GetProcessHeap(),0,trackname);
2941         HeapFree(GetProcessHeap(),0,tracknametmp);
2942
2943         return cabinet_open(file, _O_WRONLY | _O_CREAT, 0);
2944     }
2945     case fdintCLOSE_FILE_INFO:
2946     {
2947         FILETIME ft;
2948             FILETIME ftLocal;
2949         if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft))
2950             return -1;
2951         if (!LocalFileTimeToFileTime(&ft, &ftLocal))
2952             return -1;
2953         if (!SetFileTime((HANDLE)pfdin->hf, &ftLocal, 0, &ftLocal))
2954             return -1;
2955
2956         cabinet_close(pfdin->hf);
2957         return 1;
2958     }
2959     default:
2960         return 0;
2961     }
2962 }
2963
2964 /***********************************************************************
2965  *            extract_cabinet_file
2966  *
2967  * Extract files from a cab file.
2968  */
2969 static BOOL extract_a_cabinet_file(MSIPACKAGE* package, const WCHAR* source, 
2970                                  const WCHAR* path, const WCHAR* file)
2971 {
2972     HFDI hfdi;
2973     ERF erf;
2974     BOOL ret;
2975     char *cabinet;
2976     char *cab_path;
2977     char *file_name;
2978     CabData data;
2979
2980     TRACE("Extracting %s (%s) to %s\n",debugstr_w(source), 
2981                     debugstr_w(file), debugstr_w(path));
2982
2983     hfdi = FDICreate(cabinet_alloc,
2984                      cabinet_free,
2985                      cabinet_open,
2986                      cabinet_read,
2987                      cabinet_write,
2988                      cabinet_close,
2989                      cabinet_seek,
2990                      0,
2991                      &erf);
2992     if (!hfdi)
2993     {
2994         ERR("FDICreate failed\n");
2995         return FALSE;
2996     }
2997
2998     if (!(cabinet = strdupWtoA( source )))
2999     {
3000         FDIDestroy(hfdi);
3001         return FALSE;
3002     }
3003     if (!(cab_path = strdupWtoA( path )))
3004     {
3005         FDIDestroy(hfdi);
3006         HeapFree(GetProcessHeap(), 0, cabinet);
3007         return FALSE;
3008     }
3009
3010     data.package = package;
3011     data.cab_path = cab_path;
3012     file_name = strdupWtoA(file);
3013     data.file_name = file_name;
3014
3015     ret = FDICopy(hfdi, cabinet, "", 0, cabinet_notify, NULL, &data);
3016
3017     if (!ret)
3018         ERR("FDICopy failed\n");
3019
3020     FDIDestroy(hfdi);
3021
3022     HeapFree(GetProcessHeap(), 0, cabinet);
3023     HeapFree(GetProcessHeap(), 0, cab_path);
3024     HeapFree(GetProcessHeap(), 0, file_name);
3025
3026     return ret;
3027 }
3028
3029 static UINT ready_media_for_file(MSIPACKAGE *package, UINT sequence, 
3030                                  WCHAR* path, WCHAR* file)
3031 {
3032     UINT rc;
3033     MSIQUERY * view;
3034     MSIRECORD * row = 0;
3035     static WCHAR source[MAX_PATH];
3036     static const WCHAR ExecSeqQuery[] = {
3037         's','e','l','e','c','t',' ','*',' ',
3038         'f','r','o','m',' ','M','e','d','i','a',' ',
3039         'w','h','e','r','e',' ','L','a','s','t','S','e','q','u','e','n','c','e',' ','>','=',' ','%','i',' ',
3040         'o','r','d','e','r',' ','b','y',' ','L','a','s','t','S','e','q','u','e','n','c','e',0};
3041     WCHAR Query[1024];
3042     WCHAR cab[0x100];
3043     DWORD sz=0x100;
3044     INT seq;
3045     static UINT last_sequence = 0; 
3046
3047     if (sequence <= last_sequence)
3048     {
3049         TRACE("Media already ready (%u, %u)\n",sequence,last_sequence);
3050         extract_a_cabinet_file(package, source,path,file);
3051         return ERROR_SUCCESS;
3052     }
3053
3054     sprintfW(Query,ExecSeqQuery,sequence);
3055
3056     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3057     if (rc != ERROR_SUCCESS)
3058         return rc;
3059
3060     rc = MSI_ViewExecute(view, 0);
3061     if (rc != ERROR_SUCCESS)
3062     {
3063         MSI_ViewClose(view);
3064         msiobj_release(&view->hdr);
3065         return rc;
3066     }
3067
3068     rc = MSI_ViewFetch(view,&row);
3069     if (rc != ERROR_SUCCESS)
3070     {
3071         MSI_ViewClose(view);
3072         msiobj_release(&view->hdr);
3073         return rc;
3074     }
3075     seq = MSI_RecordGetInteger(row,2);
3076     last_sequence = seq;
3077
3078     if (!MSI_RecordIsNull(row,4))
3079     {
3080         sz=0x100;
3081         MSI_RecordGetStringW(row,4,cab,&sz);
3082         TRACE("Source is CAB %s\n",debugstr_w(cab));
3083         /* the stream does not contain the # character */
3084         if (cab[0]=='#')
3085         {
3086             writeout_cabinet_stream(package,&cab[1],source);
3087             strcpyW(path,source);
3088             *(strrchrW(path,'\\')+1)=0;
3089         }
3090         else
3091         {
3092             sz = MAX_PATH;
3093             if (MSI_GetPropertyW(package, cszSourceDir, source, &sz))
3094             {
3095                 ERR("No Source dir defined \n");
3096                 rc = ERROR_FUNCTION_FAILED;
3097             }
3098             else
3099             {
3100                 strcpyW(path,source);
3101                 strcatW(source,cab);
3102                 /* extract the cab file into a folder in the temp folder */
3103                 sz = MAX_PATH;
3104                 if (MSI_GetPropertyW(package, cszTempFolder,path, &sz) 
3105                                     != ERROR_SUCCESS)
3106                     GetTempPathW(MAX_PATH,path);
3107             }
3108         }
3109         rc = !extract_a_cabinet_file(package, source,path,file);
3110     }
3111     msiobj_release(&row->hdr);
3112     MSI_ViewClose(view);
3113     msiobj_release(&view->hdr);
3114     return rc;
3115 }
3116
3117 inline static UINT create_component_directory ( MSIPACKAGE* package, INT component)
3118 {
3119     UINT rc;
3120     MSIFOLDER *folder;
3121     LPWSTR install_path;
3122
3123     install_path = resolve_folder(package, package->components[component].Directory,
3124                         FALSE, FALSE, &folder);
3125     if (!install_path)
3126         return ERROR_FUNCTION_FAILED; 
3127
3128     /* create the path */
3129     if (folder->State == 0)
3130     {
3131         create_full_pathW(install_path);
3132         folder->State = 2;
3133     }
3134     HeapFree(GetProcessHeap(), 0, install_path);
3135
3136     return rc;
3137 }
3138
3139 static UINT ACTION_InstallFiles(MSIPACKAGE *package)
3140 {
3141     UINT rc = ERROR_SUCCESS;
3142     DWORD index;
3143     MSIRECORD * uirow;
3144     WCHAR uipath[MAX_PATH];
3145
3146     if (!package)
3147         return ERROR_INVALID_HANDLE;
3148
3149     /* increment progress bar each time action data is sent */
3150     ui_progress(package,1,1,0,0);
3151
3152     for (index = 0; index < package->loaded_files; index++)
3153     {
3154         WCHAR path_to_source[MAX_PATH];
3155         MSIFILE *file;
3156         
3157         file = &package->files[index];
3158
3159         if (file->Temporary)
3160             continue;
3161
3162         if (package->components[file->ComponentIndex].ActionRequest != 
3163              INSTALLSTATE_LOCAL)
3164         {
3165             ui_progress(package,2,file->FileSize,0,0);
3166             TRACE("File %s is not scheduled for install\n",
3167                    debugstr_w(file->File));
3168
3169             continue;
3170         }
3171
3172         if ((file->State == 1) || (file->State == 2))
3173         {
3174             TRACE("Installing %s\n",debugstr_w(file->File));
3175             rc = ready_media_for_file(package,file->Sequence,path_to_source,
3176                             file->File);
3177             /* 
3178              * WARNING!
3179              * our file table could change here because a new temp file
3180              * may have been created
3181              */
3182             file = &package->files[index];
3183             if (rc != ERROR_SUCCESS)
3184             {
3185                 ERR("Unable to ready media\n");
3186                 rc = ERROR_FUNCTION_FAILED;
3187                 break;
3188             }
3189
3190             create_component_directory( package, file->ComponentIndex);
3191
3192             strcpyW(file->SourcePath, path_to_source);
3193             strcatW(file->SourcePath, file->File);
3194
3195             TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
3196                   debugstr_w(file->TargetPath));
3197
3198             /* the UI chunk */
3199             uirow=MSI_CreateRecord(9);
3200             MSI_RecordSetStringW(uirow,1,file->File);
3201             strcpyW(uipath,file->TargetPath);
3202             *(strrchrW(uipath,'\\')+1)=0;
3203             MSI_RecordSetStringW(uirow,9,uipath);
3204             MSI_RecordSetInteger(uirow,6,file->FileSize);
3205             ui_actiondata(package,szInstallFiles,uirow);
3206             msiobj_release( &uirow->hdr );
3207             ui_progress(package,2,file->FileSize,0,0);
3208
3209             if (!MoveFileW(file->SourcePath,file->TargetPath))
3210             {
3211                 rc = GetLastError();
3212                 ERR("Unable to move file (%s -> %s) (error %d)\n",
3213                      debugstr_w(file->SourcePath), debugstr_w(file->TargetPath),
3214                       rc);
3215                 if (rc == ERROR_ALREADY_EXISTS && file->State == 2)
3216                 {
3217                     CopyFileW(file->SourcePath,file->TargetPath,FALSE);
3218                     DeleteFileW(file->SourcePath);
3219                     rc = 0;
3220                 }
3221                 else if (rc == ERROR_FILE_NOT_FOUND)
3222                 {
3223                     ERR("Source File Not Found!  Continueing\n");
3224                     rc = 0;
3225                 }
3226                 else
3227                 {
3228                     ERR("Ignoring Error and continuing...\n");
3229                     rc = 0;
3230                 }
3231             }
3232             else
3233                 file->State = 4;
3234         }
3235     }
3236
3237     return rc;
3238 }
3239
3240 inline static UINT get_file_target(MSIPACKAGE *package, LPCWSTR file_key, 
3241                                    LPWSTR* file_source)
3242 {
3243     DWORD index;
3244
3245     if (!package)
3246         return ERROR_INVALID_HANDLE;
3247
3248     for (index = 0; index < package->loaded_files; index ++)
3249     {
3250         if (strcmpW(file_key,package->files[index].File)==0)
3251         {
3252             if (package->files[index].State >= 3)
3253             {
3254                 *file_source = dupstrW(package->files[index].TargetPath);
3255                 return ERROR_SUCCESS;
3256             }
3257             else
3258                 return ERROR_FILE_NOT_FOUND;
3259         }
3260     }
3261
3262     return ERROR_FUNCTION_FAILED;
3263 }
3264
3265 static UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
3266 {
3267     UINT rc;
3268     MSIQUERY * view;
3269     MSIRECORD * row = 0;
3270     static const WCHAR ExecSeqQuery[] = {
3271         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ',
3272         'D','u','p','l','i','c','a','t','e','F','i','l','e',0};
3273
3274     if (!package)
3275         return ERROR_INVALID_HANDLE;
3276
3277     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3278     if (rc != ERROR_SUCCESS)
3279         return ERROR_SUCCESS;
3280
3281     rc = MSI_ViewExecute(view, 0);
3282     if (rc != ERROR_SUCCESS)
3283     {
3284         MSI_ViewClose(view);
3285         msiobj_release(&view->hdr);
3286         return rc;
3287     }
3288
3289     while (1)
3290     {
3291         WCHAR file_key[0x100];
3292         WCHAR *file_source = NULL;
3293         WCHAR dest_name[0x100];
3294         LPWSTR dest_path, dest;
3295         WCHAR component[0x100];
3296         INT component_index;
3297
3298         DWORD sz=0x100;
3299
3300         rc = MSI_ViewFetch(view,&row);
3301         if (rc != ERROR_SUCCESS)
3302         {
3303             rc = ERROR_SUCCESS;
3304             break;
3305         }
3306
3307         sz=0x100;
3308         rc = MSI_RecordGetStringW(row,2,component,&sz);
3309         if (rc != ERROR_SUCCESS)
3310         {
3311             ERR("Unable to get component\n");
3312             msiobj_release(&row->hdr);
3313             break;
3314         }
3315
3316         component_index = get_loaded_component(package,component);
3317         if (package->components[component_index].ActionRequest != 
3318              INSTALLSTATE_LOCAL)
3319         {
3320             TRACE("Skipping copy due to disabled component\n");
3321             msiobj_release(&row->hdr);
3322             continue;
3323         }
3324
3325         sz=0x100;
3326         rc = MSI_RecordGetStringW(row,3,file_key,&sz);
3327         if (rc != ERROR_SUCCESS)
3328         {
3329             ERR("Unable to get file key\n");
3330             msiobj_release(&row->hdr);
3331             break;
3332         }
3333
3334         rc = get_file_target(package,file_key,&file_source);
3335
3336         if (rc != ERROR_SUCCESS)
3337         {
3338             ERR("Original file unknown %s\n",debugstr_w(file_key));
3339             msiobj_release(&row->hdr);
3340             if (file_source)
3341                 HeapFree(GetProcessHeap(),0,file_source);
3342             break;
3343         }
3344
3345         if (MSI_RecordIsNull(row,4))
3346         {
3347             strcpyW(dest_name,strrchrW(file_source,'\\')+1);
3348         }
3349         else
3350         {
3351             sz=0x100;
3352             MSI_RecordGetStringW(row,4,dest_name,&sz);
3353             reduce_to_longfilename(dest_name);
3354          }
3355
3356         if (MSI_RecordIsNull(row,5))
3357         {
3358             LPWSTR p;
3359             dest_path = dupstrW(file_source);
3360             p = strrchrW(dest_path,'\\');
3361             if (p)
3362                 *p=0;
3363         }
3364         else
3365         {
3366             WCHAR destkey[0x100];
3367             sz=0x100;
3368             MSI_RecordGetStringW(row,5,destkey,&sz);
3369             sz = 0x100;
3370             dest_path = resolve_folder(package, destkey, FALSE,FALSE,NULL);
3371             if (!dest_path)
3372             {
3373                 ERR("Unable to get destination folder\n");
3374                 msiobj_release(&row->hdr);
3375                 if (file_source)
3376                     HeapFree(GetProcessHeap(),0,file_source);
3377                 break;
3378             }
3379         }
3380
3381         dest = build_directory_name(2, dest_path, dest_name);
3382         HeapFree(GetProcessHeap(), 0, dest_path);
3383            
3384         TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
3385               debugstr_w(dest)); 
3386         
3387         if (strcmpW(file_source,dest))
3388             rc = !CopyFileW(file_source,dest,TRUE);
3389         else
3390             rc = ERROR_SUCCESS;
3391         
3392         if (rc != ERROR_SUCCESS)
3393             ERR("Failed to copy file\n");
3394
3395         FIXME("We should track these duplicate files as well\n");   
3396  
3397         msiobj_release(&row->hdr);
3398         HeapFree(GetProcessHeap(),0,dest);
3399         HeapFree(GetProcessHeap(),0,file_source);
3400     }
3401     MSI_ViewClose(view);
3402     msiobj_release(&view->hdr);
3403     return rc;
3404 }
3405
3406
3407 /* OK this value is "interpretted" and then formatted based on the 
3408    first few characters */
3409 static LPSTR parse_value(MSIPACKAGE *package, WCHAR *value, DWORD *type, 
3410                          DWORD *size)
3411 {
3412     LPSTR data = NULL;
3413     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
3414     {
3415         if (value[1]=='x')
3416         {
3417             LPWSTR ptr;
3418             CHAR byte[5];
3419             LPWSTR deformated;
3420             int count;
3421
3422             deformat_string(package, &value[2], &deformated);
3423
3424             /* binary value type */
3425             ptr = deformated; 
3426             *type=REG_BINARY;
3427             *size = strlenW(ptr)/2;
3428             data = HeapAlloc(GetProcessHeap(),0,*size);
3429           
3430             byte[0] = '0'; 
3431             byte[1] = 'x'; 
3432             byte[4] = 0; 
3433             count = 0;
3434             while (*ptr)
3435             {
3436                 byte[2]= *ptr;
3437                 ptr++;
3438                 byte[3]= *ptr;
3439                 ptr++;
3440                 data[count] = (BYTE)strtol(byte,NULL,0);
3441                 count ++;
3442             }
3443             HeapFree(GetProcessHeap(),0,deformated);
3444
3445             TRACE("Data %li bytes(%i)\n",*size,count);
3446         }
3447         else
3448         {
3449             LPWSTR deformated;
3450             deformat_string(package, &value[1], &deformated);
3451
3452             *type=REG_DWORD; 
3453             *size = sizeof(DWORD);
3454             data = HeapAlloc(GetProcessHeap(),0,*size);
3455             *(LPDWORD)data = atoiW(deformated); 
3456             TRACE("DWORD %i\n",*data);
3457
3458             HeapFree(GetProcessHeap(),0,deformated);
3459         }
3460     }
3461     else
3462     {
3463         WCHAR *ptr;
3464         *type=REG_SZ;
3465
3466         if (value[0]=='#')
3467         {
3468             if (value[1]=='%')
3469             {
3470                 ptr = &value[2];
3471                 *type=REG_EXPAND_SZ;
3472             }
3473             else
3474                 ptr = &value[1];
3475          }
3476          else
3477             ptr=value;
3478
3479         *size = deformat_string(package, ptr,(LPWSTR*)&data);
3480     }
3481     return data;
3482 }
3483
3484 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
3485 {
3486     UINT rc;
3487     MSIQUERY * view;
3488     MSIRECORD * row = 0;
3489     static const WCHAR ExecSeqQuery[] = {
3490         's','e','l','e','c','t',' ','*',' ',
3491         'f','r','o','m',' ','R','e','g','i','s','t','r','y',0 };
3492
3493     if (!package)
3494         return ERROR_INVALID_HANDLE;
3495
3496     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3497     if (rc != ERROR_SUCCESS)
3498         return ERROR_SUCCESS;
3499
3500     rc = MSI_ViewExecute(view, 0);
3501     if (rc != ERROR_SUCCESS)
3502     {
3503         MSI_ViewClose(view);
3504         msiobj_release(&view->hdr);
3505         return rc;
3506     }
3507
3508     /* increment progress bar each time action data is sent */
3509     ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
3510
3511     while (1)
3512     {
3513         static const WCHAR szHCR[] = 
3514 {'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T','\\',0};
3515         static const WCHAR szHCU[] =
3516 {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','\\',0};
3517         static const WCHAR szHLM[] =
3518 {'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E',
3519 '\\',0};
3520         static const WCHAR szHU[] =
3521 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
3522
3523         LPSTR value_data = NULL;
3524         HKEY  root_key, hkey;
3525         DWORD type,size;
3526         LPWSTR value, key, name, component;
3527         LPCWSTR szRoot;
3528         INT component_index;
3529         MSIRECORD * uirow;
3530         LPWSTR uikey;
3531         INT   root;
3532
3533         rc = MSI_ViewFetch(view,&row);
3534         if (rc != ERROR_SUCCESS)
3535         {
3536             rc = ERROR_SUCCESS;
3537             break;
3538         }
3539         ui_progress(package,2,0,0,0);
3540
3541         value = NULL;
3542         key = NULL;
3543         uikey = NULL;
3544         name = NULL;
3545
3546         component = load_dynamic_stringW(row, 6);
3547         component_index = get_loaded_component(package,component);
3548
3549         if (package->components[component_index].ActionRequest != 
3550              INSTALLSTATE_LOCAL)
3551         {
3552             TRACE("Skipping write due to disabled component\n");
3553             msiobj_release(&row->hdr);
3554             goto next;
3555         }
3556
3557         /* null values have special meanings during uninstalls and such */
3558         
3559         if(MSI_RecordIsNull(row,5))
3560         {
3561             msiobj_release(&row->hdr);
3562             goto next;
3563         }
3564
3565         root = MSI_RecordGetInteger(row,2);
3566         key = load_dynamic_stringW(row, 3);
3567       
3568         name = load_dynamic_stringW(row, 4);
3569    
3570         /* get the root key */
3571         switch (root)
3572         {
3573             case 0:  root_key = HKEY_CLASSES_ROOT; 
3574                      szRoot = szHCR;
3575                      break;
3576             case 1:  root_key = HKEY_CURRENT_USER;
3577                      szRoot = szHCU;
3578                      break;
3579             case 2:  root_key = HKEY_LOCAL_MACHINE;
3580                      szRoot = szHLM;
3581                      break;
3582             case 3:  root_key = HKEY_USERS; 
3583                      szRoot = szHU;
3584                      break;
3585             default:
3586                  ERR("Unknown root %i\n",root);
3587                  root_key=NULL;
3588                  szRoot = NULL;
3589                  break;
3590         }
3591         if (!root_key)
3592         {
3593             msiobj_release(&row->hdr);
3594             goto next;
3595         }
3596
3597         size = strlenW(key) + strlenW(szRoot) + 1;
3598         uikey = HeapAlloc(GetProcessHeap(), 0, size*sizeof(WCHAR));
3599         strcpyW(uikey,szRoot);
3600         strcatW(uikey,key);
3601         if (RegCreateKeyW( root_key, key, &hkey))
3602         {
3603             ERR("Could not create key %s\n",debugstr_w(key));
3604             msiobj_release(&row->hdr);
3605             goto next;
3606         }
3607
3608         value = load_dynamic_stringW(row,5);
3609         value_data = parse_value(package, value, &type, &size); 
3610
3611         if (value_data)
3612         {
3613             TRACE("Setting value %s\n",debugstr_w(name));
3614             RegSetValueExW(hkey, name, 0, type, value_data, size);
3615
3616             uirow = MSI_CreateRecord(3);
3617             MSI_RecordSetStringW(uirow,2,name);
3618             MSI_RecordSetStringW(uirow,1,uikey);
3619
3620             if (type == REG_SZ)
3621                 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
3622             else
3623                 MSI_RecordSetStringW(uirow,3,value);
3624
3625             ui_actiondata(package,szWriteRegistryValues,uirow);
3626             msiobj_release( &uirow->hdr );
3627
3628             HeapFree(GetProcessHeap(),0,value_data);
3629         }
3630         HeapFree(GetProcessHeap(),0,value);
3631
3632         msiobj_release(&row->hdr);
3633         RegCloseKey(hkey);
3634 next:
3635         if (uikey)
3636             HeapFree(GetProcessHeap(),0,uikey);
3637         if (key)
3638             HeapFree(GetProcessHeap(),0,key);
3639         if (name)
3640             HeapFree(GetProcessHeap(),0,name);
3641         if (component)
3642             HeapFree(GetProcessHeap(),0,component);
3643     }
3644     MSI_ViewClose(view);
3645     msiobj_release(&view->hdr);
3646     return rc;
3647 }
3648
3649 /*
3650  * This helper function should probably go alot of places
3651  *
3652  * Thinking about this, maybe this should become yet another Bison file
3653  */
3654 static DWORD deformat_string(MSIPACKAGE *package, WCHAR* ptr,WCHAR** data)
3655 {
3656     WCHAR* mark=NULL;
3657     DWORD size=0;
3658     DWORD chunk=0;
3659     WCHAR key[0x100];
3660     LPWSTR value;
3661     DWORD sz;
3662     UINT rc;
3663
3664     if (ptr==NULL)
3665     {
3666         TRACE("Deformatting NULL string\n");
3667         *data = NULL;
3668         return 0;
3669     }
3670     /* scan for special characters */
3671     if (!strchrW(ptr,'[') || (strchrW(ptr,'[') && !strchrW(ptr,']')))
3672     {
3673         /* not formatted */
3674         size = (strlenW(ptr)+1) * sizeof(WCHAR);
3675         *data = HeapAlloc(GetProcessHeap(),0,size);
3676         strcpyW(*data,ptr);
3677         return size;
3678     }
3679    
3680     /* formatted string located */ 
3681     mark = strchrW(ptr,'[');
3682     if (mark != ptr)
3683     {
3684         INT cnt = (mark - ptr);
3685         TRACE("%i  (%i) characters before marker\n",cnt,(mark-ptr));
3686         size = cnt * sizeof(WCHAR);
3687         size += sizeof(WCHAR);
3688         *data = HeapAlloc(GetProcessHeap(),0,size);
3689         strncpyW(*data,ptr,cnt);
3690         (*data)[cnt]=0;
3691     }
3692     else
3693     {
3694         size = sizeof(WCHAR);
3695         *data = HeapAlloc(GetProcessHeap(),0,size);
3696         (*data)[0]=0;
3697     }
3698     mark++;
3699     strcpyW(key,mark);
3700     *strchrW(key,']')=0;
3701     mark = strchrW(mark,']');
3702     mark++;
3703     TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
3704     sz = 0;
3705     rc = MSI_GetPropertyW(package, key, NULL, &sz);
3706     if ((rc == ERROR_SUCCESS) || (rc == ERROR_MORE_DATA))
3707     {
3708         LPWSTR newdata;
3709
3710         sz++;
3711         value = HeapAlloc(GetProcessHeap(),0,sz * sizeof(WCHAR));
3712         MSI_GetPropertyW(package, key, value, &sz);
3713
3714         chunk = (strlenW(value)+1) * sizeof(WCHAR);
3715         size+=chunk;   
3716         newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
3717         *data = newdata;
3718         strcatW(*data,value);
3719     }
3720     TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
3721     if (*mark!=0)
3722     {
3723         LPWSTR newdata;
3724         chunk = (strlenW(mark)+1) * sizeof(WCHAR);
3725         size+=chunk;
3726         newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
3727         *data = newdata;
3728         strcatW(*data,mark);
3729     }
3730     (*data)[strlenW(*data)]=0;
3731     TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
3732
3733     /* recursively do this to clean up */
3734     mark = HeapAlloc(GetProcessHeap(),0,size);
3735     strcpyW(mark,*data);
3736     TRACE("String at this point %s\n",debugstr_w(mark));
3737     size = deformat_string(package,mark,data);
3738     HeapFree(GetProcessHeap(),0,mark);
3739     return size;
3740 }
3741
3742 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3743 {
3744     return ERROR_SUCCESS;
3745 }
3746
3747
3748 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3749 {
3750     DWORD progress = 0;
3751     DWORD total = 0;
3752     static const WCHAR q1[]={
3753         'S','E','L','E','C','T',' ','*',' ',
3754         'F','R','O','M',' ','R','e','g','i','s','t','r','y',0};
3755     UINT rc;
3756     MSIQUERY * view;
3757     MSIRECORD * row = 0;
3758     int i;
3759
3760     TRACE(" InstallValidate \n");
3761
3762     rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
3763     if (rc != ERROR_SUCCESS)
3764         return ERROR_SUCCESS;
3765
3766     rc = MSI_ViewExecute(view, 0);
3767     if (rc != ERROR_SUCCESS)
3768     {
3769         MSI_ViewClose(view);
3770         msiobj_release(&view->hdr);
3771         return rc;
3772     }
3773     while (1)
3774     {
3775         rc = MSI_ViewFetch(view,&row);
3776         if (rc != ERROR_SUCCESS)
3777         {
3778             rc = ERROR_SUCCESS;
3779             break;
3780         }
3781         progress +=1;
3782
3783         msiobj_release(&row->hdr);
3784     }
3785     MSI_ViewClose(view);
3786     msiobj_release(&view->hdr);
3787
3788     total = total + progress * REG_PROGRESS_VALUE;
3789     total = total + package->loaded_components * COMPONENT_PROGRESS_VALUE;
3790     for (i=0; i < package->loaded_files; i++)
3791         total += package->files[i].FileSize;
3792     ui_progress(package,0,total,0,0);
3793
3794     return ERROR_SUCCESS;
3795 }
3796
3797 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3798 {
3799     UINT rc;
3800     MSIQUERY * view = NULL;
3801     MSIRECORD * row = 0;
3802     static const WCHAR ExecSeqQuery[] = {
3803         'S','E','L','E','C','T',' ','*',' ',
3804         'f','r','o','m',' ','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n',0};
3805     static const WCHAR title[]=
3806             {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3807
3808     TRACE("Checking launch conditions\n");
3809
3810     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3811     if (rc != ERROR_SUCCESS)
3812         return ERROR_SUCCESS;
3813
3814     rc = MSI_ViewExecute(view, 0);
3815     if (rc != ERROR_SUCCESS)
3816     {
3817         MSI_ViewClose(view);
3818         msiobj_release(&view->hdr);
3819         return rc;
3820     }
3821
3822     rc = ERROR_SUCCESS;
3823     while (rc == ERROR_SUCCESS)
3824     {
3825         LPWSTR cond = NULL; 
3826         LPWSTR message = NULL;
3827
3828         rc = MSI_ViewFetch(view,&row);
3829         if (rc != ERROR_SUCCESS)
3830         {
3831             rc = ERROR_SUCCESS;
3832             break;
3833         }
3834
3835         cond = load_dynamic_stringW(row,1);
3836
3837         if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
3838         {
3839             message = load_dynamic_stringW(row,2);
3840             MessageBoxW(NULL,message,title,MB_OK);
3841             HeapFree(GetProcessHeap(),0,message);
3842             rc = ERROR_FUNCTION_FAILED;
3843         }
3844         HeapFree(GetProcessHeap(),0,cond);
3845         msiobj_release(&row->hdr);
3846     }
3847     MSI_ViewClose(view);
3848     msiobj_release(&view->hdr);
3849     return rc;
3850 }
3851
3852 static LPWSTR resolve_keypath( MSIPACKAGE* package, INT
3853                             component_index)
3854 {
3855     MSICOMPONENT* cmp = &package->components[component_index];
3856
3857     if (cmp->KeyPath[0]==0)
3858     {
3859         LPWSTR p = resolve_folder(package,cmp->Directory,FALSE,FALSE,NULL);
3860         return p;
3861     }
3862     if ((cmp->Attributes & 0x4) || (cmp->Attributes & 0x20))
3863     {
3864         FIXME("UNIMPLEMENTED keypath as Registry or ODBC Source\n");
3865         return NULL;
3866     }
3867     else
3868     {
3869         int j;
3870         j = get_loaded_file(package,cmp->KeyPath);
3871
3872         if (j>=0)
3873         {
3874             LPWSTR p = dupstrW(package->files[j].TargetPath);
3875             return p;
3876         }
3877     }
3878     return NULL;
3879 }
3880
3881 /*
3882  * Ok further analysis makes me think that this work is
3883  * actually done in the PublishComponents and PublishFeatures
3884  * step, and not here.  It appears like the keypath and all that is
3885  * resolved in this step, however actually written in the Publish steps.
3886  * But we will leave it here for now because it is unclear
3887  */
3888 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3889 {
3890     LPWSTR productcode;
3891     WCHAR squished_pc[0x100];
3892     WCHAR squished_cc[0x100];
3893     UINT rc;
3894     DWORD i;
3895     HKEY hkey=0,hkey2=0,hkey3=0;
3896     static const WCHAR szProductCode[]=
3897          {'P','r','o','d','u','c','t','C','o','d','e',0};
3898     static const WCHAR szInstaller[] = {
3899          'S','o','f','t','w','a','r','e','\\',
3900          'M','i','c','r','o','s','o','f','t','\\',
3901          'W','i','n','d','o','w','s','\\',
3902          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3903          'I','n','s','t','a','l','l','e','r',0 };
3904     static const WCHAR szFeatures[] = {
3905          'F','e','a','t','u','r','e','s',0 };
3906     static const WCHAR szComponents[] = {
3907          'C','o','m','p','o','n','e','n','t','s',0 };
3908
3909     if (!package)
3910         return ERROR_INVALID_HANDLE;
3911
3912     /* writes the Component and Features values to the registry */
3913     productcode = load_dynamic_property(package,szProductCode,&rc);
3914     if (!productcode)
3915         return rc;
3916
3917     squash_guid(productcode,squished_pc);
3918     rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller,&hkey);
3919     if (rc != ERROR_SUCCESS)
3920         goto end;
3921
3922     rc = RegCreateKeyW(hkey,szFeatures,&hkey2);
3923     if (rc != ERROR_SUCCESS)
3924         goto end;
3925
3926     rc = RegCreateKeyW(hkey2,squished_pc,&hkey3);
3927     if (rc != ERROR_SUCCESS)
3928         goto end;
3929
3930     /* here the guids are base 85 encoded */
3931     for (i = 0; i < package->loaded_features; i++)
3932     {
3933         LPWSTR data = NULL;
3934         GUID clsid;
3935         int j;
3936         INT size;
3937
3938         size = package->features[i].ComponentCount*21*sizeof(WCHAR);
3939         data = HeapAlloc(GetProcessHeap(), 0, size);
3940
3941         data[0] = 0;
3942         for (j = 0; j < package->features[i].ComponentCount; j++)
3943         {
3944             WCHAR buf[21];
3945             TRACE("From %s\n",debugstr_w(package->components
3946                             [package->features[i].Components[j]].ComponentId));
3947             CLSIDFromString(package->components
3948                             [package->features[i].Components[j]].ComponentId,
3949                             &clsid);
3950             encode_base85_guid(&clsid,buf);
3951             TRACE("to %s\n",debugstr_w(buf));
3952             strcatW(data,buf);
3953         }
3954
3955         size = strlenW(data)*sizeof(WCHAR);
3956         RegSetValueExW(hkey3,package->features[i].Feature,0,REG_SZ,
3957                        (LPSTR)data,size);
3958         HeapFree(GetProcessHeap(),0,data);
3959     }
3960
3961     RegCloseKey(hkey3);
3962     RegCloseKey(hkey2);
3963
3964     rc = RegCreateKeyW(hkey,szComponents,&hkey2);
3965     if (rc != ERROR_SUCCESS)
3966         goto end;
3967       
3968     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
3969     for (i = 0; i < package->loaded_components; i++)
3970     {
3971         ui_progress(package,2,0,0,0);
3972         if (package->components[i].ComponentId[0]!=0)
3973         {
3974             WCHAR *keypath = NULL;
3975             MSIRECORD * uirow;
3976
3977             squash_guid(package->components[i].ComponentId,squished_cc);
3978             rc = RegCreateKeyW(hkey2,squished_cc,&hkey3);
3979             if (rc != ERROR_SUCCESS)
3980                 continue;
3981            
3982             keypath = resolve_keypath(package,i);
3983             if (keypath)
3984             {
3985                 RegSetValueExW(hkey3,squished_pc,0,REG_SZ,(LPVOID)keypath,
3986                             (strlenW(keypath)+1)*sizeof(WCHAR));
3987                 RegCloseKey(hkey3);
3988         
3989                 /* UI stuff */
3990                 uirow = MSI_CreateRecord(3);
3991                 MSI_RecordSetStringW(uirow,1,productcode);
3992                 MSI_RecordSetStringW(uirow,2,package->components[i].
3993                                                         ComponentId);
3994                 MSI_RecordSetStringW(uirow,3,keypath);
3995                 ui_actiondata(package,szProcessComponents,uirow);
3996                 msiobj_release( &uirow->hdr );
3997                 HeapFree(GetProcessHeap(),0,keypath);
3998             }
3999         }
4000     } 
4001 end:
4002     HeapFree(GetProcessHeap(), 0, productcode);
4003     RegCloseKey(hkey2);
4004     RegCloseKey(hkey);
4005     return rc;
4006 }
4007
4008 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
4009 {
4010     /* 
4011      * OK this is a bit confusing.. I am given a _Component key and I believe
4012      * that the file that is being registered as a type library is the "key file
4013      * of that component" which I interpret to mean "The file in the KeyPath of
4014      * that component".
4015      */
4016     UINT rc;
4017     MSIQUERY * view;
4018     MSIRECORD * row = 0;
4019     static const WCHAR Query[] = {
4020         'S','E','L','E','C','T',' ','*',' ',
4021         'f','r','o','m',' ','T','y','p','e','L','i','b',0};
4022     ITypeLib *ptLib;
4023     HRESULT res;
4024
4025     if (!package)
4026         return ERROR_INVALID_HANDLE;
4027
4028     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
4029     if (rc != ERROR_SUCCESS)
4030         return ERROR_SUCCESS;
4031
4032     rc = MSI_ViewExecute(view, 0);
4033     if (rc != ERROR_SUCCESS)
4034     {
4035         MSI_ViewClose(view);
4036         msiobj_release(&view->hdr);
4037         return rc;
4038     }
4039
4040     while (1)
4041     {   
4042         WCHAR component[0x100];
4043         DWORD sz;
4044         INT index;
4045
4046         rc = MSI_ViewFetch(view,&row);
4047         if (rc != ERROR_SUCCESS)
4048         {
4049             rc = ERROR_SUCCESS;
4050             break;
4051         }
4052
4053         sz = 0x100;
4054         MSI_RecordGetStringW(row,3,component,&sz);
4055
4056         index = get_loaded_component(package,component);
4057         if (index < 0)
4058         {
4059             msiobj_release(&row->hdr);
4060             continue;
4061         }
4062
4063         if (package->components[index].ActionRequest != INSTALLSTATE_LOCAL)
4064         {
4065             TRACE("Skipping typelib reg due to disabled component\n");
4066             msiobj_release(&row->hdr);
4067             continue;
4068         }
4069
4070         index = get_loaded_file(package,package->components[index].KeyPath); 
4071    
4072         if (index < 0)
4073         {
4074             msiobj_release(&row->hdr);
4075             continue;
4076         }
4077
4078         res = LoadTypeLib(package->files[index].TargetPath,&ptLib);
4079         if (SUCCEEDED(res))
4080         {
4081             LPWSTR help;
4082             WCHAR helpid[0x100];
4083
4084             sz = 0x100;
4085             MSI_RecordGetStringW(row,6,helpid,&sz);
4086
4087             help = resolve_folder(package,helpid,FALSE,FALSE,NULL);
4088             res = RegisterTypeLib(ptLib,package->files[index].TargetPath,help);
4089             HeapFree(GetProcessHeap(),0,help);
4090
4091             if (!SUCCEEDED(res))
4092                 ERR("Failed to register type library %s\n",
4093                      debugstr_w(package->files[index].TargetPath));
4094             else
4095             {
4096                 /* Yes the row has more fields than I need, but #1 is 
4097                    correct and the only one I need. Why make a new row? */
4098
4099                 ui_actiondata(package,szRegisterTypeLibraries,row);
4100                 
4101                 TRACE("Registered %s\n",
4102                        debugstr_w(package->files[index].TargetPath));
4103             }
4104
4105             if (ptLib)
4106                 ITypeLib_Release(ptLib);
4107         }
4108         else
4109             ERR("Failed to load type library %s\n",
4110                 debugstr_w(package->files[index].TargetPath));
4111         
4112         msiobj_release(&row->hdr);
4113     }
4114     MSI_ViewClose(view);
4115     msiobj_release(&view->hdr);
4116     return rc;
4117    
4118 }
4119
4120 static UINT register_appid(MSIPACKAGE *package, LPCWSTR clsid, LPCWSTR app )
4121 {
4122     static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
4123     UINT rc;
4124     MSIQUERY * view;
4125     MSIRECORD * row = 0;
4126     static const WCHAR ExecSeqQuery[] = 
4127         {'S','E','L','E','C','T',' ','*',' ','f','r','o','m',' ','A','p','p','I'
4128         ,'d',' ','w','h','e','r','e',' ','A','p','p','I','d','=','`','%','s','`',0};
4129     HKEY hkey2,hkey3;
4130     LPWSTR buffer=0;
4131
4132     if (!package)
4133         return ERROR_INVALID_HANDLE;
4134
4135     rc = ACTION_OpenQuery(package->db, &view, ExecSeqQuery, clsid);
4136     if (rc != ERROR_SUCCESS)
4137         return rc;
4138
4139     rc = MSI_ViewExecute(view, 0);
4140     if (rc != ERROR_SUCCESS)
4141     {
4142         MSI_ViewClose(view);
4143         msiobj_release(&view->hdr);
4144         return rc;
4145     }
4146
4147     RegCreateKeyW(HKEY_CLASSES_ROOT,szAppID,&hkey2);
4148     RegCreateKeyW(hkey2,clsid,&hkey3);
4149     RegSetValueExW(hkey3,NULL,0,REG_SZ,(LPVOID)app,
4150                    (strlenW(app)+1)*sizeof(WCHAR));
4151
4152     rc = MSI_ViewFetch(view,&row);
4153     if (rc != ERROR_SUCCESS)
4154     {
4155         MSI_ViewClose(view);
4156         msiobj_release(&view->hdr);
4157         return rc;
4158     }
4159
4160     if (!MSI_RecordIsNull(row,2)) 
4161     {
4162         LPWSTR deformated=0;
4163         UINT size; 
4164         static const WCHAR szRemoteServerName[] =
4165              {'R','e','m','o','t','e','S','e','r','v','e','r','N','a','m','e',0};
4166         buffer = load_dynamic_stringW(row,2);
4167         size = deformat_string(package,buffer,&deformated);
4168         RegSetValueExW(hkey3,szRemoteServerName,0,REG_SZ,(LPVOID)deformated,
4169                        size);
4170         HeapFree(GetProcessHeap(),0,deformated);
4171         HeapFree(GetProcessHeap(),0,buffer);
4172     }
4173
4174     if (!MSI_RecordIsNull(row,3)) 
4175     {
4176         static const WCHAR szLocalService[] =
4177              {'L','o','c','a','l','S','e','r','v','i','c','e',0};
4178         UINT size;
4179         buffer = load_dynamic_stringW(row,3);
4180         size = (strlenW(buffer)+1) * sizeof(WCHAR);
4181         RegSetValueExW(hkey3,szLocalService,0,REG_SZ,(LPVOID)buffer,size);
4182         HeapFree(GetProcessHeap(),0,buffer);
4183     }
4184
4185     if (!MSI_RecordIsNull(row,4)) 
4186     {
4187         static const WCHAR szService[] =
4188              {'S','e','r','v','i','c','e','P','a','r','a','m','e','t','e','r','s',0};
4189         UINT size;
4190         buffer = load_dynamic_stringW(row,4);
4191         size = (strlenW(buffer)+1) * sizeof(WCHAR);
4192         RegSetValueExW(hkey3,szService,0,REG_SZ,(LPVOID)buffer,size);
4193         HeapFree(GetProcessHeap(),0,buffer);
4194     }
4195
4196     if (!MSI_RecordIsNull(row,5)) 
4197     {
4198         static const WCHAR szDLL[] =
4199              {'D','l','l','S','u','r','r','o','g','a','t','e',0};
4200         UINT size;
4201         buffer = load_dynamic_stringW(row,5);
4202         size = (strlenW(buffer)+1) * sizeof(WCHAR);
4203         RegSetValueExW(hkey3,szDLL,0,REG_SZ,(LPVOID)buffer,size);
4204         HeapFree(GetProcessHeap(),0,buffer);
4205     }
4206
4207     if (!MSI_RecordIsNull(row,6)) 
4208     {
4209         static const WCHAR szActivate[] =
4210              {'A','c','t','i','v','a','t','e','A','s','S','t','o','r','a','g','e',0};
4211         static const WCHAR szY[] = {'Y',0};
4212
4213         if (MSI_RecordGetInteger(row,6))
4214             RegSetValueExW(hkey3,szActivate,0,REG_SZ,(LPVOID)szY,4);
4215     }
4216
4217     if (!MSI_RecordIsNull(row,7)) 
4218     {
4219         static const WCHAR szRunAs[] = {'R','u','n','A','s',0};
4220         static const WCHAR szUser[] = 
4221              {'I','n','t','e','r','a','c','t','i','v','e',' ','U','s','e','r',0};
4222
4223         if (MSI_RecordGetInteger(row,7))
4224             RegSetValueExW(hkey3,szRunAs,0,REG_SZ,(LPVOID)szUser,34);
4225     }
4226
4227     msiobj_release(&row->hdr);
4228     MSI_ViewClose(view);
4229     msiobj_release(&view->hdr);
4230     RegCloseKey(hkey3);
4231     RegCloseKey(hkey2);
4232     return rc;
4233 }
4234
4235 static UINT ACTION_RegisterClassInfo(MSIPACKAGE *package)
4236 {
4237     /* 
4238      * Again I am assuming the words, "Whose key file represents" when referring
4239      * to a Component as to meaning that Components KeyPath file
4240      *
4241      * Also there is a very strong connection between ClassInfo and ProgID
4242      * that I am mostly glossing over.  
4243      * What would be more propper is to load the ClassInfo and the ProgID info
4244      * into memory data structures and then be able to enable and disable them
4245      * based on component. 
4246      */
4247     
4248     UINT rc;
4249     MSIQUERY * view;
4250     MSIRECORD * row = 0;
4251     static const WCHAR ExecSeqQuery[] = {
4252         'S','E','L','E','C','T',' ','*',' ',
4253         'f','r','o','m',' ','C','l','a','s','s',0};
4254     static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
4255     static const WCHAR szProgID[] = { 'P','r','o','g','I','D',0 };
4256     static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
4257     HKEY hkey,hkey2,hkey3;
4258
4259     if (!package)
4260         return ERROR_INVALID_HANDLE;
4261
4262     rc = RegCreateKeyW(HKEY_CLASSES_ROOT,szCLSID,&hkey);
4263     if (rc != ERROR_SUCCESS)
4264         return ERROR_FUNCTION_FAILED;
4265
4266     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4267     if (rc != ERROR_SUCCESS)
4268     {
4269         rc = ERROR_SUCCESS;
4270         goto end;
4271     }
4272
4273     rc = MSI_ViewExecute(view, 0);
4274     if (rc != ERROR_SUCCESS)
4275     {
4276         MSI_ViewClose(view);
4277         msiobj_release(&view->hdr);
4278         goto end;
4279     }
4280
4281     while (1)
4282     {
4283         WCHAR clsid[0x100];
4284         WCHAR buffer[0x100];
4285         WCHAR desc[0x100];
4286         DWORD sz;
4287         INT index;
4288      
4289         rc = MSI_ViewFetch(view,&row);
4290         if (rc != ERROR_SUCCESS)
4291         {
4292             rc = ERROR_SUCCESS;
4293             break;
4294         }
4295
4296         sz=0x100;
4297         MSI_RecordGetStringW(row,3,buffer,&sz);
4298
4299         index = get_loaded_component(package,buffer);
4300
4301         if (index < 0)
4302         {
4303             msiobj_release(&row->hdr);
4304             continue;
4305         }
4306
4307         if (package->components[index].ActionRequest != INSTALLSTATE_LOCAL)
4308         {
4309             TRACE("Skipping class reg due to disabled component\n");
4310             msiobj_release(&row->hdr);
4311             continue;
4312         }
4313
4314         sz=0x100;
4315         MSI_RecordGetStringW(row,1,clsid,&sz);
4316         RegCreateKeyW(hkey,clsid,&hkey2);
4317
4318         if (!MSI_RecordIsNull(row,5))
4319         {
4320             sz=0x100;
4321             MSI_RecordGetStringW(row,5,desc,&sz);
4322
4323             RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)desc,
4324                            (strlenW(desc)+1)*sizeof(WCHAR));
4325         }
4326         else
4327             desc[0]=0;
4328
4329         sz=0x100;
4330         MSI_RecordGetStringW(row,2,buffer,&sz);
4331
4332         RegCreateKeyW(hkey2,buffer,&hkey3);
4333
4334         index = get_loaded_file(package,package->components[index].KeyPath);
4335         RegSetValueExW(hkey3,NULL,0,REG_SZ,
4336                        (LPVOID)package->files[index].TargetPath,
4337                        (strlenW(package->files[index].TargetPath)+1)
4338                         *sizeof(WCHAR));
4339
4340         RegCloseKey(hkey3);
4341
4342         if (!MSI_RecordIsNull(row,4))
4343         {
4344             sz=0x100;
4345             MSI_RecordGetStringW(row,4,buffer,&sz);
4346
4347             RegCreateKeyW(hkey2,szProgID,&hkey3);
4348     
4349             RegSetValueExW(hkey3,NULL,0,REG_SZ,(LPVOID)buffer,
4350                        (strlenW(buffer)+1)*sizeof(WCHAR));
4351
4352             RegCloseKey(hkey3);
4353         }
4354
4355         if (!MSI_RecordIsNull(row,6))
4356         { 
4357             sz=0x100;
4358             MSI_RecordGetStringW(row,6,buffer,&sz);
4359
4360             RegSetValueExW(hkey2,szAppID,0,REG_SZ,(LPVOID)buffer,
4361                        (strlenW(buffer)+1)*sizeof(WCHAR));
4362
4363             register_appid(package,buffer,desc);
4364         }
4365
4366         RegCloseKey(hkey2);
4367
4368         FIXME("Process the rest of the fields >7\n");
4369
4370         ui_actiondata(package,szRegisterClassInfo,row);
4371
4372         msiobj_release(&row->hdr);
4373     }
4374     MSI_ViewClose(view);
4375     msiobj_release(&view->hdr);
4376
4377 end:
4378     RegCloseKey(hkey);
4379     return rc;
4380 }
4381
4382 static UINT register_progid_base(MSIRECORD * row, LPWSTR clsid)
4383 {
4384     static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
4385     HKEY hkey,hkey2;
4386     WCHAR buffer[0x100];
4387     DWORD sz;
4388
4389
4390     sz = 0x100;
4391     MSI_RecordGetStringW(row,1,buffer,&sz);
4392     RegCreateKeyW(HKEY_CLASSES_ROOT,buffer,&hkey);
4393
4394     if (!MSI_RecordIsNull(row,4))
4395     {
4396         sz = 0x100;
4397         MSI_RecordGetStringW(row,4,buffer,&sz);
4398         RegSetValueExW(hkey,NULL,0,REG_SZ,(LPVOID)buffer, (strlenW(buffer)+1) *
4399                        sizeof(WCHAR));
4400     }
4401
4402     if (!MSI_RecordIsNull(row,3))
4403     {   
4404         sz = 0x100;
4405     
4406         MSI_RecordGetStringW(row,3,buffer,&sz);
4407         RegCreateKeyW(hkey,szCLSID,&hkey2);
4408         RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)buffer, (strlenW(buffer)+1) *
4409                        sizeof(WCHAR));
4410
4411         if (clsid)
4412             strcpyW(clsid,buffer);
4413
4414         RegCloseKey(hkey2);
4415     }
4416     else
4417     {
4418         FIXME("UNHANDLED case, Parent progid but classid is NULL\n");
4419         return ERROR_FUNCTION_FAILED;
4420     }
4421     if (!MSI_RecordIsNull(row,5))
4422         FIXME ("UNHANDLED icon in Progid\n");
4423     return ERROR_SUCCESS;
4424 }
4425
4426 static UINT register_progid(MSIPACKAGE *package, MSIRECORD * row, LPWSTR clsid);
4427
4428 static UINT register_parent_progid(MSIPACKAGE *package, LPCWSTR parent, 
4429                                    LPWSTR clsid)
4430 {
4431     UINT rc;
4432     MSIQUERY * view;
4433     MSIRECORD * row = 0;
4434     static const WCHAR Query_t[] = 
4435         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','r','o','g'
4436         ,'I','d',' ','w','h','e','r','e',' ','P','r','o','g','I','d',' ','=',' ','`'
4437         ,'%','s','`',0};
4438
4439     if (!package)
4440         return ERROR_INVALID_HANDLE;
4441
4442     rc = ACTION_OpenQuery(package->db, &view, Query_t, parent);
4443     if (rc != ERROR_SUCCESS)
4444         return rc;
4445
4446     rc = MSI_ViewExecute(view, 0);
4447     if (rc != ERROR_SUCCESS)
4448     {
4449         MSI_ViewClose(view);
4450         msiobj_release(&view->hdr);
4451         return rc;
4452     }
4453
4454     rc = MSI_ViewFetch(view,&row);
4455     if (rc != ERROR_SUCCESS)
4456     {
4457         MSI_ViewClose(view);
4458         msiobj_release(&view->hdr);
4459         return rc;
4460     }
4461
4462     register_progid(package,row,clsid);
4463
4464     msiobj_release(&row->hdr);
4465     MSI_ViewClose(view);
4466     msiobj_release(&view->hdr);
4467     return rc;
4468 }
4469
4470 static UINT register_progid(MSIPACKAGE *package, MSIRECORD * row, LPWSTR clsid)
4471 {
4472     UINT rc = ERROR_SUCCESS; 
4473
4474     if (MSI_RecordIsNull(row,2))
4475         rc = register_progid_base(row,clsid);
4476     else
4477     {
4478         WCHAR buffer[0x1000];
4479         DWORD sz, disp;
4480         HKEY hkey,hkey2;
4481         static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
4482
4483         /* check if already registered */
4484         sz = 0x100;
4485         MSI_RecordGetStringW(row,1,buffer,&sz);
4486         RegCreateKeyExW(HKEY_CLASSES_ROOT, buffer, 0, NULL, 0,
4487                         KEY_ALL_ACCESS, NULL, &hkey, &disp );
4488         if (disp == REG_OPENED_EXISTING_KEY)
4489         {
4490             TRACE("Key already registered\n");
4491             RegCloseKey(hkey);
4492             return rc;
4493         }
4494         /* clsid is same as parent */
4495         RegCreateKeyW(hkey,szCLSID,&hkey2);
4496         RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)clsid, (strlenW(clsid)+1) *
4497                        sizeof(WCHAR));
4498
4499         RegCloseKey(hkey2);
4500
4501         sz = 0x100;
4502         MSI_RecordGetStringW(row,2,buffer,&sz);
4503         rc = register_parent_progid(package,buffer,clsid);
4504
4505         if (!MSI_RecordIsNull(row,4))
4506         {
4507             sz = 0x100;
4508             MSI_RecordGetStringW(row,4,buffer,&sz);
4509             RegSetValueExW(hkey,NULL,0,REG_SZ,(LPVOID)buffer,
4510                            (strlenW(buffer)+1) * sizeof(WCHAR));
4511         }
4512
4513         if (!MSI_RecordIsNull(row,5))
4514             FIXME ("UNHANDLED icon in Progid\n");
4515
4516         RegCloseKey(hkey);
4517     }
4518     return rc;
4519 }
4520
4521 static UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package)
4522 {
4523     /* 
4524      * Sigh, here I am just brute force registering all progids
4525      * this needs to be linked to the Classes that have been registered
4526      * but the easiest way to do that is to load all these stuff into
4527      * memory for easy checking.
4528      *
4529      * Gives me something to continue to work toward.
4530      */
4531     UINT rc;
4532     MSIQUERY * view;
4533     MSIRECORD * row = 0;
4534     static const WCHAR Query[] = {
4535         'S','E','L','E','C','T',' ','*',' ',
4536         'F','R','O','M',' ','P','r','o','g','I','d',0};
4537
4538     if (!package)
4539         return ERROR_INVALID_HANDLE;
4540
4541     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
4542     if (rc != ERROR_SUCCESS)
4543         return ERROR_SUCCESS;
4544
4545     rc = MSI_ViewExecute(view, 0);
4546     if (rc != ERROR_SUCCESS)
4547     {
4548         MSI_ViewClose(view);
4549         msiobj_release(&view->hdr);
4550         return rc;
4551     }
4552
4553     while (1)
4554     {
4555         WCHAR clsid[0x1000];
4556
4557         rc = MSI_ViewFetch(view,&row);
4558         if (rc != ERROR_SUCCESS)
4559         {
4560             rc = ERROR_SUCCESS;
4561             break;
4562         }
4563         
4564         register_progid(package,row,clsid);
4565         ui_actiondata(package,szRegisterProgIdInfo,row);
4566
4567         msiobj_release(&row->hdr);
4568     }
4569     MSI_ViewClose(view);
4570     msiobj_release(&view->hdr);
4571     return rc;
4572 }
4573
4574 static UINT build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name, 
4575                             LPWSTR *FilePath)
4576 {
4577     LPWSTR ProductCode;
4578     LPWSTR SystemFolder;
4579     LPWSTR dest;
4580     UINT rc;
4581
4582     static const WCHAR szInstaller[] = 
4583         {'I','n','s','t','a','l','l','e','r','\\',0};
4584     static const WCHAR szProductCode[] =
4585         {'P','r','o','d','u','c','t','C','o','d','e',0};
4586     static const WCHAR szFolder[] =
4587         {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
4588
4589     ProductCode = load_dynamic_property(package,szProductCode,&rc);
4590     if (!ProductCode)
4591         return rc;
4592
4593     SystemFolder = load_dynamic_property(package,szFolder,NULL);
4594
4595     dest = build_directory_name(3, SystemFolder, szInstaller, ProductCode);
4596
4597     create_full_pathW(dest);
4598
4599     *FilePath = build_directory_name(2, dest, icon_name);
4600
4601     HeapFree(GetProcessHeap(),0,SystemFolder);
4602     HeapFree(GetProcessHeap(),0,ProductCode);
4603     HeapFree(GetProcessHeap(),0,dest);
4604     return ERROR_SUCCESS;
4605 }
4606
4607 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4608 {
4609     UINT rc;
4610     MSIQUERY * view;
4611     MSIRECORD * row = 0;
4612     static const WCHAR Query[] = {
4613        'S','E','L','E','C','T',' ','*',' ','f','r','o','m',' ',
4614        'S','h','o','r','t','c','u','t',0};
4615     IShellLinkW *sl;
4616     IPersistFile *pf;
4617     HRESULT res;
4618
4619     if (!package)
4620         return ERROR_INVALID_HANDLE;
4621
4622     res = CoInitialize( NULL );
4623     if (FAILED (res))
4624     {
4625         ERR("CoInitialize failed\n");
4626         return ERROR_FUNCTION_FAILED;
4627     }
4628
4629     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
4630     if (rc != ERROR_SUCCESS)
4631         return ERROR_SUCCESS;
4632
4633     rc = MSI_ViewExecute(view, 0);
4634     if (rc != ERROR_SUCCESS)
4635     {
4636         MSI_ViewClose(view);
4637         msiobj_release(&view->hdr);
4638         return rc;
4639     }
4640
4641     while (1)
4642     {
4643         LPWSTR target_file, target_folder;
4644         WCHAR buffer[0x100];
4645         DWORD sz;
4646         DWORD index;
4647         static const WCHAR szlnk[]={'.','l','n','k',0};
4648
4649         rc = MSI_ViewFetch(view,&row);
4650         if (rc != ERROR_SUCCESS)
4651         {
4652             rc = ERROR_SUCCESS;
4653             break;
4654         }
4655         
4656         sz = 0x100;
4657         MSI_RecordGetStringW(row,4,buffer,&sz);
4658
4659         index = get_loaded_component(package,buffer);
4660
4661         if (index < 0)
4662         {
4663             msiobj_release(&row->hdr);
4664             continue;
4665         }
4666
4667         if (package->components[index].ActionRequest != INSTALLSTATE_LOCAL)
4668         {
4669             TRACE("Skipping shortcut creation due to disabled component\n");
4670             msiobj_release(&row->hdr);
4671             continue;
4672         }
4673
4674         ui_actiondata(package,szCreateShortcuts,row);
4675
4676         res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
4677                               &IID_IShellLinkW, (LPVOID *) &sl );
4678
4679         if (FAILED(res))
4680         {
4681             ERR("Is IID_IShellLink\n");
4682             msiobj_release(&row->hdr);
4683             continue;
4684         }
4685
4686         res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
4687         if( FAILED( res ) )
4688         {
4689             ERR("Is IID_IPersistFile\n");
4690             msiobj_release(&row->hdr);
4691             continue;
4692         }
4693
4694         sz = 0x100;
4695         MSI_RecordGetStringW(row,2,buffer,&sz);
4696         target_folder = resolve_folder(package, buffer,FALSE,FALSE,NULL);
4697
4698         /* may be needed because of a bug somehwere else */
4699         create_full_pathW(target_folder);
4700
4701         sz = 0x100;
4702         MSI_RecordGetStringW(row,3,buffer,&sz);
4703         reduce_to_longfilename(buffer);
4704         if (!strchrW(buffer,'.'))
4705             strcatW(buffer,szlnk);
4706         target_file = build_directory_name(2, target_folder, buffer);
4707         HeapFree(GetProcessHeap(),0,target_folder);
4708
4709         sz = 0x100;
4710         MSI_RecordGetStringW(row,5,buffer,&sz);
4711         if (strchrW(buffer,'['))
4712         {
4713             LPWSTR deformated;
4714             deformat_string(package,buffer,&deformated);
4715             IShellLinkW_SetPath(sl,deformated);
4716             HeapFree(GetProcessHeap(),0,deformated);
4717         }
4718         else
4719         {
4720             FIXME("UNHANDLED shortcut format, advertised shortcut\n");
4721             IPersistFile_Release( pf );
4722             IShellLinkW_Release( sl );
4723             msiobj_release(&row->hdr);
4724             continue;
4725         }
4726
4727         if (!MSI_RecordIsNull(row,6))
4728         {
4729             LPWSTR deformated;
4730             sz = 0x100;
4731             MSI_RecordGetStringW(row,6,buffer,&sz);
4732             deformat_string(package,buffer,&deformated);
4733             IShellLinkW_SetArguments(sl,deformated);
4734             HeapFree(GetProcessHeap(),0,deformated);
4735         }
4736
4737         if (!MSI_RecordIsNull(row,7))
4738         {
4739             LPWSTR deformated;
4740             deformated = load_dynamic_stringW(row,7);
4741             IShellLinkW_SetDescription(sl,deformated);
4742             HeapFree(GetProcessHeap(),0,deformated);
4743         }
4744
4745         if (!MSI_RecordIsNull(row,8))
4746             IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
4747
4748         if (!MSI_RecordIsNull(row,9))
4749         {
4750             WCHAR *Path = NULL;
4751             INT index; 
4752
4753             sz = 0x100;
4754             MSI_RecordGetStringW(row,9,buffer,&sz);
4755
4756             build_icon_path(package,buffer,&Path);
4757             index = MSI_RecordGetInteger(row,10);
4758
4759             IShellLinkW_SetIconLocation(sl,Path,index);
4760             HeapFree(GetProcessHeap(),0,Path);
4761         }
4762
4763         if (!MSI_RecordIsNull(row,11))
4764             IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4765
4766         if (!MSI_RecordIsNull(row,12))
4767         {
4768             LPWSTR Path;
4769             sz = 0x100;
4770             MSI_RecordGetStringW(row,12,buffer,&sz);
4771             Path = resolve_folder(package, buffer, FALSE, FALSE, NULL);
4772             IShellLinkW_SetWorkingDirectory(sl,Path);
4773             HeapFree(GetProcessHeap(), 0, Path);
4774         }
4775
4776         TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
4777         IPersistFile_Save(pf,target_file,FALSE);
4778     
4779         HeapFree(GetProcessHeap(),0,target_file);    
4780
4781         IPersistFile_Release( pf );
4782         IShellLinkW_Release( sl );
4783
4784         msiobj_release(&row->hdr);
4785     }
4786     MSI_ViewClose(view);
4787     msiobj_release(&view->hdr);
4788
4789
4790     CoUninitialize();
4791
4792     return rc;
4793 }
4794
4795
4796 /*
4797  * 99% of the work done here is only done for 
4798  * advertised installs. However this is where the
4799  * Icon table is processed and written out
4800  * so that is what I am going to do here.
4801  */
4802 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4803 {
4804     UINT rc;
4805     MSIQUERY * view;
4806     MSIRECORD * row = 0;
4807     static const WCHAR Query[]={
4808         'S','E','L','E','C','T',' ','*',' ',
4809         'f','r','o','m',' ','I','c','o','n',0};
4810     DWORD sz;
4811
4812     if (!package)
4813         return ERROR_INVALID_HANDLE;
4814
4815     rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
4816     if (rc != ERROR_SUCCESS)
4817         return ERROR_SUCCESS;
4818
4819     rc = MSI_ViewExecute(view, 0);
4820     if (rc != ERROR_SUCCESS)
4821     {
4822         MSI_ViewClose(view);
4823         msiobj_release(&view->hdr);
4824         return rc;
4825     }
4826
4827     while (1)
4828     {
4829         HANDLE the_file;
4830         WCHAR *FilePath=NULL;
4831         WCHAR *FileName=NULL;
4832         CHAR buffer[1024];
4833
4834         rc = MSI_ViewFetch(view,&row);
4835         if (rc != ERROR_SUCCESS)
4836         {
4837             rc = ERROR_SUCCESS;
4838             break;
4839         }
4840     
4841         FileName = load_dynamic_stringW(row,1);
4842         if (!FileName)
4843         {
4844             ERR("Unable to get FileName\n");
4845             msiobj_release(&row->hdr);
4846             continue;
4847         }
4848
4849         build_icon_path(package,FileName,&FilePath);
4850
4851         HeapFree(GetProcessHeap(),0,FileName);
4852
4853         TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4854         
4855         the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4856                            FILE_ATTRIBUTE_NORMAL, NULL);
4857
4858         if (the_file == INVALID_HANDLE_VALUE)
4859         {
4860             ERR("Unable to create file %s\n",debugstr_w(FilePath));
4861             msiobj_release(&row->hdr);
4862             HeapFree(GetProcessHeap(),0,FilePath);
4863             continue;
4864         }
4865
4866         do 
4867         {
4868             DWORD write;
4869             sz = 1024;
4870             rc = MSI_RecordReadStream(row,2,buffer,&sz);
4871             if (rc != ERROR_SUCCESS)
4872             {
4873                 ERR("Failed to get stream\n");
4874                 CloseHandle(the_file);  
4875                 DeleteFileW(FilePath);
4876                 break;
4877             }
4878             WriteFile(the_file,buffer,sz,&write,NULL);
4879         } while (sz == 1024);
4880
4881         HeapFree(GetProcessHeap(),0,FilePath);
4882
4883         CloseHandle(the_file);
4884         msiobj_release(&row->hdr);
4885     }
4886     MSI_ViewClose(view);
4887     msiobj_release(&view->hdr);
4888     return rc;
4889
4890 }
4891
4892 /* Msi functions that seem appropriate here */
4893 UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
4894 {
4895     LPWSTR szwAction;
4896     UINT rc;
4897
4898     TRACE(" exteral attempt at action %s\n",szAction);
4899
4900     if (!szAction)
4901         return ERROR_FUNCTION_FAILED;
4902     if (hInstall == 0)
4903         return ERROR_FUNCTION_FAILED;
4904
4905     szwAction = strdupAtoW(szAction);
4906
4907     if (!szwAction)
4908         return ERROR_FUNCTION_FAILED; 
4909
4910
4911     rc = MsiDoActionW(hInstall, szwAction);
4912     HeapFree(GetProcessHeap(),0,szwAction);
4913     return rc;
4914 }
4915
4916 UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction )
4917 {
4918     MSIPACKAGE *package;
4919     UINT ret = ERROR_INVALID_HANDLE;
4920
4921     TRACE(" external attempt at action %s \n",debugstr_w(szAction));
4922
4923     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
4924     if( package )
4925     {
4926         ret = ACTION_PerformAction(package,szAction);
4927         msiobj_release( &package->hdr );
4928     }
4929     return ret;
4930 }
4931
4932 UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder, 
4933                                LPSTR szPathBuf, DWORD* pcchPathBuf) 
4934 {
4935     LPWSTR szwFolder;
4936     LPWSTR szwPathBuf;
4937     UINT rc;
4938
4939     TRACE("getting folder %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
4940
4941     if (!szFolder)
4942         return ERROR_FUNCTION_FAILED;
4943     if (hInstall == 0)
4944         return ERROR_FUNCTION_FAILED;
4945
4946     szwFolder = strdupAtoW(szFolder);
4947
4948     if (!szwFolder)
4949         return ERROR_FUNCTION_FAILED; 
4950
4951     szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
4952
4953     rc = MsiGetTargetPathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
4954
4955     WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
4956                          *pcchPathBuf, NULL, NULL );
4957
4958     HeapFree(GetProcessHeap(),0,szwFolder);
4959     HeapFree(GetProcessHeap(),0,szwPathBuf);
4960
4961     return rc;
4962 }
4963
4964 UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
4965                                 szPathBuf, DWORD* pcchPathBuf) 
4966 {
4967     LPWSTR path;
4968     UINT rc = ERROR_FUNCTION_FAILED;
4969     MSIPACKAGE *package;
4970
4971     TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
4972
4973     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
4974     if (!package)
4975         return ERROR_INVALID_HANDLE;
4976     path = resolve_folder(package, szFolder, FALSE, FALSE, NULL);
4977     msiobj_release( &package->hdr );
4978
4979     if (path && (strlenW(path) > *pcchPathBuf))
4980     {
4981         *pcchPathBuf = strlenW(path)+1;
4982         rc = ERROR_MORE_DATA;
4983     }
4984     else if (path)
4985     {
4986         *pcchPathBuf = strlenW(path)+1;
4987         strcpyW(szPathBuf,path);
4988         TRACE("Returning Path %s\n",debugstr_w(path));
4989         rc = ERROR_SUCCESS;
4990     }
4991     HeapFree(GetProcessHeap(),0,path);
4992     
4993     return rc;
4994 }
4995
4996
4997 UINT WINAPI MsiGetSourcePathA( MSIHANDLE hInstall, LPCSTR szFolder, 
4998                                LPSTR szPathBuf, DWORD* pcchPathBuf) 
4999 {
5000     LPWSTR szwFolder;
5001     LPWSTR szwPathBuf;
5002     UINT rc;
5003
5004     TRACE("getting source %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
5005
5006     if (!szFolder)
5007         return ERROR_FUNCTION_FAILED;
5008     if (hInstall == 0)
5009         return ERROR_FUNCTION_FAILED;
5010
5011     szwFolder = strdupAtoW(szFolder);
5012     if (!szwFolder)
5013         return ERROR_FUNCTION_FAILED; 
5014
5015     szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
5016
5017     rc = MsiGetSourcePathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
5018
5019     WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
5020                          *pcchPathBuf, NULL, NULL );
5021
5022     HeapFree(GetProcessHeap(),0,szwFolder);
5023     HeapFree(GetProcessHeap(),0,szwPathBuf);
5024
5025     return rc;
5026 }
5027
5028 UINT WINAPI MsiGetSourcePathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
5029                                 szPathBuf, DWORD* pcchPathBuf) 
5030 {
5031     LPWSTR path;
5032     UINT rc = ERROR_FUNCTION_FAILED;
5033     MSIPACKAGE *package;
5034
5035     TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
5036
5037     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
5038     if( !package )
5039         return ERROR_INVALID_HANDLE;
5040     path = resolve_folder(package, szFolder, TRUE, FALSE, NULL);
5041     msiobj_release( &package->hdr );
5042
5043     if (path && strlenW(path) > *pcchPathBuf)
5044     {
5045         *pcchPathBuf = strlenW(path)+1;
5046         rc = ERROR_MORE_DATA;
5047     }
5048     else if (path)
5049     {
5050         *pcchPathBuf = strlenW(path)+1;
5051         strcpyW(szPathBuf,path);
5052         TRACE("Returning Path %s\n",debugstr_w(path));
5053         rc = ERROR_SUCCESS;
5054     }
5055     HeapFree(GetProcessHeap(),0,path);
5056     
5057     return rc;
5058 }
5059
5060
5061 UINT WINAPI MsiSetTargetPathA(MSIHANDLE hInstall, LPCSTR szFolder, 
5062                              LPCSTR szFolderPath)
5063 {
5064     LPWSTR szwFolder;
5065     LPWSTR szwFolderPath;
5066     UINT rc;
5067
5068     if (!szFolder)
5069         return ERROR_FUNCTION_FAILED;
5070     if (hInstall == 0)
5071         return ERROR_FUNCTION_FAILED;
5072
5073     szwFolder = strdupAtoW(szFolder);
5074     if (!szwFolder)
5075         return ERROR_FUNCTION_FAILED; 
5076
5077     szwFolderPath = strdupAtoW(szFolderPath);
5078     if (!szwFolderPath)
5079     {
5080         HeapFree(GetProcessHeap(),0,szwFolder);
5081         return ERROR_FUNCTION_FAILED; 
5082     }
5083
5084     rc = MsiSetTargetPathW(hInstall, szwFolder, szwFolderPath);
5085
5086     HeapFree(GetProcessHeap(),0,szwFolder);
5087     HeapFree(GetProcessHeap(),0,szwFolderPath);
5088
5089     return rc;
5090 }
5091
5092 UINT MSI_SetTargetPathW(MSIPACKAGE *package, LPCWSTR szFolder, 
5093                              LPCWSTR szFolderPath)
5094 {
5095     DWORD i;
5096     LPWSTR path = NULL;
5097     LPWSTR path2 = NULL;
5098     INT len;
5099     MSIFOLDER *folder;
5100
5101     TRACE("(%p %s %s)\n",package, debugstr_w(szFolder),debugstr_w(szFolderPath));
5102
5103     if (package==NULL)
5104         return ERROR_INVALID_HANDLE;
5105
5106     if (szFolderPath[0]==0)
5107         return ERROR_FUNCTION_FAILED;
5108
5109     if (GetFileAttributesW(szFolderPath) == INVALID_FILE_ATTRIBUTES)
5110         return ERROR_FUNCTION_FAILED;
5111
5112     path = resolve_folder(package,szFolder,FALSE,FALSE,&folder);
5113
5114     if (!path)
5115         return ERROR_INVALID_PARAMETER;
5116
5117     if (folder->Property)
5118         HeapFree(GetProcessHeap(),0,folder->Property);
5119
5120     len = strlenW(szFolderPath);
5121
5122     if (szFolderPath[len-1]!='\\')
5123     {
5124         len +=2;
5125         folder->Property = HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR));
5126         strcpyW(folder->Property,szFolderPath);
5127         strcatW(folder->Property,cszbs);
5128     }
5129     else
5130         folder->Property = dupstrW(szFolderPath);
5131
5132     if (strcmpiW(path, szFolderPath) == 0)
5133     {
5134         /*
5135          *  Resolved Target has not really changed, so just 
5136          *  set this folder and do not recalculate everything.
5137          */
5138         HeapFree(GetProcessHeap(),0,folder->ResolvedTarget);
5139         folder->ResolvedTarget = NULL;
5140         path2 = resolve_folder(package,szFolder,FALSE,TRUE,NULL);
5141         HeapFree(GetProcessHeap(),0,path2);
5142     }
5143     else
5144     {
5145         for (i = 0; i < package->loaded_folders; i++)
5146         {
5147             if (package->folders[i].ResolvedTarget)
5148                 HeapFree(GetProcessHeap(),0,package->folders[i].ResolvedTarget);
5149             package->folders[i].ResolvedTarget=NULL;
5150         }
5151
5152         for (i = 0; i < package->loaded_folders; i++)
5153         {
5154             path2=resolve_folder(package, package->folders[i].Directory, FALSE,
5155                        TRUE, NULL);
5156             HeapFree(GetProcessHeap(),0,path2);
5157         }
5158     }
5159     HeapFree(GetProcessHeap(),0,path);
5160
5161     return ERROR_SUCCESS;
5162 }
5163
5164 UINT WINAPI MsiSetTargetPathW(MSIHANDLE hInstall, LPCWSTR szFolder, 
5165                              LPCWSTR szFolderPath)
5166 {
5167     MSIPACKAGE *package;
5168     UINT ret;
5169
5170     TRACE("(%s %s)\n",debugstr_w(szFolder),debugstr_w(szFolderPath));
5171
5172     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
5173     ret = MSI_SetTargetPathW( package, szFolder, szFolderPath );
5174     msiobj_release( &package->hdr );
5175     return ret;
5176 }
5177
5178 /***********************************************************************
5179  *           MsiGetMode    (MSI.@)
5180  *
5181  * Returns an internal installer state (if it is running in a mode iRunMode)
5182  *
5183  * PARAMS
5184  *   hInstall    [I]  Handle to the installation
5185  *   hRunMode    [I]  Checking run mode
5186  *        MSIRUNMODE_ADMIN             Administrative mode
5187  *        MSIRUNMODE_ADVERTISE         Advertisement mode
5188  *        MSIRUNMODE_MAINTENANCE       Maintenance mode
5189  *        MSIRUNMODE_ROLLBACKENABLED   Rollback is enabled
5190  *        MSIRUNMODE_LOGENABLED        Log file is writing
5191  *        MSIRUNMODE_OPERATIONS        Operations in progress??
5192  *        MSIRUNMODE_REBOOTATEND       We need to reboot after installation completed
5193  *        MSIRUNMODE_REBOOTNOW         We need to reboot to continue the installation
5194  *        MSIRUNMODE_CABINET           Files from cabinet are installed
5195  *        MSIRUNMODE_SOURCESHORTNAMES  Long names in source files is supressed
5196  *        MSIRUNMODE_TARGETSHORTNAMES  Long names in destination files is supressed
5197  *        MSIRUNMODE_RESERVED11        Reserved
5198  *        MSIRUNMODE_WINDOWS9X         Running under Windows95/98
5199  *        MSIRUNMODE_ZAWENABLED        Demand installation is supported
5200  *        MSIRUNMODE_RESERVED14        Reserved
5201  *        MSIRUNMODE_RESERVED15        Reserved
5202  *        MSIRUNMODE_SCHEDULED         called from install script
5203  *        MSIRUNMODE_ROLLBACK          called from rollback script
5204  *        MSIRUNMODE_COMMIT            called from commit script
5205  *
5206  * RETURNS
5207  *    In the state: TRUE
5208  *    Not in the state: FALSE
5209  *
5210  */
5211
5212 BOOL WINAPI MsiGetMode(MSIHANDLE hInstall, MSIRUNMODE iRunMode)
5213 {
5214     FIXME("STUB (iRunMode=%i)\n",iRunMode);
5215     return TRUE;
5216 }
5217
5218 /*
5219  * According to the docs, when this is called it immediately recalculates
5220  * all the component states as well
5221  */
5222 UINT WINAPI MsiSetFeatureStateA(MSIHANDLE hInstall, LPCSTR szFeature,
5223                                 INSTALLSTATE iState)
5224 {
5225     LPWSTR szwFeature = NULL;
5226     UINT rc;
5227
5228     szwFeature = strdupAtoW(szFeature);
5229
5230     if (!szwFeature)
5231         return ERROR_FUNCTION_FAILED;
5232    
5233     rc = MsiSetFeatureStateW(hInstall,szwFeature, iState); 
5234
5235     HeapFree(GetProcessHeap(),0,szwFeature);
5236
5237     return rc;
5238 }
5239
5240 UINT WINAPI MsiSetFeatureStateW(MSIHANDLE hInstall, LPCWSTR szFeature,
5241                                 INSTALLSTATE iState)
5242 {
5243     MSIPACKAGE* package;
5244     INT index;
5245
5246     TRACE(" %s to %i\n",debugstr_w(szFeature), iState);
5247
5248     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
5249     if (!package)
5250         return ERROR_INVALID_HANDLE;
5251
5252     index = get_loaded_feature(package,szFeature);
5253     if (index < 0)
5254         return ERROR_UNKNOWN_FEATURE;
5255
5256     package->features[index].ActionRequest= iState;
5257
5258     return ERROR_SUCCESS;
5259 }
5260
5261 UINT WINAPI MsiGetFeatureStateA(MSIHANDLE hInstall, LPSTR szFeature,
5262                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
5263 {
5264     LPWSTR szwFeature = NULL;
5265     UINT rc;
5266     
5267     szwFeature = strdupAtoW(szFeature);
5268
5269     rc = MsiGetFeatureStateW(hInstall,szwFeature,piInstalled, piAction);
5270
5271     HeapFree( GetProcessHeap(), 0 , szwFeature);
5272
5273     return rc;
5274 }
5275
5276 UINT MSI_GetFeatureStateW(MSIPACKAGE *package, LPWSTR szFeature,
5277                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
5278 {
5279     INT index;
5280
5281     index = get_loaded_feature(package,szFeature);
5282     if (index < 0)
5283         return ERROR_UNKNOWN_FEATURE;
5284
5285     if (piInstalled)
5286         *piInstalled = package->features[index].Installed;
5287
5288     if (piAction)
5289         *piAction = package->features[index].Action;
5290
5291     TRACE("returning %i %i\n",*piInstalled,*piAction);
5292
5293     return ERROR_SUCCESS;
5294 }
5295
5296 UINT WINAPI MsiGetFeatureStateW(MSIHANDLE hInstall, LPWSTR szFeature,
5297                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
5298 {
5299     MSIPACKAGE* package;
5300     UINT ret;
5301
5302     TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szFeature), piInstalled,
5303 piAction);
5304
5305     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
5306     if (!package)
5307         return ERROR_INVALID_HANDLE;
5308     ret = MSI_GetFeatureStateW(package, szFeature, piInstalled, piAction);
5309     msiobj_release( &package->hdr );
5310     return ret;
5311 }
5312
5313 UINT WINAPI MsiGetComponentStateA(MSIHANDLE hInstall, LPSTR szComponent,
5314                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
5315 {
5316     LPWSTR szwComponent= NULL;
5317     UINT rc;
5318     
5319     szwComponent= strdupAtoW(szComponent);
5320
5321     rc = MsiGetComponentStateW(hInstall,szwComponent,piInstalled, piAction);
5322
5323     HeapFree( GetProcessHeap(), 0 , szwComponent);
5324
5325     return rc;
5326 }
5327
5328 UINT MSI_GetComponentStateW(MSIPACKAGE *package, LPWSTR szComponent,
5329                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
5330 {
5331     INT index;
5332
5333     TRACE("%p %s %p %p\n", package, debugstr_w(szComponent), piInstalled,
5334 piAction);
5335
5336     index = get_loaded_component(package,szComponent);
5337     if (index < 0)
5338         return ERROR_UNKNOWN_COMPONENT;
5339
5340     if (piInstalled)
5341         *piInstalled = package->components[index].Installed;
5342
5343     if (piAction)
5344         *piInstalled = package->components[index].Action;
5345
5346     return ERROR_SUCCESS;
5347 }
5348
5349 UINT WINAPI MsiGetComponentStateW(MSIHANDLE hInstall, LPWSTR szComponent,
5350                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
5351 {
5352     MSIPACKAGE* package;
5353     UINT ret;
5354
5355     TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szComponent),
5356            piInstalled, piAction);
5357
5358     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
5359     if (!package)
5360         return ERROR_INVALID_HANDLE;
5361     ret = MSI_GetComponentStateW( package, szComponent, piInstalled, piAction);
5362     msiobj_release( &package->hdr );
5363     return ret;
5364 }
5365
5366 #if 0
5367 static UINT ACTION_Template(MSIPACKAGE *package)
5368 {
5369     UINT rc;
5370     MSIQUERY * view;
5371     MSIRECORD * row = 0;
5372     static const WCHAR ExecSeqQuery[] = {0};
5373
5374     rc = MsiDatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5375     if (rc != ERROR_SUCCESS)
5376         return rc;
5377
5378     rc = MsiViewExecute(view, 0);
5379     if (rc != ERROR_SUCCESS)
5380     {
5381         MsiViewClose(view);
5382         msiobj_release(&view->hdr);
5383         return rc;
5384     }
5385
5386     while (1)
5387     {
5388         rc = MsiViewFetch(view,&row);
5389         if (rc != ERROR_SUCCESS)
5390         {
5391             rc = ERROR_SUCCESS;
5392             break;
5393         }
5394
5395         msiobj_release(&row->hdr);
5396     }
5397     MsiViewClose(view);
5398     msiobj_release(&view->hdr);
5399     return rc;
5400 }
5401 #endif