ddraw: Protect the ddraw list.
[wine] / dlls / advpack / files.c
1 /*
2  * Advpack file functions
3  *
4  * Copyright 2006 James Hawkins
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <stdlib.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "winreg.h"
28 #include "winver.h"
29 #include "winternl.h"
30 #include "setupapi.h"
31 #include "advpub.h"
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34 #include "advpack_private.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(advpack);
37
38 /* converts an ansi double null-terminated list to a unicode list */
39 static LPWSTR ansi_to_unicode_list(LPCSTR ansi_list)
40 {
41     DWORD len, wlen = 0;
42     LPWSTR list;
43     LPCSTR ptr = ansi_list;
44
45     while (*ptr) ptr += lstrlenA(ptr) + 1;
46     len = ptr + 1 - ansi_list;
47     wlen = MultiByteToWideChar(CP_ACP, 0, ansi_list, len, NULL, 0);
48     list = HeapAlloc(GetProcessHeap(), 0, wlen * sizeof(WCHAR));
49     MultiByteToWideChar(CP_ACP, 0, ansi_list, len, list, wlen);
50     return list;
51 }
52
53 /***********************************************************************
54  *      AddDelBackupEntryA (ADVPACK.@)
55  *
56  * See AddDelBackupEntryW.
57  */
58 HRESULT WINAPI AddDelBackupEntryA(LPCSTR lpcszFileList, LPCSTR lpcszBackupDir,
59                                   LPCSTR lpcszBaseName, DWORD dwFlags)
60 {
61     UNICODE_STRING backupdir, basename;
62     LPWSTR filelist;
63     LPCWSTR backup;
64     HRESULT res;
65
66     TRACE("(%s, %s, %s, %d)\n", debugstr_a(lpcszFileList),
67           debugstr_a(lpcszBackupDir), debugstr_a(lpcszBaseName), dwFlags);
68
69     if (lpcszFileList)
70         filelist = ansi_to_unicode_list(lpcszFileList);
71     else
72         filelist = NULL;
73
74     RtlCreateUnicodeStringFromAsciiz(&backupdir, lpcszBackupDir);
75     RtlCreateUnicodeStringFromAsciiz(&basename, lpcszBaseName);
76
77     if (lpcszBackupDir)
78         backup = backupdir.Buffer;
79     else
80         backup = NULL;
81
82     res = AddDelBackupEntryW(filelist, backup, basename.Buffer, dwFlags);
83
84     HeapFree(GetProcessHeap(), 0, filelist);
85
86     RtlFreeUnicodeString(&backupdir);
87     RtlFreeUnicodeString(&basename);
88
89     return res;
90 }
91
92 /***********************************************************************
93  *      AddDelBackupEntryW (ADVPACK.@)
94  *
95  * Either appends the files in the file list to the backup section of
96  * the specified INI, or deletes the entries from the INI file.
97  *
98  * PARAMS
99  *   lpcszFileList  [I] NULL-separated list of filenames.
100  *   lpcszBackupDir [I] Path of the backup directory.
101  *   lpcszBaseName  [I] Basename of the INI file.
102  *   dwFlags        [I] AADBE_ADD_ENTRY adds the entries in the file list
103  *                      to the INI file, while AADBE_DEL_ENTRY removes
104  *                      the entries from the INI file.
105  *
106  * RETURNS
107  *   S_OK in all cases.
108  *
109  * NOTES
110  *   If the INI file does not exist before adding entries to it, the file
111  *   will be created.
112  * 
113  *   If lpcszBackupDir is NULL, the INI file is assumed to exist in
114  *   c:\windows or created there if it does not exist.
115  */
116 HRESULT WINAPI AddDelBackupEntryW(LPCWSTR lpcszFileList, LPCWSTR lpcszBackupDir,
117                                   LPCWSTR lpcszBaseName, DWORD dwFlags)
118 {
119     WCHAR szIniPath[MAX_PATH];
120     LPCWSTR szString = NULL;
121
122     static const WCHAR szBackupEntry[] = {
123         '-','1',',','0',',','0',',','0',',','0',',','0',',','-','1',0
124     };
125     
126     static const WCHAR backslash[] = {'\\',0};
127     static const WCHAR ini[] = {'.','i','n','i',0};
128     static const WCHAR backup[] = {'b','a','c','k','u','p',0};
129
130     TRACE("(%s, %s, %s, %d)\n", debugstr_w(lpcszFileList),
131           debugstr_w(lpcszBackupDir), debugstr_w(lpcszBaseName), dwFlags);
132
133     if (!lpcszFileList || !*lpcszFileList)
134         return S_OK;
135
136     if (lpcszBackupDir)
137         lstrcpyW(szIniPath, lpcszBackupDir);
138     else
139         GetWindowsDirectoryW(szIniPath, MAX_PATH);
140
141     lstrcatW(szIniPath, backslash);
142     lstrcatW(szIniPath, lpcszBaseName);
143     lstrcatW(szIniPath, ini);
144
145     SetFileAttributesW(szIniPath, FILE_ATTRIBUTE_NORMAL);
146
147     if (dwFlags & AADBE_ADD_ENTRY)
148         szString = szBackupEntry;
149     else if (dwFlags & AADBE_DEL_ENTRY)
150         szString = NULL;
151
152     /* add or delete the INI entries */
153     while (*lpcszFileList)
154     {
155         WritePrivateProfileStringW(backup, lpcszFileList, szString, szIniPath);
156         lpcszFileList += lstrlenW(lpcszFileList) + 1;
157     }
158
159     /* hide the INI file */
160     SetFileAttributesW(szIniPath, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
161
162     return S_OK;
163 }
164
165 /* FIXME: this is only for the local case, X:\ */
166 #define ROOT_LENGTH 3
167
168 static UINT CALLBACK pQuietQueueCallback(PVOID Context, UINT Notification,
169                                          UINT_PTR Param1, UINT_PTR Param2)
170 {
171     return 1;
172 }
173
174 static UINT CALLBACK pQueueCallback(PVOID Context, UINT Notification,
175                                     UINT_PTR Param1, UINT_PTR Param2)
176 {
177     /* only be verbose for error notifications */
178     if (!Notification ||
179         Notification == SPFILENOTIFY_RENAMEERROR ||
180         Notification == SPFILENOTIFY_DELETEERROR ||
181         Notification == SPFILENOTIFY_COPYERROR)
182     {
183         return SetupDefaultQueueCallbackW(Context, Notification,
184                                           Param1, Param2);
185     }
186
187     return 1;
188 }
189
190 /***********************************************************************
191  *      AdvInstallFileA (ADVPACK.@)
192  *
193  * See AdvInstallFileW.
194  */
195 HRESULT WINAPI AdvInstallFileA(HWND hwnd, LPCSTR lpszSourceDir, LPCSTR lpszSourceFile,
196                                LPCSTR lpszDestDir, LPCSTR lpszDestFile,
197                                DWORD dwFlags, DWORD dwReserved)
198 {
199     UNICODE_STRING sourcedir, sourcefile;
200     UNICODE_STRING destdir, destfile;
201     HRESULT res;
202
203     TRACE("(%p, %s, %s, %s, %s, %d, %d)\n", hwnd, debugstr_a(lpszSourceDir),
204           debugstr_a(lpszSourceFile), debugstr_a(lpszDestDir),
205           debugstr_a(lpszDestFile), dwFlags, dwReserved);
206
207     if (!lpszSourceDir || !lpszSourceFile || !lpszDestDir)
208         return E_INVALIDARG;
209
210     RtlCreateUnicodeStringFromAsciiz(&sourcedir, lpszSourceDir);
211     RtlCreateUnicodeStringFromAsciiz(&sourcefile, lpszSourceFile);
212     RtlCreateUnicodeStringFromAsciiz(&destdir, lpszDestDir);
213     RtlCreateUnicodeStringFromAsciiz(&destfile, lpszDestFile);
214
215     res = AdvInstallFileW(hwnd, sourcedir.Buffer, sourcefile.Buffer,
216                           destdir.Buffer, destfile.Buffer, dwFlags, dwReserved);
217
218     RtlFreeUnicodeString(&sourcedir);
219     RtlFreeUnicodeString(&sourcefile);
220     RtlFreeUnicodeString(&destdir);
221     RtlFreeUnicodeString(&destfile);
222
223     return res;
224 }
225
226 /***********************************************************************
227  *      AdvInstallFileW (ADVPACK.@)
228  *
229  * Copies a file from the source to a destination.
230  *
231  * PARAMS
232  *   hwnd           [I] Handle to the window used for messages.
233  *   lpszSourceDir  [I] Source directory.
234  *   lpszSourceFile [I] Source filename.
235  *   lpszDestDir    [I] Destination directory.
236  *   lpszDestFile   [I] Optional destination filename.
237  *   dwFlags        [I] See advpub.h.
238  *   dwReserved     [I] Reserved.  Must be 0.
239  *
240  * RETURNS
241  *   Success: S_OK.
242  *   Failure: E_FAIL.
243  *
244  * NOTES
245  *   If lpszDestFile is NULL, the destination filename is the same as
246  *   lpszSourceFIle.
247  */
248 HRESULT WINAPI AdvInstallFileW(HWND hwnd, LPCWSTR lpszSourceDir, LPCWSTR lpszSourceFile,
249                                LPCWSTR lpszDestDir, LPCWSTR lpszDestFile,
250                                DWORD dwFlags, DWORD dwReserved)
251 {
252     PSP_FILE_CALLBACK_W pFileCallback;
253     LPWSTR szDestFilename;
254     LPCWSTR szPath;
255     WCHAR szRootPath[ROOT_LENGTH];
256     DWORD dwLen, dwLastError;
257     HSPFILEQ fileQueue;
258     PVOID pContext;
259
260     TRACE("(%p, %s, %s, %s, %s, %d, %d)\n", hwnd, debugstr_w(lpszSourceDir),
261           debugstr_w(lpszSourceFile), debugstr_w(lpszDestDir),
262           debugstr_w(lpszDestFile), dwFlags, dwReserved);
263
264     if (!lpszSourceDir || !lpszSourceFile || !lpszDestDir)
265         return E_INVALIDARG;
266         
267     fileQueue = SetupOpenFileQueue();
268     if (fileQueue == INVALID_HANDLE_VALUE)
269         return HRESULT_FROM_WIN32(GetLastError());
270
271     pContext = NULL;
272     dwLastError = ERROR_SUCCESS;
273
274     lstrcpynW(szRootPath, lpszSourceDir, ROOT_LENGTH);
275     szPath = lpszSourceDir + ROOT_LENGTH;
276
277     /* use lpszSourceFile as destination filename if lpszDestFile is NULL */
278     if (lpszDestFile)
279     {
280         dwLen = lstrlenW(lpszDestFile);
281         szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
282         lstrcpyW(szDestFilename, lpszDestFile);
283     }
284     else
285     {
286         dwLen = lstrlenW(lpszSourceFile);
287         szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
288         lstrcpyW(szDestFilename, lpszSourceFile);
289     }
290
291     /* add the file copy operation to the setup queue */
292     if (!SetupQueueCopyW(fileQueue, szRootPath, szPath, lpszSourceFile, NULL,
293                          NULL, lpszDestDir, szDestFilename, dwFlags))
294     {
295         dwLastError = GetLastError();
296         goto done;
297     }
298
299     pContext = SetupInitDefaultQueueCallbackEx(hwnd, INVALID_HANDLE_VALUE,
300                                                0, 0, NULL);
301     if (!pContext)
302     {
303         dwLastError = GetLastError();
304         goto done;
305     }
306
307     /* don't output anything for AIF_QUIET */
308     if (dwFlags & AIF_QUIET)
309         pFileCallback = pQuietQueueCallback;
310     else
311         pFileCallback = pQueueCallback;
312
313     /* perform the file copy */
314     if (!SetupCommitFileQueueW(hwnd, fileQueue, pFileCallback, pContext))
315     {
316         dwLastError = GetLastError();
317         goto done;
318     }
319
320 done:
321     SetupTermDefaultQueueCallback(pContext);
322     SetupCloseFileQueue(fileQueue);
323     
324     HeapFree(GetProcessHeap(), 0, szDestFilename);
325     
326     return HRESULT_FROM_WIN32(dwLastError);
327 }
328
329 static HRESULT DELNODE_recurse_dirtree(LPWSTR fname, DWORD flags)
330 {
331     DWORD fattrs = GetFileAttributesW(fname);
332     HRESULT ret = E_FAIL;
333
334     static const WCHAR backslash[] = {'\\',0};
335     static const WCHAR asterisk[] = {'*',0};
336     static const WCHAR dot[] = {'.',0};
337     static const WCHAR dotdot[] = {'.','.',0};
338
339     if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
340     {
341         HANDLE hFindFile;
342         WIN32_FIND_DATAW w32fd;
343         BOOL done = TRUE;
344         int fname_len = lstrlenW(fname);
345
346         /* Generate a path with wildcard suitable for iterating */
347         if (lstrcmpW(CharPrevW(fname, fname + fname_len), backslash))
348         {
349             lstrcpyW(fname + fname_len, backslash);
350             ++fname_len;
351         }
352         lstrcpyW(fname + fname_len, asterisk);
353
354         if ((hFindFile = FindFirstFileW(fname, &w32fd)) != INVALID_HANDLE_VALUE)
355         {
356             /* Iterate through the files in the directory */
357             for (done = FALSE; !done; done = !FindNextFileW(hFindFile, &w32fd))
358             {
359                 TRACE("%s\n", debugstr_w(w32fd.cFileName));
360                 if (lstrcmpW(dot, w32fd.cFileName) != 0 &&
361                     lstrcmpW(dotdot, w32fd.cFileName) != 0)
362                 {
363                     lstrcpyW(fname + fname_len, w32fd.cFileName);
364                     if (DELNODE_recurse_dirtree(fname, flags) != S_OK)
365                     {
366                         break; /* Failure */
367                     }
368                 }
369             }
370             FindClose(hFindFile);
371         }
372
373         /* We're done with this directory, so restore the old path without wildcard */
374         *(fname + fname_len) = '\0';
375
376         if (done)
377         {
378             TRACE("%s: directory\n", debugstr_w(fname));
379             if (SetFileAttributesW(fname, FILE_ATTRIBUTE_NORMAL) && RemoveDirectoryW(fname))
380             {
381                 ret = S_OK;
382             }
383         }
384     }
385     else
386     {
387         TRACE("%s: file\n", debugstr_w(fname));
388         if (SetFileAttributesW(fname, FILE_ATTRIBUTE_NORMAL) && DeleteFileW(fname))
389         {
390             ret = S_OK;
391         }
392     }
393     
394     return ret;
395 }
396
397 /***********************************************************************
398  *              DelNodeA   (ADVPACK.@)
399  *
400  * See DelNodeW.
401  */
402 HRESULT WINAPI DelNodeA(LPCSTR pszFileOrDirName, DWORD dwFlags)
403 {
404     UNICODE_STRING fileordirname;
405     HRESULT res;
406
407     TRACE("(%s, %d)\n", debugstr_a(pszFileOrDirName), dwFlags);
408
409     RtlCreateUnicodeStringFromAsciiz(&fileordirname, pszFileOrDirName);
410
411     res = DelNodeW(fileordirname.Buffer, dwFlags);
412
413     RtlFreeUnicodeString(&fileordirname);
414
415     return res;
416 }
417
418 /***********************************************************************
419  *              DelNodeW   (ADVPACK.@)
420  *
421  * Deletes a file or directory
422  *
423  * PARAMS
424  *   pszFileOrDirName   [I] Name of file or directory to delete
425  *   dwFlags            [I] Flags; see include/advpub.h
426  *
427  * RETURNS 
428  *   Success: S_OK
429  *   Failure: E_FAIL
430  *
431  * BUGS
432  *   - Ignores flags
433  *   - Native version apparently does a lot of checking to make sure
434  *     we're not trying to delete a system directory etc.
435  */
436 HRESULT WINAPI DelNodeW(LPCWSTR pszFileOrDirName, DWORD dwFlags)
437 {
438     WCHAR fname[MAX_PATH];
439     HRESULT ret = E_FAIL;
440     
441     TRACE("(%s, %d)\n", debugstr_w(pszFileOrDirName), dwFlags);
442     
443     if (dwFlags)
444         FIXME("Flags ignored!\n");
445
446     if (pszFileOrDirName && *pszFileOrDirName)
447     {
448         lstrcpyW(fname, pszFileOrDirName);
449
450         /* TODO: Should check for system directory deletion etc. here */
451
452         ret = DELNODE_recurse_dirtree(fname, dwFlags);
453     }
454
455     return ret;
456 }
457
458 /***********************************************************************
459  *             DelNodeRunDLL32A   (ADVPACK.@)
460  *
461  * See DelNodeRunDLL32W.
462  */
463 HRESULT WINAPI DelNodeRunDLL32A(HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show)
464 {
465     UNICODE_STRING params;
466     HRESULT hr;
467
468     TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_a(cmdline), show);
469
470     RtlCreateUnicodeStringFromAsciiz(&params, cmdline);
471
472     hr = DelNodeRunDLL32W(hWnd, hInst, params.Buffer, show);
473
474     RtlFreeUnicodeString(&params);
475
476     return hr;
477 }
478
479 /***********************************************************************
480  *             DelNodeRunDLL32W   (ADVPACK.@)
481  *
482  * Deletes a file or directory, WinMain style.
483  *
484  * PARAMS
485  *   hWnd    [I] Handle to the window used for the display.
486  *   hInst   [I] Instance of the process.
487  *   cmdline [I] Contains parameters in the order FileOrDirName,Flags.
488  *   show    [I] How the window should be shown.
489  *
490  * RETURNS
491  *   Success: S_OK.
492  *   Failure: E_FAIL.
493  */
494 HRESULT WINAPI DelNodeRunDLL32W(HWND hWnd, HINSTANCE hInst, LPWSTR cmdline, INT show)
495 {
496     LPWSTR szFilename, szFlags;
497     LPWSTR cmdline_copy, cmdline_ptr;
498     DWORD dwFlags = 0;
499     HRESULT res;
500
501     TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_w(cmdline), show);
502
503     cmdline_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
504     cmdline_ptr = cmdline_copy;
505     lstrcpyW(cmdline_copy, cmdline);
506
507     /* get the parameters at indexes 0 and 1 respectively */
508     szFilename = get_parameter(&cmdline_ptr, ',');
509     szFlags = get_parameter(&cmdline_ptr, ',');
510
511     if (szFlags)
512         dwFlags = atolW(szFlags);
513
514     res = DelNodeW(szFilename, dwFlags);
515
516     HeapFree(GetProcessHeap(), 0, cmdline_copy);
517
518     return res;
519 }
520
521 /* The following defintions were copied from dlls/cabinet/cabinet.h */
522
523 /* EXTRACTdest flags */
524 #define EXTRACT_FILLFILELIST  0x00000001
525 #define EXTRACT_EXTRACTFILES  0x00000002
526
527 struct ExtractFileList {
528         LPSTR  filename;
529         struct ExtractFileList *next;
530         BOOL   unknown;  /* always 1L */
531 } ;
532
533 /* the first parameter of the function Extract */
534 typedef struct {
535         long  result1;          /* 0x000 */
536         long  unknown1[3];      /* 0x004 */
537         struct ExtractFileList *filelist; /* 0x010 */
538         long  filecount;        /* 0x014 */
539         DWORD flags;            /* 0x018 */
540         char  directory[0x104]; /* 0x01c */
541         char  lastfile[0x20c];  /* 0x120 */
542 } EXTRACTdest;
543
544 static HRESULT (WINAPI *pExtract)(EXTRACTdest*, LPCSTR);
545
546 /* removes legal characters before and after file list, and
547  * converts the file list to a NULL-separated list
548  */
549 static LPSTR convert_file_list(LPCSTR FileList, DWORD *dwNumFiles)
550 {
551     DWORD dwLen;
552     const char *first = FileList;
553     const char *last = FileList + strlen(FileList) - 1;
554     LPSTR szConvertedList, temp;
555     
556     /* any number of these chars before the list is OK */
557     while (first < last && (*first == ' ' || *first == '\t' || *first == ':'))
558         first++;
559
560     /* any number of these chars after the list is OK */
561     while (last > first && (*last == ' ' || *last == '\t' || *last == ':'))
562         last--;
563
564     if (first == last)
565         return NULL;
566
567     dwLen = last - first + 3; /* room for double-null termination */
568     szConvertedList = HeapAlloc(GetProcessHeap(), 0, dwLen);
569     lstrcpynA(szConvertedList, first, dwLen - 1);
570
571     szConvertedList[dwLen - 1] = '\0';
572     szConvertedList[dwLen] = '\0';
573
574     /* empty list */
575     if (!lstrlenA(szConvertedList))
576         return NULL;
577         
578     *dwNumFiles = 1;
579
580     /* convert the colons to double-null termination */
581     temp = szConvertedList;
582     while (*temp)
583     {
584         if (*temp == ':')
585         {
586             *temp = '\0';
587             (*dwNumFiles)++;
588         }
589
590         temp++;
591     }
592
593     return szConvertedList;
594 }
595
596 static void free_file_node(struct ExtractFileList *pNode)
597 {
598     HeapFree(GetProcessHeap(), 0, pNode->filename);
599     HeapFree(GetProcessHeap(), 0, pNode);
600 }
601
602 /* determines whether szFile is in the NULL-separated szFileList */
603 static BOOL file_in_list(LPSTR szFile, LPSTR szFileList)
604 {
605     DWORD dwLen = lstrlenA(szFile);
606     DWORD dwTestLen;
607
608     while (*szFileList)
609     {
610         dwTestLen = lstrlenA(szFileList);
611
612         if (dwTestLen == dwLen)
613         {
614             if (!lstrcmpiA(szFile, szFileList))
615                 return TRUE;
616         }
617
618         szFileList += dwTestLen + 1;
619     }
620
621     return FALSE;
622 }
623
624 /* removes nodes from the linked list that aren't specified in szFileList
625  * returns the number of files that are in both the linked list and szFileList
626  */
627 static DWORD fill_file_list(EXTRACTdest *extractDest, LPCSTR szCabName, LPSTR szFileList)
628 {
629     DWORD dwNumFound = 0;
630     struct ExtractFileList *pNode;
631     struct ExtractFileList *prev = NULL;
632
633     extractDest->flags |= EXTRACT_FILLFILELIST;
634     if (pExtract(extractDest, szCabName))
635     {
636         extractDest->flags &= ~EXTRACT_FILLFILELIST;
637         return -1;
638     }
639
640     pNode = extractDest->filelist;
641     while (pNode)
642     {
643         if (file_in_list(pNode->filename, szFileList))
644         {
645             prev = pNode;
646             pNode = pNode->next;
647             dwNumFound++;
648         }
649         else if (prev)
650         {
651             prev->next = pNode->next;
652             free_file_node(pNode);
653             pNode = prev->next;
654         }
655         else
656         {
657             extractDest->filelist = pNode->next;
658             free_file_node(pNode);
659             pNode = extractDest->filelist;
660         }
661     }
662
663     extractDest->flags &= ~EXTRACT_FILLFILELIST;
664     return dwNumFound;
665 }
666
667 /***********************************************************************
668  *             ExtractFilesA    (ADVPACK.@)
669  *
670  * Extracts the specified files from a cab archive into
671  * a destination directory.
672  *
673  * PARAMS
674  *   CabName   [I] Filename of the cab archive.
675  *   ExpandDir [I] Destination directory for the extracted files.
676  *   Flags     [I] Reserved.
677  *   FileList  [I] Optional list of files to extract.  See NOTES.
678  *   LReserved [I] Reserved.  Must be NULL.
679  *   Reserved  [I] Reserved.  Must be 0.
680  *
681  * RETURNS
682  *   Success: S_OK.
683  *   Failure: E_FAIL.
684  *
685  * NOTES
686  *   FileList is a colon-separated list of filenames.  If FileList is
687  *   non-NULL, only the files in the list will be extracted from the
688  *   cab file, otherwise all files will be extracted.  Any number of
689  *   spaces, tabs, or colons can be before or after the list, but
690  *   the list itself must only be separated by colons.
691  */
692 HRESULT WINAPI ExtractFilesA(LPCSTR CabName, LPCSTR ExpandDir, DWORD Flags,
693                              LPCSTR FileList, LPVOID LReserved, DWORD Reserved)
694 {   
695     EXTRACTdest extractDest;
696     HMODULE hCabinet;
697     HRESULT res = S_OK;
698     DWORD dwFileCount = 0;
699     DWORD dwFilesFound = 0;
700     LPSTR szConvertedList = NULL;
701
702     TRACE("(%s, %s, %d, %s, %p, %d)\n", debugstr_a(CabName), debugstr_a(ExpandDir),
703           Flags, debugstr_a(FileList), LReserved, Reserved);
704
705     if (!CabName || !ExpandDir)
706         return E_INVALIDARG;
707
708     if (GetFileAttributesA(ExpandDir) == INVALID_FILE_ATTRIBUTES)
709         return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
710
711     hCabinet = LoadLibraryA("cabinet.dll");
712     if (!hCabinet)
713         return E_FAIL;
714
715     pExtract = (void *)GetProcAddress(hCabinet, "Extract");
716     if (!pExtract)
717     {
718         res = E_FAIL;
719         goto done;
720     }
721
722     ZeroMemory(&extractDest, sizeof(EXTRACTdest));
723     lstrcpyA(extractDest.directory, ExpandDir);
724
725     if (FileList)
726     {
727         szConvertedList = convert_file_list(FileList, &dwFileCount);
728         if (!szConvertedList || dwFileCount == -1)
729         {
730             res = E_FAIL;
731             goto done;
732         }
733
734         dwFilesFound = fill_file_list(&extractDest, CabName, szConvertedList);
735         if (dwFilesFound != dwFileCount)
736         {
737             res = E_FAIL;
738             goto done;
739         }
740     }
741     else
742         extractDest.flags |= EXTRACT_FILLFILELIST;
743
744     extractDest.flags |= EXTRACT_EXTRACTFILES;
745     res = pExtract(&extractDest, CabName);
746
747 done:
748     FreeLibrary(hCabinet);
749     HeapFree(GetProcessHeap(), 0, szConvertedList);
750
751     return res;
752 }
753
754 /***********************************************************************
755  *      FileSaveMarkNotExistA (ADVPACK.@)
756  *
757  * See FileSaveMarkNotExistW.
758  */
759 HRESULT WINAPI FileSaveMarkNotExistA(LPSTR pszFileList, LPSTR pszDir, LPSTR pszBaseName)
760 {
761     TRACE("(%s, %s, %s)\n", debugstr_a(pszFileList),
762           debugstr_a(pszDir), debugstr_a(pszBaseName));
763
764     return AddDelBackupEntryA(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY);
765 }
766
767 /***********************************************************************
768  *      FileSaveMarkNotExistW (ADVPACK.@)
769  *
770  * Marks the files in the file list as not existing so they won't be
771  * backed up during a save.
772  *
773  * PARAMS
774  *   pszFileList [I] NULL-separated list of filenames.
775  *   pszDir      [I] Path of the backup directory.
776  *   pszBaseName [I] Basename of the INI file.
777  *
778  * RETURNS
779  *   Success: S_OK.
780  *   Failure: E_FAIL.
781  */
782 HRESULT WINAPI FileSaveMarkNotExistW(LPWSTR pszFileList, LPWSTR pszDir, LPWSTR pszBaseName)
783 {
784     TRACE("(%s, %s, %s)\n", debugstr_w(pszFileList),
785           debugstr_w(pszDir), debugstr_w(pszBaseName));
786
787     return AddDelBackupEntryW(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY);
788 }
789
790 /***********************************************************************
791  *      FileSaveRestoreA (ADVPACK.@)
792  *
793  * See FileSaveRestoreW.
794  */
795 HRESULT WINAPI FileSaveRestoreA(HWND hDlg, LPSTR pszFileList, LPSTR pszDir,
796                                 LPSTR pszBaseName, DWORD dwFlags)
797 {
798     UNICODE_STRING filelist, dir, basename;
799     HRESULT hr;
800
801     TRACE("(%p, %s, %s, %s, %d)\n", hDlg, debugstr_a(pszFileList),
802           debugstr_a(pszDir), debugstr_a(pszBaseName), dwFlags);
803
804     RtlCreateUnicodeStringFromAsciiz(&filelist, pszFileList);
805     RtlCreateUnicodeStringFromAsciiz(&dir, pszDir);
806     RtlCreateUnicodeStringFromAsciiz(&basename, pszBaseName);
807
808     hr = FileSaveRestoreW(hDlg, filelist.Buffer, dir.Buffer,
809                           basename.Buffer, dwFlags);
810
811     RtlFreeUnicodeString(&filelist);
812     RtlFreeUnicodeString(&dir);
813     RtlFreeUnicodeString(&basename);
814
815     return hr;
816 }                         
817
818 /***********************************************************************
819  *      FileSaveRestoreW (ADVPACK.@)
820  *
821  * Saves or restores the files in the specified file list.
822  *
823  * PARAMS
824  *   hDlg        [I] Handle to the dialog used for the display.
825  *   pszFileList [I] NULL-separated list of filenames.
826  *   pszDir      [I] Path of the backup directory.
827  *   pszBaseName [I] Basename of the backup files.
828  *   dwFlags     [I] See advpub.h.
829  *
830  * RETURNS
831  *   Success: S_OK.
832  *   Failure: E_FAIL.
833  *
834  * NOTES
835  *   If pszFileList is NULL on restore, all files will be restored.
836  *
837  * BUGS
838  *   Unimplemented.
839  */
840 HRESULT WINAPI FileSaveRestoreW(HWND hDlg, LPWSTR pszFileList, LPWSTR pszDir,
841                                 LPWSTR pszBaseName, DWORD dwFlags)
842 {
843     FIXME("(%p, %s, %s, %s, %d) stub\n", hDlg, debugstr_w(pszFileList),
844           debugstr_w(pszDir), debugstr_w(pszBaseName), dwFlags);
845
846     return E_FAIL;
847 }
848
849 /***********************************************************************
850  *      FileSaveRestoreOnINFA (ADVPACK.@)
851  *
852  * See FileSaveRestoreOnINFW.
853  */
854 HRESULT WINAPI FileSaveRestoreOnINFA(HWND hWnd, LPCSTR pszTitle, LPCSTR pszINF,
855                                     LPCSTR pszSection, LPCSTR pszBackupDir,
856                                     LPCSTR pszBaseBackupFile, DWORD dwFlags)
857 {
858     UNICODE_STRING title, inf, section;
859     UNICODE_STRING backupdir, backupfile;
860     HRESULT hr;
861
862     TRACE("(%p, %s, %s, %s, %s, %s, %d)\n", hWnd, debugstr_a(pszTitle),
863           debugstr_a(pszINF), debugstr_a(pszSection), debugstr_a(pszBackupDir),
864           debugstr_a(pszBaseBackupFile), dwFlags);
865
866     RtlCreateUnicodeStringFromAsciiz(&title, pszTitle);
867     RtlCreateUnicodeStringFromAsciiz(&inf, pszINF);
868     RtlCreateUnicodeStringFromAsciiz(&section, pszSection);
869     RtlCreateUnicodeStringFromAsciiz(&backupdir, pszBackupDir);
870     RtlCreateUnicodeStringFromAsciiz(&backupfile, pszBaseBackupFile);
871
872     hr = FileSaveRestoreOnINFW(hWnd, title.Buffer, inf.Buffer, section.Buffer,
873                                backupdir.Buffer, backupfile.Buffer, dwFlags);
874
875     RtlFreeUnicodeString(&title);
876     RtlFreeUnicodeString(&inf);
877     RtlFreeUnicodeString(&section);
878     RtlFreeUnicodeString(&backupdir);
879     RtlFreeUnicodeString(&backupfile);
880
881     return hr;
882 }
883
884 /***********************************************************************
885  *      FileSaveRestoreOnINFW (ADVPACK.@)
886  *
887  *
888  * PARAMS
889  *   hWnd              [I] Handle to the window used for the display.
890  *   pszTitle          [I] Title of the window.
891  *   pszINF            [I] Fully-qualified INF filename.
892  *   pszSection        [I] GenInstall INF section name.
893  *   pszBackupDir      [I] Directory to store the backup file.
894  *   pszBaseBackupFile [I] Basename of the backup files.
895  *   dwFlags           [I] See advpub.h
896  *
897  * RETURNS
898  *   Success: S_OK.
899  *   Failure: E_FAIL.
900  *
901  * NOTES
902  *   If pszSection is NULL, the default section will be used.
903  *
904  * BUGS
905  *   Unimplemented.
906  */
907 HRESULT WINAPI FileSaveRestoreOnINFW(HWND hWnd, LPCWSTR pszTitle, LPCWSTR pszINF,
908                                      LPCWSTR pszSection, LPCWSTR pszBackupDir,
909                                      LPCWSTR pszBaseBackupFile, DWORD dwFlags)
910 {
911     FIXME("(%p, %s, %s, %s, %s, %s, %d): stub\n", hWnd, debugstr_w(pszTitle),
912           debugstr_w(pszINF), debugstr_w(pszSection), debugstr_w(pszBackupDir),
913           debugstr_w(pszBaseBackupFile), dwFlags);
914
915     return E_FAIL;
916 }
917
918 /***********************************************************************
919  *             GetVersionFromFileA     (ADVPACK.@)
920  *
921  * See GetVersionFromFileExW.
922  */
923 HRESULT WINAPI GetVersionFromFileA(LPCSTR Filename, LPDWORD MajorVer,
924                                    LPDWORD MinorVer, BOOL Version )
925 {
926     TRACE("(%s, %p, %p, %d)\n", debugstr_a(Filename), MajorVer, MinorVer, Version);
927     return GetVersionFromFileExA(Filename, MajorVer, MinorVer, Version);
928 }
929
930 /***********************************************************************
931  *             GetVersionFromFileW     (ADVPACK.@)
932  *
933  * See GetVersionFromFileExW.
934  */
935 HRESULT WINAPI GetVersionFromFileW(LPCWSTR Filename, LPDWORD MajorVer,
936                                    LPDWORD MinorVer, BOOL Version )
937 {
938     TRACE("(%s, %p, %p, %d)\n", debugstr_w(Filename), MajorVer, MinorVer, Version);
939     return GetVersionFromFileExW(Filename, MajorVer, MinorVer, Version);
940 }
941
942 /* data for GetVersionFromFileEx */
943 typedef struct tagLANGANDCODEPAGE
944 {
945     WORD wLanguage;
946     WORD wCodePage;
947 } LANGANDCODEPAGE;
948
949 /***********************************************************************
950  *             GetVersionFromFileExA   (ADVPACK.@)
951  *
952  * See GetVersionFromFileExW.
953  */
954 HRESULT WINAPI GetVersionFromFileExA(LPCSTR lpszFilename, LPDWORD pdwMSVer,
955                                      LPDWORD pdwLSVer, BOOL bVersion )
956 {
957     UNICODE_STRING filename;
958     HRESULT res;
959
960     TRACE("(%s, %p, %p, %d)\n", debugstr_a(lpszFilename),
961           pdwMSVer, pdwLSVer, bVersion);
962
963     RtlCreateUnicodeStringFromAsciiz(&filename, lpszFilename);
964
965     res = GetVersionFromFileExW(filename.Buffer, pdwMSVer, pdwLSVer, bVersion);
966
967     RtlFreeUnicodeString(&filename);
968
969     return res;
970 }
971
972 /***********************************************************************
973  *             GetVersionFromFileExW   (ADVPACK.@)
974  *
975  * Gets the files version or language information.
976  *
977  * PARAMS
978  *   lpszFilename [I] The file to get the info from.
979  *   pdwMSVer     [O] Major version.
980  *   pdwLSVer     [O] Minor version.
981  *   bVersion     [I] Whether to retrieve version or language info.
982  *
983  * RETURNS
984  *   Always returns S_OK.
985  *
986  * NOTES
987  *   If bVersion is TRUE, version information is retrieved, else
988  *   pdwMSVer gets the language ID and pdwLSVer gets the codepage ID.
989  */
990 HRESULT WINAPI GetVersionFromFileExW(LPCWSTR lpszFilename, LPDWORD pdwMSVer,
991                                      LPDWORD pdwLSVer, BOOL bVersion )
992 {
993     VS_FIXEDFILEINFO *pFixedVersionInfo;
994     LANGANDCODEPAGE *pLangAndCodePage;
995     DWORD dwHandle, dwInfoSize;
996     WCHAR szWinDir[MAX_PATH];
997     WCHAR szFile[MAX_PATH];
998     LPVOID pVersionInfo = NULL;
999     BOOL bFileCopied = FALSE;
1000     UINT uValueLen;
1001
1002     static WCHAR backslash[] = {'\\',0};
1003     static WCHAR translation[] = {
1004         '\\','V','a','r','F','i','l','e','I','n','f','o',
1005         '\\','T','r','a','n','s','l','a','t','i','o','n',0
1006     };
1007
1008     TRACE("(%s, %p, %p, %d)\n", debugstr_w(lpszFilename),
1009           pdwMSVer, pdwLSVer, bVersion);
1010
1011     *pdwLSVer = 0;
1012     *pdwMSVer = 0;
1013
1014     lstrcpynW(szFile, lpszFilename, MAX_PATH);
1015
1016     dwInfoSize = GetFileVersionInfoSizeW(szFile, &dwHandle);
1017     if (!dwInfoSize)
1018     {
1019         /* check that the file exists */
1020         if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES)
1021             return S_OK;
1022
1023         /* file exists, but won't be found by GetFileVersionInfoSize,
1024         * so copy it to the temp dir where it will be found.
1025         */
1026         GetWindowsDirectoryW(szWinDir, MAX_PATH);
1027         GetTempFileNameW(szWinDir, NULL, 0, szFile);
1028         CopyFileW(lpszFilename, szFile, FALSE);
1029         bFileCopied = TRUE;
1030
1031         dwInfoSize = GetFileVersionInfoSizeW(szFile, &dwHandle);
1032         if (!dwInfoSize)
1033             goto done;
1034     }
1035
1036     pVersionInfo = HeapAlloc(GetProcessHeap(), 0, dwInfoSize);
1037     if (!pVersionInfo)
1038         goto done;
1039
1040     if (!GetFileVersionInfoW(szFile, dwHandle, dwInfoSize, pVersionInfo))
1041         goto done;
1042
1043     if (bVersion)
1044     {
1045         if (!VerQueryValueW(pVersionInfo, backslash,
1046             (LPVOID *)&pFixedVersionInfo, &uValueLen))
1047             goto done;
1048
1049         if (!uValueLen)
1050             goto done;
1051
1052         *pdwMSVer = pFixedVersionInfo->dwFileVersionMS;
1053         *pdwLSVer = pFixedVersionInfo->dwFileVersionLS;
1054     }
1055     else
1056     {
1057         if (!VerQueryValueW(pVersionInfo, translation,
1058              (LPVOID *)&pLangAndCodePage, &uValueLen))
1059             goto done;
1060
1061         if (!uValueLen)
1062             goto done;
1063
1064         *pdwMSVer = pLangAndCodePage->wLanguage;
1065         *pdwLSVer = pLangAndCodePage->wCodePage;
1066     }
1067
1068 done:
1069     HeapFree(GetProcessHeap(), 0, pVersionInfo);
1070
1071     if (bFileCopied)
1072         DeleteFileW(szFile);
1073
1074     return S_OK;
1075 }