shell32: Replace inline static with static inline.
[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     {
577         HeapFree(GetProcessHeap(), 0, szConvertedList);
578         return NULL;
579     }
580         
581     *dwNumFiles = 1;
582
583     /* convert the colons to double-null termination */
584     temp = szConvertedList;
585     while (*temp)
586     {
587         if (*temp == ':')
588         {
589             *temp = '\0';
590             (*dwNumFiles)++;
591         }
592
593         temp++;
594     }
595
596     return szConvertedList;
597 }
598
599 static void free_file_node(struct ExtractFileList *pNode)
600 {
601     HeapFree(GetProcessHeap(), 0, pNode->filename);
602     HeapFree(GetProcessHeap(), 0, pNode);
603 }
604
605 /* determines whether szFile is in the NULL-separated szFileList */
606 static BOOL file_in_list(LPCSTR szFile, LPCSTR szFileList)
607 {
608     DWORD dwLen = lstrlenA(szFile);
609     DWORD dwTestLen;
610
611     while (*szFileList)
612     {
613         dwTestLen = lstrlenA(szFileList);
614
615         if (dwTestLen == dwLen)
616         {
617             if (!lstrcmpiA(szFile, szFileList))
618                 return TRUE;
619         }
620
621         szFileList += dwTestLen + 1;
622     }
623
624     return FALSE;
625 }
626
627 /* removes nodes from the linked list that aren't specified in szFileList
628  * returns the number of files that are in both the linked list and szFileList
629  */
630 static DWORD fill_file_list(EXTRACTdest *extractDest, LPCSTR szCabName, LPCSTR szFileList)
631 {
632     DWORD dwNumFound = 0;
633     struct ExtractFileList *pNode;
634     struct ExtractFileList *prev = NULL;
635
636     extractDest->flags |= EXTRACT_FILLFILELIST;
637     if (pExtract(extractDest, szCabName))
638     {
639         extractDest->flags &= ~EXTRACT_FILLFILELIST;
640         return -1;
641     }
642
643     pNode = extractDest->filelist;
644     while (pNode)
645     {
646         if (file_in_list(pNode->filename, szFileList))
647         {
648             prev = pNode;
649             pNode = pNode->next;
650             dwNumFound++;
651         }
652         else if (prev)
653         {
654             prev->next = pNode->next;
655             free_file_node(pNode);
656             pNode = prev->next;
657         }
658         else
659         {
660             extractDest->filelist = pNode->next;
661             free_file_node(pNode);
662             pNode = extractDest->filelist;
663         }
664     }
665
666     extractDest->flags &= ~EXTRACT_FILLFILELIST;
667     return dwNumFound;
668 }
669
670 /***********************************************************************
671  *             ExtractFilesA    (ADVPACK.@)
672  *
673  * Extracts the specified files from a cab archive into
674  * a destination directory.
675  *
676  * PARAMS
677  *   CabName   [I] Filename of the cab archive.
678  *   ExpandDir [I] Destination directory for the extracted files.
679  *   Flags     [I] Reserved.
680  *   FileList  [I] Optional list of files to extract.  See NOTES.
681  *   LReserved [I] Reserved.  Must be NULL.
682  *   Reserved  [I] Reserved.  Must be 0.
683  *
684  * RETURNS
685  *   Success: S_OK.
686  *   Failure: E_FAIL.
687  *
688  * NOTES
689  *   FileList is a colon-separated list of filenames.  If FileList is
690  *   non-NULL, only the files in the list will be extracted from the
691  *   cab file, otherwise all files will be extracted.  Any number of
692  *   spaces, tabs, or colons can be before or after the list, but
693  *   the list itself must only be separated by colons.
694  */
695 HRESULT WINAPI ExtractFilesA(LPCSTR CabName, LPCSTR ExpandDir, DWORD Flags,
696                              LPCSTR FileList, LPVOID LReserved, DWORD Reserved)
697 {   
698     EXTRACTdest extractDest;
699     HMODULE hCabinet;
700     HRESULT res = S_OK;
701     DWORD dwFileCount = 0;
702     DWORD dwFilesFound = 0;
703     LPSTR szConvertedList = NULL;
704
705     TRACE("(%s, %s, %d, %s, %p, %d)\n", debugstr_a(CabName), debugstr_a(ExpandDir),
706           Flags, debugstr_a(FileList), LReserved, Reserved);
707
708     if (!CabName || !ExpandDir)
709         return E_INVALIDARG;
710
711     if (GetFileAttributesA(ExpandDir) == INVALID_FILE_ATTRIBUTES)
712         return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
713
714     hCabinet = LoadLibraryA("cabinet.dll");
715     if (!hCabinet)
716         return E_FAIL;
717
718     pExtract = (void *)GetProcAddress(hCabinet, "Extract");
719     if (!pExtract)
720     {
721         res = E_FAIL;
722         goto done;
723     }
724
725     ZeroMemory(&extractDest, sizeof(EXTRACTdest));
726     lstrcpyA(extractDest.directory, ExpandDir);
727
728     if (FileList)
729     {
730         szConvertedList = convert_file_list(FileList, &dwFileCount);
731         if (!szConvertedList || dwFileCount == -1)
732         {
733             res = E_FAIL;
734             goto done;
735         }
736
737         dwFilesFound = fill_file_list(&extractDest, CabName, szConvertedList);
738         if (dwFilesFound != dwFileCount)
739         {
740             res = E_FAIL;
741             goto done;
742         }
743     }
744     else
745         extractDest.flags |= EXTRACT_FILLFILELIST;
746
747     extractDest.flags |= EXTRACT_EXTRACTFILES;
748     res = pExtract(&extractDest, CabName);
749
750     if (extractDest.filelist)
751     {
752         struct ExtractFileList* curr = extractDest.filelist;
753         struct ExtractFileList* next;
754
755         while (curr)
756         {
757             next = curr->next;
758             free_file_node(curr);
759             curr = next;
760         }
761     }
762
763 done:
764     FreeLibrary(hCabinet);
765     HeapFree(GetProcessHeap(), 0, szConvertedList);
766
767     return res;
768 }
769
770 /***********************************************************************
771  *      FileSaveMarkNotExistA (ADVPACK.@)
772  *
773  * See FileSaveMarkNotExistW.
774  */
775 HRESULT WINAPI FileSaveMarkNotExistA(LPSTR pszFileList, LPSTR pszDir, LPSTR pszBaseName)
776 {
777     TRACE("(%s, %s, %s)\n", debugstr_a(pszFileList),
778           debugstr_a(pszDir), debugstr_a(pszBaseName));
779
780     return AddDelBackupEntryA(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY);
781 }
782
783 /***********************************************************************
784  *      FileSaveMarkNotExistW (ADVPACK.@)
785  *
786  * Marks the files in the file list as not existing so they won't be
787  * backed up during a save.
788  *
789  * PARAMS
790  *   pszFileList [I] NULL-separated list of filenames.
791  *   pszDir      [I] Path of the backup directory.
792  *   pszBaseName [I] Basename of the INI file.
793  *
794  * RETURNS
795  *   Success: S_OK.
796  *   Failure: E_FAIL.
797  */
798 HRESULT WINAPI FileSaveMarkNotExistW(LPWSTR pszFileList, LPWSTR pszDir, LPWSTR pszBaseName)
799 {
800     TRACE("(%s, %s, %s)\n", debugstr_w(pszFileList),
801           debugstr_w(pszDir), debugstr_w(pszBaseName));
802
803     return AddDelBackupEntryW(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY);
804 }
805
806 /***********************************************************************
807  *      FileSaveRestoreA (ADVPACK.@)
808  *
809  * See FileSaveRestoreW.
810  */
811 HRESULT WINAPI FileSaveRestoreA(HWND hDlg, LPSTR pszFileList, LPSTR pszDir,
812                                 LPSTR pszBaseName, DWORD dwFlags)
813 {
814     UNICODE_STRING filelist, dir, basename;
815     HRESULT hr;
816
817     TRACE("(%p, %s, %s, %s, %d)\n", hDlg, debugstr_a(pszFileList),
818           debugstr_a(pszDir), debugstr_a(pszBaseName), dwFlags);
819
820     RtlCreateUnicodeStringFromAsciiz(&filelist, pszFileList);
821     RtlCreateUnicodeStringFromAsciiz(&dir, pszDir);
822     RtlCreateUnicodeStringFromAsciiz(&basename, pszBaseName);
823
824     hr = FileSaveRestoreW(hDlg, filelist.Buffer, dir.Buffer,
825                           basename.Buffer, dwFlags);
826
827     RtlFreeUnicodeString(&filelist);
828     RtlFreeUnicodeString(&dir);
829     RtlFreeUnicodeString(&basename);
830
831     return hr;
832 }                         
833
834 /***********************************************************************
835  *      FileSaveRestoreW (ADVPACK.@)
836  *
837  * Saves or restores the files in the specified file list.
838  *
839  * PARAMS
840  *   hDlg        [I] Handle to the dialog used for the display.
841  *   pszFileList [I] NULL-separated list of filenames.
842  *   pszDir      [I] Path of the backup directory.
843  *   pszBaseName [I] Basename of the backup files.
844  *   dwFlags     [I] See advpub.h.
845  *
846  * RETURNS
847  *   Success: S_OK.
848  *   Failure: E_FAIL.
849  *
850  * NOTES
851  *   If pszFileList is NULL on restore, all files will be restored.
852  *
853  * BUGS
854  *   Unimplemented.
855  */
856 HRESULT WINAPI FileSaveRestoreW(HWND hDlg, LPWSTR pszFileList, LPWSTR pszDir,
857                                 LPWSTR pszBaseName, DWORD dwFlags)
858 {
859     FIXME("(%p, %s, %s, %s, %d) stub\n", hDlg, debugstr_w(pszFileList),
860           debugstr_w(pszDir), debugstr_w(pszBaseName), dwFlags);
861
862     return E_FAIL;
863 }
864
865 /***********************************************************************
866  *      FileSaveRestoreOnINFA (ADVPACK.@)
867  *
868  * See FileSaveRestoreOnINFW.
869  */
870 HRESULT WINAPI FileSaveRestoreOnINFA(HWND hWnd, LPCSTR pszTitle, LPCSTR pszINF,
871                                     LPCSTR pszSection, LPCSTR pszBackupDir,
872                                     LPCSTR pszBaseBackupFile, DWORD dwFlags)
873 {
874     UNICODE_STRING title, inf, section;
875     UNICODE_STRING backupdir, backupfile;
876     HRESULT hr;
877
878     TRACE("(%p, %s, %s, %s, %s, %s, %d)\n", hWnd, debugstr_a(pszTitle),
879           debugstr_a(pszINF), debugstr_a(pszSection), debugstr_a(pszBackupDir),
880           debugstr_a(pszBaseBackupFile), dwFlags);
881
882     RtlCreateUnicodeStringFromAsciiz(&title, pszTitle);
883     RtlCreateUnicodeStringFromAsciiz(&inf, pszINF);
884     RtlCreateUnicodeStringFromAsciiz(&section, pszSection);
885     RtlCreateUnicodeStringFromAsciiz(&backupdir, pszBackupDir);
886     RtlCreateUnicodeStringFromAsciiz(&backupfile, pszBaseBackupFile);
887
888     hr = FileSaveRestoreOnINFW(hWnd, title.Buffer, inf.Buffer, section.Buffer,
889                                backupdir.Buffer, backupfile.Buffer, dwFlags);
890
891     RtlFreeUnicodeString(&title);
892     RtlFreeUnicodeString(&inf);
893     RtlFreeUnicodeString(&section);
894     RtlFreeUnicodeString(&backupdir);
895     RtlFreeUnicodeString(&backupfile);
896
897     return hr;
898 }
899
900 /***********************************************************************
901  *      FileSaveRestoreOnINFW (ADVPACK.@)
902  *
903  *
904  * PARAMS
905  *   hWnd              [I] Handle to the window used for the display.
906  *   pszTitle          [I] Title of the window.
907  *   pszINF            [I] Fully-qualified INF filename.
908  *   pszSection        [I] GenInstall INF section name.
909  *   pszBackupDir      [I] Directory to store the backup file.
910  *   pszBaseBackupFile [I] Basename of the backup files.
911  *   dwFlags           [I] See advpub.h
912  *
913  * RETURNS
914  *   Success: S_OK.
915  *   Failure: E_FAIL.
916  *
917  * NOTES
918  *   If pszSection is NULL, the default section will be used.
919  *
920  * BUGS
921  *   Unimplemented.
922  */
923 HRESULT WINAPI FileSaveRestoreOnINFW(HWND hWnd, LPCWSTR pszTitle, LPCWSTR pszINF,
924                                      LPCWSTR pszSection, LPCWSTR pszBackupDir,
925                                      LPCWSTR pszBaseBackupFile, DWORD dwFlags)
926 {
927     FIXME("(%p, %s, %s, %s, %s, %s, %d): stub\n", hWnd, debugstr_w(pszTitle),
928           debugstr_w(pszINF), debugstr_w(pszSection), debugstr_w(pszBackupDir),
929           debugstr_w(pszBaseBackupFile), dwFlags);
930
931     return E_FAIL;
932 }
933
934 /***********************************************************************
935  *             GetVersionFromFileA     (ADVPACK.@)
936  *
937  * See GetVersionFromFileExW.
938  */
939 HRESULT WINAPI GetVersionFromFileA(LPCSTR Filename, LPDWORD MajorVer,
940                                    LPDWORD MinorVer, BOOL Version )
941 {
942     TRACE("(%s, %p, %p, %d)\n", debugstr_a(Filename), MajorVer, MinorVer, Version);
943     return GetVersionFromFileExA(Filename, MajorVer, MinorVer, Version);
944 }
945
946 /***********************************************************************
947  *             GetVersionFromFileW     (ADVPACK.@)
948  *
949  * See GetVersionFromFileExW.
950  */
951 HRESULT WINAPI GetVersionFromFileW(LPCWSTR Filename, LPDWORD MajorVer,
952                                    LPDWORD MinorVer, BOOL Version )
953 {
954     TRACE("(%s, %p, %p, %d)\n", debugstr_w(Filename), MajorVer, MinorVer, Version);
955     return GetVersionFromFileExW(Filename, MajorVer, MinorVer, Version);
956 }
957
958 /* data for GetVersionFromFileEx */
959 typedef struct tagLANGANDCODEPAGE
960 {
961     WORD wLanguage;
962     WORD wCodePage;
963 } LANGANDCODEPAGE;
964
965 /***********************************************************************
966  *             GetVersionFromFileExA   (ADVPACK.@)
967  *
968  * See GetVersionFromFileExW.
969  */
970 HRESULT WINAPI GetVersionFromFileExA(LPCSTR lpszFilename, LPDWORD pdwMSVer,
971                                      LPDWORD pdwLSVer, BOOL bVersion )
972 {
973     UNICODE_STRING filename;
974     HRESULT res;
975
976     TRACE("(%s, %p, %p, %d)\n", debugstr_a(lpszFilename),
977           pdwMSVer, pdwLSVer, bVersion);
978
979     RtlCreateUnicodeStringFromAsciiz(&filename, lpszFilename);
980
981     res = GetVersionFromFileExW(filename.Buffer, pdwMSVer, pdwLSVer, bVersion);
982
983     RtlFreeUnicodeString(&filename);
984
985     return res;
986 }
987
988 /***********************************************************************
989  *             GetVersionFromFileExW   (ADVPACK.@)
990  *
991  * Gets the files version or language information.
992  *
993  * PARAMS
994  *   lpszFilename [I] The file to get the info from.
995  *   pdwMSVer     [O] Major version.
996  *   pdwLSVer     [O] Minor version.
997  *   bVersion     [I] Whether to retrieve version or language info.
998  *
999  * RETURNS
1000  *   Always returns S_OK.
1001  *
1002  * NOTES
1003  *   If bVersion is TRUE, version information is retrieved, else
1004  *   pdwMSVer gets the language ID and pdwLSVer gets the codepage ID.
1005  */
1006 HRESULT WINAPI GetVersionFromFileExW(LPCWSTR lpszFilename, LPDWORD pdwMSVer,
1007                                      LPDWORD pdwLSVer, BOOL bVersion )
1008 {
1009     VS_FIXEDFILEINFO *pFixedVersionInfo;
1010     LANGANDCODEPAGE *pLangAndCodePage;
1011     DWORD dwHandle, dwInfoSize;
1012     WCHAR szWinDir[MAX_PATH];
1013     WCHAR szFile[MAX_PATH];
1014     LPVOID pVersionInfo = NULL;
1015     BOOL bFileCopied = FALSE;
1016     UINT uValueLen;
1017
1018     static WCHAR backslash[] = {'\\',0};
1019     static WCHAR translation[] = {
1020         '\\','V','a','r','F','i','l','e','I','n','f','o',
1021         '\\','T','r','a','n','s','l','a','t','i','o','n',0
1022     };
1023
1024     TRACE("(%s, %p, %p, %d)\n", debugstr_w(lpszFilename),
1025           pdwMSVer, pdwLSVer, bVersion);
1026
1027     *pdwLSVer = 0;
1028     *pdwMSVer = 0;
1029
1030     lstrcpynW(szFile, lpszFilename, MAX_PATH);
1031
1032     dwInfoSize = GetFileVersionInfoSizeW(szFile, &dwHandle);
1033     if (!dwInfoSize)
1034     {
1035         /* check that the file exists */
1036         if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES)
1037             return S_OK;
1038
1039         /* file exists, but won't be found by GetFileVersionInfoSize,
1040         * so copy it to the temp dir where it will be found.
1041         */
1042         GetWindowsDirectoryW(szWinDir, MAX_PATH);
1043         GetTempFileNameW(szWinDir, NULL, 0, szFile);
1044         CopyFileW(lpszFilename, szFile, FALSE);
1045         bFileCopied = TRUE;
1046
1047         dwInfoSize = GetFileVersionInfoSizeW(szFile, &dwHandle);
1048         if (!dwInfoSize)
1049             goto done;
1050     }
1051
1052     pVersionInfo = HeapAlloc(GetProcessHeap(), 0, dwInfoSize);
1053     if (!pVersionInfo)
1054         goto done;
1055
1056     if (!GetFileVersionInfoW(szFile, dwHandle, dwInfoSize, pVersionInfo))
1057         goto done;
1058
1059     if (bVersion)
1060     {
1061         if (!VerQueryValueW(pVersionInfo, backslash,
1062             (LPVOID *)&pFixedVersionInfo, &uValueLen))
1063             goto done;
1064
1065         if (!uValueLen)
1066             goto done;
1067
1068         *pdwMSVer = pFixedVersionInfo->dwFileVersionMS;
1069         *pdwLSVer = pFixedVersionInfo->dwFileVersionLS;
1070     }
1071     else
1072     {
1073         if (!VerQueryValueW(pVersionInfo, translation,
1074              (LPVOID *)&pLangAndCodePage, &uValueLen))
1075             goto done;
1076
1077         if (!uValueLen)
1078             goto done;
1079
1080         *pdwMSVer = pLangAndCodePage->wLanguage;
1081         *pdwLSVer = pLangAndCodePage->wCodePage;
1082     }
1083
1084 done:
1085     HeapFree(GetProcessHeap(), 0, pVersionInfo);
1086
1087     if (bFileCopied)
1088         DeleteFileW(szFile);
1089
1090     return S_OK;
1091 }