comctl32: A couple fixes for tab icon offsets.
[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, const WCHAR *action )
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, action, 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, szInstallFiles );
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, path;
332         p = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
333         path = build_directory_name(2, p, file->ShortName);
334         if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW( path ))
335         {
336             msi_free(path);
337             path = build_directory_name(2, p, file->LongName);
338         }
339         file->SourcePath = path;
340         msi_free(p);
341     }
342     else
343         file->SourcePath = build_directory_name(2, path, file->File);
344 }
345
346 static BOOL check_volume(LPCWSTR path, LPCWSTR want_volume, LPWSTR volume, 
347         UINT *intype)
348 {
349     WCHAR drive[4];
350     WCHAR name[MAX_PATH];
351     UINT type;
352
353     if (!(path[0] && path[1] == ':'))
354     {
355         if (intype)
356             *intype = DRIVE_NO_ROOT_DIR;
357         return TRUE;
358     }
359
360     drive[0] = path[0];
361     drive[1] = path[1];
362     drive[2] = '\\';
363     drive[3] = 0;
364     TRACE("Checking volume %s .. (%s)\n",debugstr_w(drive), debugstr_w(want_volume));
365     type = GetDriveTypeW(drive);
366     TRACE("drive is of type %x\n",type);
367
368     if (intype)
369         *intype=type;
370
371     if (type == DRIVE_UNKNOWN || type == DRIVE_NO_ROOT_DIR || 
372             type == DRIVE_FIXED || type == DRIVE_RAMDISK)
373         return TRUE;
374
375     GetVolumeInformationW(drive, name, MAX_PATH, NULL, NULL, NULL, NULL, 0);
376     TRACE("Drive contains %s\n", debugstr_w(name));
377     return (strcmpiW(want_volume,name)==0);
378 }
379
380 static BOOL check_for_sourcefile(LPCWSTR source)
381 {
382     DWORD attrib = GetFileAttributesW(source);
383     return (!(attrib == INVALID_FILE_ATTRIBUTES));
384 }
385
386 static UINT ready_volume(MSIPACKAGE* package, LPCWSTR path, LPWSTR last_volume, 
387                          MSIRECORD *row,UINT *type )
388 {
389     LPWSTR volume = NULL; 
390     LPCWSTR want_volume = MSI_RecordGetString(row, 5);
391     BOOL ok = check_volume(path, want_volume, volume, type);
392
393     TRACE("Readying Volume for %s (%s, %s)\n", debugstr_w(path),
394           debugstr_w(want_volume), debugstr_w(last_volume));
395
396     if (check_for_sourcefile(path) && !ok)
397     {
398         FIXME("Found the Sourcefile but not on the correct volume.(%s,%s,%s)\n",
399                 debugstr_w(path),debugstr_w(want_volume), debugstr_w(volume));
400         return ERROR_SUCCESS;
401     }
402
403     while (!ok)
404     {
405         INT rc;
406         LPCWSTR prompt;
407         LPWSTR msg;
408       
409         prompt = MSI_RecordGetString(row,3);
410         msg = generate_error_string(package, 1302, 1, prompt);
411         rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
412         msi_free(volume);
413         msi_free(msg);
414         if (rc == IDOK)
415             ok = check_for_sourcefile(path);
416         else
417             return ERROR_INSTALL_USEREXIT;
418     }
419
420     msi_free(last_volume);
421     last_volume = strdupW(volume);
422     return ERROR_SUCCESS;
423 }
424
425 struct media_info {
426     UINT last_sequence; 
427     LPWSTR last_volume;
428     LPWSTR last_path;
429     DWORD count;
430     WCHAR source[MAX_PATH];
431 };
432
433 static struct media_info *create_media_info( void )
434 {
435     struct media_info *mi;
436
437     mi = msi_alloc( sizeof *mi  );
438     if (mi)
439     {
440         mi->last_sequence = 0; 
441         mi->last_volume = NULL;
442         mi->last_path = NULL;
443         mi->count = 0;
444         mi->source[0] = 0;
445     }
446
447     return mi;
448 }
449
450 static void free_media_info( struct media_info *mi )
451 {
452     msi_free( mi->last_path );
453     msi_free( mi );
454 }
455
456 static UINT ready_media_for_file( MSIPACKAGE *package, struct media_info *mi,
457                                   MSIFILE *file )
458 {
459     UINT rc = ERROR_SUCCESS;
460     MSIRECORD * row = 0;
461     static const WCHAR ExecSeqQuery[] =
462         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
463          '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
464          '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',
465          ' ','%', 'i',' ','O','R','D','E','R',' ','B','Y',' ',
466          '`','L','a','s','t','S','e','q','u','e','n','c','e','`',0};
467     LPCWSTR cab, volume;
468     DWORD sz;
469     INT seq;
470     UINT type;
471     LPCWSTR prompt;
472     MSICOMPONENT *comp = file->Component;
473
474     if (file->Sequence <= mi->last_sequence)
475     {
476         set_file_source(package,file,comp,mi->last_path);
477         TRACE("Media already ready (%u, %u)\n",file->Sequence,mi->last_sequence);
478         return ERROR_SUCCESS;
479     }
480
481     mi->count ++;
482     row = MSI_QueryGetRecord(package->db, ExecSeqQuery, file->Sequence);
483     if (!row)
484     {
485         TRACE("Unable to query row\n");
486         return ERROR_FUNCTION_FAILED;
487     }
488
489     seq = MSI_RecordGetInteger(row,2);
490     mi->last_sequence = seq;
491
492     volume = MSI_RecordGetString(row, 5);
493     prompt = MSI_RecordGetString(row, 3);
494
495     msi_free(mi->last_path);
496     mi->last_path = NULL;
497
498     if (file->Attributes & msidbFileAttributesNoncompressed)
499     {
500         mi->last_path = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
501         set_file_source(package,file,comp,mi->last_path);
502         rc = ready_volume(package, file->SourcePath, mi->last_volume, row,&type);
503
504         MsiSourceListAddMediaDiskW(package->ProductCode, NULL, 
505             MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, volume,
506             prompt);
507
508         if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || 
509                 type == DRIVE_RAMDISK)
510             MsiSourceListSetInfoW(package->ProductCode, NULL, 
511                 MSIINSTALLCONTEXT_USERMANAGED, 
512                 MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
513                 INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
514         else
515             MsiSourceListSetInfoW(package->ProductCode, NULL, 
516                 MSIINSTALLCONTEXT_USERMANAGED, 
517                 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
518                 INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
519         msiobj_release(&row->hdr);
520         return rc;
521     }
522
523     cab = MSI_RecordGetString(row,4);
524     if (cab)
525     {
526         TRACE("Source is CAB %s\n",debugstr_w(cab));
527         /* the stream does not contain the # character */
528         if (cab[0]=='#')
529         {
530             LPWSTR path;
531
532             writeout_cabinet_stream(package,&cab[1],mi->source);
533             mi->last_path = strdupW(mi->source);
534             *(strrchrW(mi->last_path,'\\')+1)=0;
535
536             path = msi_dup_property( package, cszSourceDir );
537
538             MsiSourceListAddMediaDiskW(package->ProductCode, NULL, 
539                 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count,
540                 volume, prompt);
541
542             MsiSourceListSetInfoW(package->ProductCode, NULL,
543                 MSIINSTALLCONTEXT_USERMANAGED,
544                 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
545                 INSTALLPROPERTY_LASTUSEDSOURCEW, path);
546
547             msi_free(path);
548         }
549         else
550         {
551             sz = MAX_PATH;
552             mi->last_path = msi_alloc(MAX_PATH*sizeof(WCHAR));
553             if (MSI_GetPropertyW(package, cszSourceDir, mi->source, &sz))
554             {
555                 ERR("No Source dir defined\n");
556                 rc = ERROR_FUNCTION_FAILED;
557             }
558             else
559             {
560                 strcpyW(mi->last_path,mi->source);
561                 strcatW(mi->source,cab);
562
563                 rc = ready_volume(package, mi->source, mi->last_volume, row, &type);
564                 if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || 
565                         type == DRIVE_RAMDISK)
566                     MsiSourceListSetInfoW(package->ProductCode, NULL,
567                             MSIINSTALLCONTEXT_USERMANAGED,
568                             MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
569                             INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
570                 else
571                     MsiSourceListSetInfoW(package->ProductCode, NULL,
572                             MSIINSTALLCONTEXT_USERMANAGED,
573                             MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
574                             INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
575
576                 /* extract the cab file into a folder in the temp folder */
577                 sz = MAX_PATH;
578                 if (MSI_GetPropertyW(package, cszTempFolder,mi->last_path, &sz) 
579                                     != ERROR_SUCCESS)
580                     GetTempPathW(MAX_PATH,mi->last_path);
581             }
582         }
583         rc = !extract_cabinet_file(package, mi->source, mi->last_path);
584     }
585     else
586     {
587         sz = MAX_PATH;
588         mi->last_path = msi_alloc(MAX_PATH*sizeof(WCHAR));
589         MSI_GetPropertyW(package,cszSourceDir,mi->source,&sz);
590         strcpyW(mi->last_path,mi->source);
591         rc = ready_volume(package, mi->last_path, mi->last_volume, row, &type);
592
593         if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || 
594                 type == DRIVE_RAMDISK)
595             MsiSourceListSetInfoW(package->ProductCode, NULL,
596                     MSIINSTALLCONTEXT_USERMANAGED,
597                     MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
598                     INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
599         else
600             MsiSourceListSetInfoW(package->ProductCode, NULL,
601                     MSIINSTALLCONTEXT_USERMANAGED,
602                     MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
603                     INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
604     }
605     set_file_source(package, file, comp, mi->last_path);
606
607     MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
608             MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, volume,
609             prompt);
610
611     msiobj_release(&row->hdr);
612
613     return rc;
614 }
615
616 static UINT get_file_target(MSIPACKAGE *package, LPCWSTR file_key, 
617                             MSIFILE** file)
618 {
619     LIST_FOR_EACH_ENTRY( *file, &package->files, MSIFILE, entry )
620     {
621         if (lstrcmpW( file_key, (*file)->File )==0)
622         {
623             if ((*file)->state >= msifs_overwrite)
624                 return ERROR_SUCCESS;
625             else
626                 return ERROR_FILE_NOT_FOUND;
627         }
628     }
629
630     return ERROR_FUNCTION_FAILED;
631 }
632
633 /*
634  * ACTION_InstallFiles()
635  * 
636  * For efficiency, this is done in two passes:
637  * 1) Correct all the TargetPaths and determine what files are to be installed.
638  * 2) Extract Cabinets and copy files.
639  */
640 UINT ACTION_InstallFiles(MSIPACKAGE *package)
641 {
642     struct media_info *mi;
643     UINT rc = ERROR_SUCCESS;
644     LPWSTR ptr;
645     MSIFILE *file;
646
647     /* increment progress bar each time action data is sent */
648     ui_progress(package,1,1,0,0);
649
650     /* handle the keys for the SourceList */
651     ptr = strrchrW(package->PackagePath,'\\');
652     if (ptr)
653     {
654         ptr ++;
655         MsiSourceListSetInfoW(package->ProductCode, NULL,
656                 MSIINSTALLCONTEXT_USERMANAGED,
657                 MSICODE_PRODUCT,
658                 INSTALLPROPERTY_PACKAGENAMEW, ptr);
659     }
660     /* FIXME("Write DiskPrompt\n"); */
661     
662     /* Pass 1 */
663     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
664     {
665         if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL ))
666         {
667             ui_progress(package,2,file->FileSize,0,0);
668             TRACE("File %s is not scheduled for install\n",
669                    debugstr_w(file->File));
670
671             file->state = msifs_skipped;
672         }
673     }
674
675     /*
676      * Despite MSDN specifying that the CreateFolders action
677      * should be called before InstallFiles, some installers don't
678      * do that, and they seem to work correctly.  We need to create
679      * directories here to make sure that the files can be copied.
680      */
681     msi_create_component_directories( package );
682
683     mi = create_media_info();
684
685     /* Pass 2 */
686     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
687     {
688         if (file->state != msifs_missing && file->state != msifs_overwrite)
689             continue;
690
691         TRACE("Pass 2: %s\n",debugstr_w(file->File));
692
693         rc = ready_media_for_file( package, mi, file );
694         if (rc != ERROR_SUCCESS)
695         {
696             ERR("Unable to ready media\n");
697             rc = ERROR_FUNCTION_FAILED;
698             break;
699         }
700
701         TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
702               debugstr_w(file->TargetPath));
703
704         if (file->state != msifs_missing && file->state != msifs_overwrite)
705             continue;
706
707         /* compressed files are extracted in ready_media_for_file */
708         if (~file->Attributes & msidbFileAttributesNoncompressed)
709         {
710             if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(file->TargetPath))
711                 ERR("compressed file wasn't extracted (%s)\n",
712                     debugstr_w(file->TargetPath));
713             continue;
714         }
715
716         rc = CopyFileW(file->SourcePath,file->TargetPath,FALSE);
717         if (!rc)
718         {
719             rc = GetLastError();
720             ERR("Unable to copy file (%s -> %s) (error %d)\n",
721                 debugstr_w(file->SourcePath), debugstr_w(file->TargetPath), rc);
722             if (rc == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite)
723             {
724                 rc = 0;
725             }
726             else if (rc == ERROR_FILE_NOT_FOUND)
727             {
728                 ERR("Source File Not Found!  Continuing\n");
729                 rc = 0;
730             }
731             else if (file->Attributes & msidbFileAttributesVital)
732             {
733                 ERR("Ignoring Error and continuing (nonvital file)...\n");
734                 rc = 0;
735             }
736         }
737         else
738         {
739             file->state = msifs_installed;
740             rc = ERROR_SUCCESS;
741         }
742     }
743
744     /* cleanup */
745     free_media_info( mi );
746     return rc;
747 }
748
749 static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param)
750 {
751     MSIPACKAGE *package = (MSIPACKAGE*)param;
752     WCHAR dest_name[0x100];
753     LPWSTR dest_path, dest;
754     LPCWSTR file_key, component;
755     DWORD sz;
756     DWORD rc;
757     MSICOMPONENT *comp;
758     MSIFILE *file;
759
760     component = MSI_RecordGetString(row,2);
761     comp = get_loaded_component(package,component);
762
763     if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
764     {
765         TRACE("Skipping copy due to disabled component %s\n",
766                         debugstr_w(component));
767
768         /* the action taken was the same as the current install state */        
769         comp->Action = comp->Installed;
770
771         return ERROR_SUCCESS;
772     }
773
774     comp->Action = INSTALLSTATE_LOCAL;
775
776     file_key = MSI_RecordGetString(row,3);
777     if (!file_key)
778     {
779         ERR("Unable to get file key\n");
780         return ERROR_FUNCTION_FAILED;
781     }
782
783     rc = get_file_target(package,file_key,&file);
784
785     if (rc != ERROR_SUCCESS)
786     {
787         ERR("Original file unknown %s\n",debugstr_w(file_key));
788         return ERROR_SUCCESS;
789     }
790
791     if (MSI_RecordIsNull(row,4))
792         strcpyW(dest_name,strrchrW(file->TargetPath,'\\')+1);
793     else
794     {
795         sz=0x100;
796         MSI_RecordGetStringW(row,4,dest_name,&sz);
797         reduce_to_longfilename(dest_name);
798     }
799
800     if (MSI_RecordIsNull(row,5))
801     {
802         LPWSTR p;
803         dest_path = strdupW(file->TargetPath);
804         p = strrchrW(dest_path,'\\');
805         if (p)
806             *p=0;
807     }
808     else
809     {
810         LPCWSTR destkey;
811         destkey = MSI_RecordGetString(row,5);
812         dest_path = resolve_folder(package, destkey, FALSE,FALSE,NULL);
813         if (!dest_path)
814         {
815             /* try a Property */
816             dest_path = msi_dup_property( package, destkey );
817             if (!dest_path)
818             {
819                 FIXME("Unable to get destination folder, try AppSearch properties\n");
820                 return ERROR_SUCCESS;
821             }
822         }
823     }
824
825     dest = build_directory_name(2, dest_path, dest_name);
826
827     TRACE("Duplicating file %s to %s\n",debugstr_w(file->TargetPath),
828                     debugstr_w(dest)); 
829
830     if (strcmpW(file->TargetPath,dest))
831         rc = !CopyFileW(file->TargetPath,dest,TRUE);
832     else
833         rc = ERROR_SUCCESS;
834
835     if (rc != ERROR_SUCCESS)
836         ERR("Failed to copy file %s -> %s, last error %ld\n",
837             debugstr_w(file->TargetPath), debugstr_w(dest_path), GetLastError());
838
839     FIXME("We should track these duplicate files as well\n");   
840
841     msi_free(dest_path);
842     msi_free(dest);
843
844     msi_file_update_ui(package, file, szDuplicateFiles);
845
846     return ERROR_SUCCESS;
847 }
848
849 UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
850 {
851     UINT rc;
852     MSIQUERY * view;
853     static const WCHAR ExecSeqQuery[] =
854         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
855          '`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0};
856
857     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
858     if (rc != ERROR_SUCCESS)
859         return ERROR_SUCCESS;
860
861     rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package);
862     msiobj_release(&view->hdr);
863
864     return rc;
865 }
866
867 UINT ACTION_RemoveFiles( MSIPACKAGE *package )
868 {
869     MSIFILE *file;
870
871     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
872     {
873         MSIRECORD *uirow;
874         LPWSTR uipath, p;
875
876         if ( !file->Component )
877             continue;
878         if ( file->Component->Installed == INSTALLSTATE_LOCAL )
879             continue;
880
881         if ( file->state == msifs_installed )
882             ERR("removing installed file %s\n", debugstr_w(file->TargetPath));
883
884         if ( file->state != msifs_present )
885             continue;
886
887         TRACE("removing %s\n", debugstr_w(file->File) );
888         if ( !DeleteFileW( file->TargetPath ) )
889             ERR("failed to delete %s\n",  debugstr_w(file->TargetPath) );
890         file->state = msifs_missing;
891
892         /* the UI chunk */
893         uirow = MSI_CreateRecord( 9 );
894         MSI_RecordSetStringW( uirow, 1, file->FileName );
895         uipath = strdupW( file->TargetPath );
896         p = strrchrW(uipath,'\\');
897         if (p)
898             p[1]=0;
899         MSI_RecordSetStringW( uirow, 9, uipath);
900         ui_actiondata( package, szRemoveFiles, uirow);
901         msiobj_release( &uirow->hdr );
902         msi_free( uipath );
903         /* FIXME: call ui_progress here? */
904     }
905
906     return ERROR_SUCCESS;
907 }