Remove spaces before '\n' in traces.
[wine] / dlls / msi / files.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Aric Stewart for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21
22 /*
23  * Actions dealing with files These are
24  *
25  * InstallFiles
26  * DuplicateFiles
27  * MoveFiles (TODO)
28  * PatchFiles (TODO)
29  * RemoveDuplicateFiles(TODO)
30  * RemoveFiles(TODO)
31  */
32
33 #include <stdarg.h>
34
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winerror.h"
38 #include "wine/debug.h"
39 #include "fdi.h"
40 #include "msi.h"
41 #include "msidefs.h"
42 #include "msvcrt/fcntl.h"
43 #include "msipriv.h"
44 #include "winuser.h"
45 #include "wine/unicode.h"
46 #include "action.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(msi);
49
50 extern const WCHAR szInstallFiles[];
51 extern const WCHAR szDuplicateFiles[];
52 extern const WCHAR szMoveFiles[];
53 extern const WCHAR szPatchFiles[];
54 extern const WCHAR szRemoveDuplicateFiles[];
55 extern const WCHAR szRemoveFiles[];
56
57 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
58
59
60 /*
61  * This is a helper function for handling embedded cabinet media
62  */
63 static UINT writeout_cabinet_stream(MSIPACKAGE *package, LPCWSTR stream_name,
64                                     WCHAR* source)
65 {
66     UINT rc;
67     USHORT* data;
68     UINT    size;
69     DWORD   write;
70     HANDLE  the_file;
71     WCHAR tmp[MAX_PATH];
72
73     rc = read_raw_stream_data(package->db,stream_name,&data,&size); 
74     if (rc != ERROR_SUCCESS)
75         return rc;
76
77     write = MAX_PATH;
78     if (MSI_GetPropertyW(package, cszTempFolder, tmp, &write))
79         GetTempPathW(MAX_PATH,tmp);
80
81     GetTempFileNameW(tmp,stream_name,0,source);
82
83     track_tempfile(package,strrchrW(source,'\\'), source);
84     the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
85                            FILE_ATTRIBUTE_NORMAL, NULL);
86
87     if (the_file == INVALID_HANDLE_VALUE)
88     {
89         ERR("Unable to create file %s\n",debugstr_w(source));
90         rc = ERROR_FUNCTION_FAILED;
91         goto end;
92     }
93
94     WriteFile(the_file,data,size,&write,NULL);
95     CloseHandle(the_file);
96     TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
97 end:
98     msi_free(data);
99     return rc;
100 }
101
102
103 /* Support functions for FDI functions */
104 typedef struct
105 {
106     MSIPACKAGE* package;
107     LPCSTR cab_path;
108 } CabData;
109
110 static void * cabinet_alloc(ULONG cb)
111 {
112     return msi_alloc(cb);
113 }
114
115 static void cabinet_free(void *pv)
116 {
117     msi_free(pv);
118 }
119
120 static INT_PTR cabinet_open(char *pszFile, int oflag, int pmode)
121 {
122     HANDLE handle;
123     DWORD dwAccess = 0;
124     DWORD dwShareMode = 0;
125     DWORD dwCreateDisposition = OPEN_EXISTING;
126     switch (oflag & _O_ACCMODE)
127     {
128     case _O_RDONLY:
129         dwAccess = GENERIC_READ;
130         dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE;
131         break;
132     case _O_WRONLY:
133         dwAccess = GENERIC_WRITE;
134         dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
135         break;
136     case _O_RDWR:
137         dwAccess = GENERIC_READ | GENERIC_WRITE;
138         dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
139         break;
140     }
141     if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL))
142         dwCreateDisposition = CREATE_NEW;
143     else if (oflag & _O_CREAT)
144         dwCreateDisposition = CREATE_ALWAYS;
145     handle = CreateFileA( pszFile, dwAccess, dwShareMode, NULL, 
146                           dwCreateDisposition, 0, NULL );
147     if (handle == INVALID_HANDLE_VALUE)
148         return 0;
149     return (INT_PTR) handle;
150 }
151
152 static UINT cabinet_read(INT_PTR hf, void *pv, UINT cb)
153 {
154     HANDLE handle = (HANDLE) hf;
155     DWORD dwRead;
156     if (ReadFile(handle, pv, cb, &dwRead, NULL))
157         return dwRead;
158     return 0;
159 }
160
161 static UINT cabinet_write(INT_PTR hf, void *pv, UINT cb)
162 {
163     HANDLE handle = (HANDLE) hf;
164     DWORD dwWritten;
165     if (WriteFile(handle, pv, cb, &dwWritten, NULL))
166         return dwWritten;
167     return 0;
168 }
169
170 static int cabinet_close(INT_PTR hf)
171 {
172     HANDLE handle = (HANDLE) hf;
173     return CloseHandle(handle) ? 0 : -1;
174 }
175
176 static long cabinet_seek(INT_PTR hf, long dist, int seektype)
177 {
178     HANDLE handle = (HANDLE) hf;
179     /* flags are compatible and so are passed straight through */
180     return SetFilePointer(handle, dist, NULL, seektype);
181 }
182
183 static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f )
184 {
185     MSIRECORD *uirow;
186     LPWSTR uipath, p;
187
188     /* the UI chunk */
189     uirow = MSI_CreateRecord( 9 );
190     MSI_RecordSetStringW( uirow, 1, f->FileName );
191     uipath = strdupW( f->TargetPath );
192     p = strrchrW(uipath,'\\');
193     if (p)
194         p[1]=0;
195     MSI_RecordSetStringW( uirow, 9, uipath);
196     MSI_RecordSetInteger( uirow, 6, f->FileSize );
197     ui_actiondata( package, szInstallFiles, uirow);
198     msiobj_release( &uirow->hdr );
199     msi_free( uipath );
200     ui_progress( package, 2, f->FileSize, 0, 0);
201 }
202
203 static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
204 {
205     switch (fdint)
206     {
207     case fdintCOPY_FILE:
208     {
209         CabData *data = (CabData*) pfdin->pv;
210         HANDLE handle;
211         LPWSTR file;
212         MSIFILE *f;
213
214         file = strdupAtoW(pfdin->psz1);
215         f = get_loaded_file(data->package, file);
216         msi_free(file);
217
218         if (!f)
219         {
220             ERR("Unknown File in Cabinet (%s)\n",debugstr_a(pfdin->psz1));
221             return 0;
222         }
223
224         if (f->state != msifs_missing && f->state != msifs_overwrite)
225         {
226             TRACE("Skipping extraction of %s\n",debugstr_a(pfdin->psz1));
227             return 0;
228         }
229
230         msi_file_update_ui( data->package, f );
231
232         TRACE("extracting %s\n", debugstr_w(f->TargetPath) );
233
234         handle = CreateFileW( f->TargetPath, GENERIC_READ | GENERIC_WRITE, 0,
235                               NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
236         if ( handle == INVALID_HANDLE_VALUE )
237         {
238             ERR("failed to create %s (error %ld)\n",
239                 debugstr_w( f->TargetPath ), GetLastError() );
240             return 0;
241         }
242
243         f->state = msifs_installed;
244         return (INT_PTR) handle;
245     }
246     case fdintCLOSE_FILE_INFO:
247     {
248         FILETIME ft;
249         FILETIME ftLocal;
250         HANDLE handle = (HANDLE) pfdin->hf;
251
252         if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft))
253             return -1;
254         if (!LocalFileTimeToFileTime(&ft, &ftLocal))
255             return -1;
256         if (!SetFileTime(handle, &ftLocal, 0, &ftLocal))
257             return -1;
258         CloseHandle(handle);
259         return 1;
260     }
261     default:
262         return 0;
263     }
264 }
265
266 /***********************************************************************
267  *            extract_cabinet_file
268  *
269  * Extract files from a cab file.
270  */
271 static BOOL extract_cabinet_file(MSIPACKAGE* package, LPCWSTR source, 
272                                  LPCWSTR path)
273 {
274     HFDI hfdi;
275     ERF erf;
276     BOOL ret;
277     char *cabinet;
278     char *cab_path;
279     CabData data;
280
281     TRACE("Extracting %s to %s\n",debugstr_w(source), debugstr_w(path));
282
283     hfdi = FDICreate(cabinet_alloc,
284                      cabinet_free,
285                      cabinet_open,
286                      cabinet_read,
287                      cabinet_write,
288                      cabinet_close,
289                      cabinet_seek,
290                      0,
291                      &erf);
292     if (!hfdi)
293     {
294         ERR("FDICreate failed\n");
295         return FALSE;
296     }
297
298     if (!(cabinet = strdupWtoA( source )))
299     {
300         FDIDestroy(hfdi);
301         return FALSE;
302     }
303     if (!(cab_path = strdupWtoA( path )))
304     {
305         FDIDestroy(hfdi);
306         msi_free(cabinet);
307         return FALSE;
308     }
309
310     data.package = package;
311     data.cab_path = cab_path;
312
313     ret = FDICopy(hfdi, cabinet, "", 0, cabinet_notify, NULL, &data);
314
315     if (!ret)
316         ERR("FDICopy failed\n");
317
318     FDIDestroy(hfdi);
319
320     msi_free(cabinet);
321     msi_free(cab_path);
322
323     return ret;
324 }
325
326 static VOID set_file_source(MSIPACKAGE* package, MSIFILE* file, MSICOMPONENT*
327         comp, LPCWSTR path)
328 {
329     if (file->Attributes & msidbFileAttributesNoncompressed)
330     {
331         LPWSTR p;
332         p = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
333         file->SourcePath = build_directory_name(2, p, file->ShortName);
334         msi_free(p);
335     }
336     else
337         file->SourcePath = build_directory_name(2, path, file->File);
338 }
339
340 static BOOL check_volume(LPCWSTR path, LPCWSTR want_volume, LPWSTR volume, 
341         UINT *intype)
342 {
343     WCHAR drive[4];
344     WCHAR name[MAX_PATH];
345     UINT type;
346
347     if (!(path[0] && path[1] == ':'))
348         return TRUE;
349
350     drive[0] = path[0];
351     drive[1] = path[1];
352     drive[2] = '\\';
353     drive[3] = 0;
354     TRACE("Checking volume %s .. (%s)\n",debugstr_w(drive), debugstr_w(want_volume));
355     type = GetDriveTypeW(drive);
356     TRACE("drive is of type %x\n",type);
357
358     if (type == DRIVE_UNKNOWN || type == DRIVE_NO_ROOT_DIR || 
359             type == DRIVE_FIXED || type == DRIVE_RAMDISK)
360         return TRUE;
361
362     GetVolumeInformationW(drive, name, MAX_PATH, NULL, NULL, NULL, NULL, 0);
363     TRACE("Drive contains %s\n", debugstr_w(name));
364     volume = strdupW(name);
365     if (*intype)
366         *intype=type;
367     return (strcmpiW(want_volume,name)==0);
368 }
369
370 static BOOL check_for_sourcefile(LPCWSTR source)
371 {
372     DWORD attrib = GetFileAttributesW(source);
373     return (!(attrib == INVALID_FILE_ATTRIBUTES));
374 }
375
376 static UINT ready_volume(MSIPACKAGE* package, LPCWSTR path, LPWSTR last_volume, 
377                          MSIRECORD *row,UINT *type )
378 {
379     LPWSTR volume = NULL; 
380     LPCWSTR want_volume = MSI_RecordGetString(row, 5);
381     BOOL ok = check_volume(path, want_volume, volume, type);
382
383     TRACE("Readying Volume for %s (%s, %s)\n", debugstr_w(path),
384           debugstr_w(want_volume), debugstr_w(last_volume));
385
386     if (check_for_sourcefile(path) && !ok)
387     {
388         FIXME("Found the Sourcefile but not on the correct volume.(%s,%s,%s)\n",
389                 debugstr_w(path),debugstr_w(want_volume), debugstr_w(volume));
390         return ERROR_SUCCESS;
391     }
392
393     while (!ok)
394     {
395         INT rc;
396         LPCWSTR prompt;
397         LPWSTR msg;
398       
399         prompt = MSI_RecordGetString(row,3);
400         msg = generate_error_string(package, 1302, 1, prompt);
401         rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
402         msi_free(volume);
403         msi_free(msg);
404         if (rc == IDOK)
405             ok = check_for_sourcefile(path);
406         else
407             return ERROR_INSTALL_USEREXIT;
408     }
409
410     msi_free(last_volume);
411     last_volume = strdupW(volume);
412     return ERROR_SUCCESS;
413 }
414
415 struct media_info {
416     UINT last_sequence; 
417     LPWSTR last_volume;
418     LPWSTR last_path;
419     DWORD count;
420     WCHAR source[MAX_PATH];
421 };
422
423 static struct media_info *create_media_info( void )
424 {
425     struct media_info *mi;
426
427     mi = msi_alloc( sizeof *mi  );
428     if (mi)
429     {
430         mi->last_sequence = 0; 
431         mi->last_volume = NULL;
432         mi->last_path = NULL;
433         mi->count = 0;
434         mi->source[0] = 0;
435     }
436
437     return mi;
438 }
439
440 static void free_media_info( struct media_info *mi )
441 {
442     msi_free( mi->last_path );
443     msi_free( mi );
444 }
445
446 static UINT ready_media_for_file( MSIPACKAGE *package, struct media_info *mi,
447                                   MSIFILE *file )
448 {
449     UINT rc = ERROR_SUCCESS;
450     MSIRECORD * row = 0;
451     static const WCHAR ExecSeqQuery[] =
452         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
453          '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
454          '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',
455          ' ','%', 'i',' ','O','R','D','E','R',' ','B','Y',' ',
456          '`','L','a','s','t','S','e','q','u','e','n','c','e','`',0};
457     LPCWSTR cab, volume;
458     DWORD sz;
459     INT seq;
460     UINT type;
461     LPCWSTR prompt;
462     MSICOMPONENT *comp = file->Component;
463
464     if (file->Sequence <= mi->last_sequence)
465     {
466         set_file_source(package,file,comp,mi->last_path);
467         TRACE("Media already ready (%u, %u)\n",file->Sequence,mi->last_sequence);
468         return ERROR_SUCCESS;
469     }
470
471     mi->count ++;
472     row = MSI_QueryGetRecord(package->db, ExecSeqQuery, file->Sequence);
473     if (!row)
474     {
475         TRACE("Unable to query row\n");
476         return ERROR_FUNCTION_FAILED;
477     }
478
479     seq = MSI_RecordGetInteger(row,2);
480     mi->last_sequence = seq;
481
482     volume = MSI_RecordGetString(row, 5);
483     prompt = MSI_RecordGetString(row, 3);
484
485     msi_free(mi->last_path);
486     mi->last_path = NULL;
487
488     if (file->Attributes & msidbFileAttributesNoncompressed)
489     {
490         mi->last_path = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
491         set_file_source(package,file,comp,mi->last_path);
492         rc = ready_volume(package, file->SourcePath, mi->last_volume, row,&type);
493
494         MsiSourceListAddMediaDiskW(package->ProductCode, NULL, 
495             MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, volume,
496             prompt);
497
498         if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || 
499                 type == DRIVE_RAMDISK)
500             MsiSourceListSetInfoW(package->ProductCode, NULL, 
501                 MSIINSTALLCONTEXT_USERMANAGED, 
502                 MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
503                 INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
504         else
505             MsiSourceListSetInfoW(package->ProductCode, NULL, 
506                 MSIINSTALLCONTEXT_USERMANAGED, 
507                 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
508                 INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
509         msiobj_release(&row->hdr);
510         return rc;
511     }
512
513     cab = MSI_RecordGetString(row,4);
514     if (cab)
515     {
516         TRACE("Source is CAB %s\n",debugstr_w(cab));
517         /* the stream does not contain the # character */
518         if (cab[0]=='#')
519         {
520             LPWSTR path;
521
522             writeout_cabinet_stream(package,&cab[1],mi->source);
523             mi->last_path = strdupW(mi->source);
524             *(strrchrW(mi->last_path,'\\')+1)=0;
525
526             path = msi_dup_property( package, cszSourceDir );
527
528             MsiSourceListAddMediaDiskW(package->ProductCode, NULL, 
529                 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count,
530                 volume, prompt);
531
532             MsiSourceListSetInfoW(package->ProductCode, NULL,
533                 MSIINSTALLCONTEXT_USERMANAGED,
534                 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
535                 INSTALLPROPERTY_LASTUSEDSOURCEW, path);
536
537             msi_free(path);
538         }
539         else
540         {
541             sz = MAX_PATH;
542             mi->last_path = msi_alloc(MAX_PATH*sizeof(WCHAR));
543             if (MSI_GetPropertyW(package, cszSourceDir, mi->source, &sz))
544             {
545                 ERR("No Source dir defined\n");
546                 rc = ERROR_FUNCTION_FAILED;
547             }
548             else
549             {
550                 strcpyW(mi->last_path,mi->source);
551                 strcatW(mi->source,cab);
552
553                 rc = ready_volume(package, mi->source, mi->last_volume, row, &type);
554                 if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || 
555                         type == DRIVE_RAMDISK)
556                     MsiSourceListSetInfoW(package->ProductCode, NULL,
557                             MSIINSTALLCONTEXT_USERMANAGED,
558                             MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
559                             INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
560                 else
561                     MsiSourceListSetInfoW(package->ProductCode, NULL,
562                             MSIINSTALLCONTEXT_USERMANAGED,
563                             MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
564                             INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
565
566                 /* extract the cab file into a folder in the temp folder */
567                 sz = MAX_PATH;
568                 if (MSI_GetPropertyW(package, cszTempFolder,mi->last_path, &sz) 
569                                     != ERROR_SUCCESS)
570                     GetTempPathW(MAX_PATH,mi->last_path);
571             }
572         }
573         rc = !extract_cabinet_file(package, mi->source, mi->last_path);
574     }
575     else
576     {
577         sz = MAX_PATH;
578         mi->last_path = msi_alloc(MAX_PATH*sizeof(WCHAR));
579         MSI_GetPropertyW(package,cszSourceDir,mi->source,&sz);
580         strcpyW(mi->last_path,mi->source);
581         rc = ready_volume(package, mi->last_path, mi->last_volume, row, &type);
582
583         if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || 
584                 type == DRIVE_RAMDISK)
585             MsiSourceListSetInfoW(package->ProductCode, NULL,
586                     MSIINSTALLCONTEXT_USERMANAGED,
587                     MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
588                     INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
589         else
590             MsiSourceListSetInfoW(package->ProductCode, NULL,
591                     MSIINSTALLCONTEXT_USERMANAGED,
592                     MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
593                     INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
594     }
595     set_file_source(package, file, comp, mi->last_path);
596
597     MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
598             MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, volume,
599             prompt);
600
601     msiobj_release(&row->hdr);
602
603     return rc;
604 }
605
606 static UINT get_file_target(MSIPACKAGE *package, LPCWSTR file_key, 
607                                    LPWSTR* file_source)
608 {
609     MSIFILE *file;
610
611     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
612     {
613         if (lstrcmpW( file_key, file->File )==0)
614         {
615             if (file->state >= msifs_overwrite)
616             {
617                 *file_source = strdupW( file->TargetPath );
618                 return ERROR_SUCCESS;
619             }
620             else
621                 return ERROR_FILE_NOT_FOUND;
622         }
623     }
624
625     return ERROR_FUNCTION_FAILED;
626 }
627
628 /*
629  * ACTION_InstallFiles()
630  * 
631  * For efficiency, this is done in two passes:
632  * 1) Correct all the TargetPaths and determine what files are to be installed.
633  * 2) Extract Cabinets and copy files.
634  */
635 UINT ACTION_InstallFiles(MSIPACKAGE *package)
636 {
637     struct media_info *mi;
638     UINT rc = ERROR_SUCCESS;
639     LPWSTR ptr;
640     MSIFILE *file;
641
642     /* increment progress bar each time action data is sent */
643     ui_progress(package,1,1,0,0);
644
645     /* handle the keys for the SourceList */
646     ptr = strrchrW(package->PackagePath,'\\');
647     if (ptr)
648     {
649         ptr ++;
650         MsiSourceListSetInfoW(package->ProductCode, NULL,
651                 MSIINSTALLCONTEXT_USERMANAGED,
652                 MSICODE_PRODUCT,
653                 INSTALLPROPERTY_PACKAGENAMEW, ptr);
654     }
655     /* FIXME("Write DiskPrompt\n"); */
656     
657     /* Pass 1 */
658     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
659     {
660         if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL ))
661         {
662             ui_progress(package,2,file->FileSize,0,0);
663             TRACE("File %s is not scheduled for install\n",
664                    debugstr_w(file->File));
665
666             file->state = msifs_skipped;
667         }
668     }
669
670     /*
671      * Despite MSDN specifying that the CreateFolders action
672      * should be called before InstallFiles, some installers don't
673      * do that, and they seem to work correctly.  We need to create
674      * directories here to make sure that the files can be copied.
675      */
676     msi_create_component_directories( package );
677
678     mi = create_media_info();
679
680     /* Pass 2 */
681     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
682     {
683         if (file->state != msifs_missing && file->state != msifs_overwrite)
684             continue;
685
686         TRACE("Pass 2: %s\n",debugstr_w(file->File));
687
688         rc = ready_media_for_file( package, mi, file );
689         if (rc != ERROR_SUCCESS)
690         {
691             ERR("Unable to ready media\n");
692             rc = ERROR_FUNCTION_FAILED;
693             break;
694         }
695
696         TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
697               debugstr_w(file->TargetPath));
698
699         if (file->state != msifs_missing && file->state != msifs_overwrite)
700             continue;
701
702         /* compressed files are extracted in ready_media_for_file */
703         if (~file->Attributes & msidbFileAttributesNoncompressed)
704         {
705             if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(file->TargetPath))
706                 ERR("compressed file wasn't extracted (%s)\n",
707                     debugstr_w(file->TargetPath));
708             continue;
709         }
710
711         rc = CopyFileW(file->SourcePath,file->TargetPath,FALSE);
712         if (!rc)
713         {
714             rc = GetLastError();
715             ERR("Unable to copy file (%s -> %s) (error %d)\n",
716                 debugstr_w(file->SourcePath), debugstr_w(file->TargetPath), rc);
717             if (rc == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite)
718             {
719                 rc = 0;
720             }
721             else if (rc == ERROR_FILE_NOT_FOUND)
722             {
723                 ERR("Source File Not Found!  Continuing\n");
724                 rc = 0;
725             }
726             else if (file->Attributes & msidbFileAttributesVital)
727             {
728                 ERR("Ignoring Error and continuing (nonvital file)...\n");
729                 rc = 0;
730             }
731         }
732         else
733         {
734             file->state = msifs_installed;
735             rc = ERROR_SUCCESS;
736         }
737     }
738
739     /* cleanup */
740     free_media_info( mi );
741     return rc;
742 }
743
744 static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param)
745 {
746     MSIPACKAGE *package = (MSIPACKAGE*)param;
747     WCHAR *file_source = NULL;
748     WCHAR dest_name[0x100];
749     LPWSTR dest_path, dest;
750     LPCWSTR file_key, component;
751     DWORD sz;
752     DWORD rc;
753     MSICOMPONENT *comp;
754
755     component = MSI_RecordGetString(row,2);
756     comp = get_loaded_component(package,component);
757
758     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
759     {
760         TRACE("Skipping copy due to disabled component %s\n",
761                         debugstr_w(component));
762
763         /* the action taken was the same as the current install state */        
764         comp->Action = comp->Installed;
765
766         return ERROR_SUCCESS;
767     }
768
769     comp->Action = INSTALLSTATE_LOCAL;
770
771     file_key = MSI_RecordGetString(row,3);
772     if (!file_key)
773     {
774         ERR("Unable to get file key\n");
775         return ERROR_FUNCTION_FAILED;
776     }
777
778     rc = get_file_target(package,file_key,&file_source);
779
780     if (rc != ERROR_SUCCESS)
781     {
782         ERR("Original file unknown %s\n",debugstr_w(file_key));
783         msi_free(file_source);
784         return ERROR_SUCCESS;
785     }
786
787     if (MSI_RecordIsNull(row,4))
788         strcpyW(dest_name,strrchrW(file_source,'\\')+1);
789     else
790     {
791         sz=0x100;
792         MSI_RecordGetStringW(row,4,dest_name,&sz);
793         reduce_to_longfilename(dest_name);
794     }
795
796     if (MSI_RecordIsNull(row,5))
797     {
798         LPWSTR p;
799         dest_path = strdupW(file_source);
800         p = strrchrW(dest_path,'\\');
801         if (p)
802             *p=0;
803     }
804     else
805     {
806         LPCWSTR destkey;
807         destkey = MSI_RecordGetString(row,5);
808         dest_path = resolve_folder(package, destkey, FALSE,FALSE,NULL);
809         if (!dest_path)
810         {
811             /* try a Property */
812             dest_path = msi_dup_property( package, destkey );
813             if (!dest_path)
814             {
815                 FIXME("Unable to get destination folder, try AppSearch properties\n");
816                 msi_free(file_source);
817                 return ERROR_SUCCESS;
818             }
819         }
820     }
821
822     dest = build_directory_name(2, dest_path, dest_name);
823
824     TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
825                     debugstr_w(dest)); 
826
827     if (strcmpW(file_source,dest))
828         rc = !CopyFileW(file_source,dest,TRUE);
829     else
830         rc = ERROR_SUCCESS;
831
832     if (rc != ERROR_SUCCESS)
833         ERR("Failed to copy file %s -> %s, last error %ld\n",
834             debugstr_w(file_source), debugstr_w(dest_path), GetLastError());
835
836     FIXME("We should track these duplicate files as well\n");   
837
838     msi_free(dest_path);
839     msi_free(dest);
840     msi_free(file_source);
841
842     return ERROR_SUCCESS;
843 }
844
845 UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
846 {
847     UINT rc;
848     MSIQUERY * view;
849     static const WCHAR ExecSeqQuery[] =
850         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
851          '`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0};
852
853     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
854     if (rc != ERROR_SUCCESS)
855         return ERROR_SUCCESS;
856
857     rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package);
858     msiobj_release(&view->hdr);
859
860     return rc;
861 }
862
863 UINT ACTION_RemoveFiles( MSIPACKAGE *package )
864 {
865     MSIFILE *file;
866
867     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
868     {
869         if ( !file->Component )
870             continue;
871         if ( file->Component->Installed == INSTALLSTATE_LOCAL )
872             continue;
873
874         if ( file->state == msifs_installed )
875             ERR("removing installed file %s\n", debugstr_w(file->TargetPath));
876
877         if ( file->state != msifs_present )
878             continue;
879
880         TRACE("removing %s\n", debugstr_w(file->File) );
881         if ( !DeleteFileW( file->TargetPath ) )
882             ERR("failed to delete %s\n",  debugstr_w(file->TargetPath) );
883         file->state = msifs_missing;
884     }
885
886     return ERROR_SUCCESS;
887 }