files.c 31.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Advpack file functions
 *
 * Copyright 2006 James Hawkins
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 25 26

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
27 28
#include "winreg.h"
#include "winver.h"
29
#include "winternl.h"
30
#include "setupapi.h"
31
#include "advpub.h"
32
#include "fdi.h"
33
#include "wine/debug.h"
34
#include "wine/unicode.h"
35
#include "advpack_private.h"
36 37 38

WINE_DEFAULT_DEBUG_CHANNEL(advpack);

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
/* converts an ansi double null-terminated list to a unicode list */
static LPWSTR ansi_to_unicode_list(LPCSTR ansi_list)
{
    DWORD len, wlen = 0;
    LPWSTR list;
    LPCSTR ptr = ansi_list;

    while (*ptr) ptr += lstrlenA(ptr) + 1;
    len = ptr + 1 - ansi_list;
    wlen = MultiByteToWideChar(CP_ACP, 0, ansi_list, len, NULL, 0);
    list = HeapAlloc(GetProcessHeap(), 0, wlen * sizeof(WCHAR));
    MultiByteToWideChar(CP_ACP, 0, ansi_list, len, list, wlen);
    return list;
}

54
/***********************************************************************
55
 *      AddDelBackupEntryA (ADVPACK.@)
56
 *
57 58 59 60 61 62
 * See AddDelBackupEntryW.
 */
HRESULT WINAPI AddDelBackupEntryA(LPCSTR lpcszFileList, LPCSTR lpcszBackupDir,
                                  LPCSTR lpcszBaseName, DWORD dwFlags)
{
    UNICODE_STRING backupdir, basename;
63 64
    LPWSTR filelist;
    LPCWSTR backup;
65 66
    HRESULT res;

67
    TRACE("(%s, %s, %s, %d)\n", debugstr_a(lpcszFileList),
68
          debugstr_a(lpcszBackupDir), debugstr_a(lpcszBaseName), dwFlags);
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

    if (lpcszFileList)
        filelist = ansi_to_unicode_list(lpcszFileList);
    else
        filelist = NULL;

    RtlCreateUnicodeStringFromAsciiz(&backupdir, lpcszBackupDir);
    RtlCreateUnicodeStringFromAsciiz(&basename, lpcszBaseName);

    if (lpcszBackupDir)
        backup = backupdir.Buffer;
    else
        backup = NULL;

    res = AddDelBackupEntryW(filelist, backup, basename.Buffer, dwFlags);

    HeapFree(GetProcessHeap(), 0, filelist);

    RtlFreeUnicodeString(&backupdir);
    RtlFreeUnicodeString(&basename);

    return res;
}

/***********************************************************************
 *      AddDelBackupEntryW (ADVPACK.@)
 *
96 97
 * Either appends the files in the file list to the backup section of
 * the specified INI, or deletes the entries from the INI file.
98 99 100 101
 *
 * PARAMS
 *   lpcszFileList  [I] NULL-separated list of filenames.
 *   lpcszBackupDir [I] Path of the backup directory.
102 103 104 105
 *   lpcszBaseName  [I] Basename of the INI file.
 *   dwFlags        [I] AADBE_ADD_ENTRY adds the entries in the file list
 *                      to the INI file, while AADBE_DEL_ENTRY removes
 *                      the entries from the INI file.
106 107
 *
 * RETURNS
108
 *   S_OK in all cases.
109
 *
110 111 112 113 114 115
 * NOTES
 *   If the INI file does not exist before adding entries to it, the file
 *   will be created.
 * 
 *   If lpcszBackupDir is NULL, the INI file is assumed to exist in
 *   c:\windows or created there if it does not exist.
116
 */
117 118
HRESULT WINAPI AddDelBackupEntryW(LPCWSTR lpcszFileList, LPCWSTR lpcszBackupDir,
                                  LPCWSTR lpcszBaseName, DWORD dwFlags)
119
{
120
    WCHAR szIniPath[MAX_PATH];
121
    LPCWSTR szString = NULL;
122

123 124 125 126 127 128 129
    static const WCHAR szBackupEntry[] = {
        '-','1',',','0',',','0',',','0',',','0',',','0',',','-','1',0
    };
    
    static const WCHAR backslash[] = {'\\',0};
    static const WCHAR ini[] = {'.','i','n','i',0};
    static const WCHAR backup[] = {'b','a','c','k','u','p',0};
130

131
    TRACE("(%s, %s, %s, %d)\n", debugstr_w(lpcszFileList),
132
          debugstr_w(lpcszBackupDir), debugstr_w(lpcszBaseName), dwFlags);
133

134 135 136 137
    if (!lpcszFileList || !*lpcszFileList)
        return S_OK;

    if (lpcszBackupDir)
138
        lstrcpyW(szIniPath, lpcszBackupDir);
139
    else
140
        GetWindowsDirectoryW(szIniPath, MAX_PATH);
141

142 143 144
    lstrcatW(szIniPath, backslash);
    lstrcatW(szIniPath, lpcszBaseName);
    lstrcatW(szIniPath, ini);
145

146
    SetFileAttributesW(szIniPath, FILE_ATTRIBUTE_NORMAL);
147 148

    if (dwFlags & AADBE_ADD_ENTRY)
149
        szString = szBackupEntry;
150 151 152 153 154 155
    else if (dwFlags & AADBE_DEL_ENTRY)
        szString = NULL;

    /* add or delete the INI entries */
    while (*lpcszFileList)
    {
156 157
        WritePrivateProfileStringW(backup, lpcszFileList, szString, szIniPath);
        lpcszFileList += lstrlenW(lpcszFileList) + 1;
158 159 160
    }

    /* hide the INI file */
161
    SetFileAttributesW(szIniPath, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
162

163
    return S_OK;
164 165
}

166 167 168
/* FIXME: this is only for the local case, X:\ */
#define ROOT_LENGTH 3

169 170
static UINT CALLBACK pQuietQueueCallback(PVOID Context, UINT Notification,
                                         UINT_PTR Param1, UINT_PTR Param2)
171 172 173 174
{
    return 1;
}

175 176
static UINT CALLBACK pQueueCallback(PVOID Context, UINT Notification,
                                    UINT_PTR Param1, UINT_PTR Param2)
177 178 179 180 181 182 183
{
    /* only be verbose for error notifications */
    if (!Notification ||
        Notification == SPFILENOTIFY_RENAMEERROR ||
        Notification == SPFILENOTIFY_DELETEERROR ||
        Notification == SPFILENOTIFY_COPYERROR)
    {
184
        return SetupDefaultQueueCallbackW(Context, Notification,
185 186 187 188 189 190 191
                                          Param1, Param2);
    }

    return 1;
}

/***********************************************************************
192
 *      AdvInstallFileA (ADVPACK.@)
193
 *
194 195 196
 * See AdvInstallFileW.
 */
HRESULT WINAPI AdvInstallFileA(HWND hwnd, LPCSTR lpszSourceDir, LPCSTR lpszSourceFile,
197 198
                               LPCSTR lpszDestDir, LPCSTR lpszDestFile,
                               DWORD dwFlags, DWORD dwReserved)
199 200 201 202 203
{
    UNICODE_STRING sourcedir, sourcefile;
    UNICODE_STRING destdir, destfile;
    HRESULT res;

204
    TRACE("(%p, %s, %s, %s, %s, %d, %d)\n", hwnd, debugstr_a(lpszSourceDir),
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
          debugstr_a(lpszSourceFile), debugstr_a(lpszDestDir),
          debugstr_a(lpszDestFile), dwFlags, dwReserved);

    if (!lpszSourceDir || !lpszSourceFile || !lpszDestDir)
        return E_INVALIDARG;

    RtlCreateUnicodeStringFromAsciiz(&sourcedir, lpszSourceDir);
    RtlCreateUnicodeStringFromAsciiz(&sourcefile, lpszSourceFile);
    RtlCreateUnicodeStringFromAsciiz(&destdir, lpszDestDir);
    RtlCreateUnicodeStringFromAsciiz(&destfile, lpszDestFile);

    res = AdvInstallFileW(hwnd, sourcedir.Buffer, sourcefile.Buffer,
                          destdir.Buffer, destfile.Buffer, dwFlags, dwReserved);

    RtlFreeUnicodeString(&sourcedir);
    RtlFreeUnicodeString(&sourcefile);
    RtlFreeUnicodeString(&destdir);
    RtlFreeUnicodeString(&destfile);

    return res;
}

/***********************************************************************
 *      AdvInstallFileW (ADVPACK.@)
 *
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
 * Copies a file from the source to a destination.
 *
 * PARAMS
 *   hwnd           [I] Handle to the window used for messages.
 *   lpszSourceDir  [I] Source directory.
 *   lpszSourceFile [I] Source filename.
 *   lpszDestDir    [I] Destination directory.
 *   lpszDestFile   [I] Optional destination filename.
 *   dwFlags        [I] See advpub.h.
 *   dwReserved     [I] Reserved.  Must be 0.
 *
 * RETURNS
 *   Success: S_OK.
 *   Failure: E_FAIL.
 *
 * NOTES
 *   If lpszDestFile is NULL, the destination filename is the same as
 *   lpszSourceFIle.
 */
249 250 251
HRESULT WINAPI AdvInstallFileW(HWND hwnd, LPCWSTR lpszSourceDir, LPCWSTR lpszSourceFile,
                               LPCWSTR lpszDestDir, LPCWSTR lpszDestFile,
                               DWORD dwFlags, DWORD dwReserved)
252
{
253
    PSP_FILE_CALLBACK_W pFileCallback;
254 255
    LPWSTR szDestFilename;
    LPCWSTR szPath;
256
    WCHAR szRootPath[ROOT_LENGTH];
257 258 259 260
    DWORD dwLen, dwLastError;
    HSPFILEQ fileQueue;
    PVOID pContext;

261
    TRACE("(%p, %s, %s, %s, %s, %d, %d)\n", hwnd, debugstr_w(lpszSourceDir),
262 263
          debugstr_w(lpszSourceFile), debugstr_w(lpszDestDir),
          debugstr_w(lpszDestFile), dwFlags, dwReserved);
264 265 266 267 268 269 270 271 272 273 274

    if (!lpszSourceDir || !lpszSourceFile || !lpszDestDir)
        return E_INVALIDARG;
        
    fileQueue = SetupOpenFileQueue();
    if (fileQueue == INVALID_HANDLE_VALUE)
        return HRESULT_FROM_WIN32(GetLastError());

    pContext = NULL;
    dwLastError = ERROR_SUCCESS;

275
    lstrcpynW(szRootPath, lpszSourceDir, ROOT_LENGTH);
276
    szPath = lpszSourceDir + ROOT_LENGTH;
277 278 279 280

    /* use lpszSourceFile as destination filename if lpszDestFile is NULL */
    if (lpszDestFile)
    {
281 282 283
        dwLen = lstrlenW(lpszDestFile);
        szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
        lstrcpyW(szDestFilename, lpszDestFile);
284 285 286
    }
    else
    {
287 288 289
        dwLen = lstrlenW(lpszSourceFile);
        szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
        lstrcpyW(szDestFilename, lpszSourceFile);
290 291 292
    }

    /* add the file copy operation to the setup queue */
293
    if (!SetupQueueCopyW(fileQueue, szRootPath, szPath, lpszSourceFile, NULL,
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
                         NULL, lpszDestDir, szDestFilename, dwFlags))
    {
        dwLastError = GetLastError();
        goto done;
    }

    pContext = SetupInitDefaultQueueCallbackEx(hwnd, INVALID_HANDLE_VALUE,
                                               0, 0, NULL);
    if (!pContext)
    {
        dwLastError = GetLastError();
        goto done;
    }

    /* don't output anything for AIF_QUIET */
    if (dwFlags & AIF_QUIET)
        pFileCallback = pQuietQueueCallback;
    else
        pFileCallback = pQueueCallback;

    /* perform the file copy */
315
    if (!SetupCommitFileQueueW(hwnd, fileQueue, pFileCallback, pContext))
316 317 318 319 320 321 322 323 324 325 326 327 328 329
    {
        dwLastError = GetLastError();
        goto done;
    }

done:
    SetupTermDefaultQueueCallback(pContext);
    SetupCloseFileQueue(fileQueue);
    
    HeapFree(GetProcessHeap(), 0, szDestFilename);
    
    return HRESULT_FROM_WIN32(dwLastError);
}

330
static HRESULT DELNODE_recurse_dirtree(LPWSTR fname, DWORD flags)
331
{
332
    DWORD fattrs = GetFileAttributesW(fname);
333 334
    HRESULT ret = E_FAIL;

335 336 337 338
    static const WCHAR asterisk[] = {'*',0};
    static const WCHAR dot[] = {'.',0};
    static const WCHAR dotdot[] = {'.','.',0};

339 340 341
    if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
    {
        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 (fname_len && fname[fname_len-1] != '\\') fname[fname_len++] = '\\';
348
        lstrcpyW(fname + fname_len, asterisk);
349

350
        if ((hFindFile = FindFirstFileW(fname, &w32fd)) != INVALID_HANDLE_VALUE)
351 352
        {
            /* Iterate through the files in the directory */
353
            for (done = FALSE; !done; done = !FindNextFileW(hFindFile, &w32fd))
354
            {
355 356 357
                TRACE("%s\n", debugstr_w(w32fd.cFileName));
                if (lstrcmpW(dot, w32fd.cFileName) != 0 &&
                    lstrcmpW(dotdot, w32fd.cFileName) != 0)
358
                {
359
                    lstrcpyW(fname + fname_len, w32fd.cFileName);
360 361 362 363 364 365 366 367 368 369 370 371 372 373
                    if (DELNODE_recurse_dirtree(fname, flags) != S_OK)
                    {
                        break; /* Failure */
                    }
                }
            }
            FindClose(hFindFile);
        }

        /* We're done with this directory, so restore the old path without wildcard */
        *(fname + fname_len) = '\0';

        if (done)
        {
374 375
            TRACE("%s: directory\n", debugstr_w(fname));
            if (SetFileAttributesW(fname, FILE_ATTRIBUTE_NORMAL) && RemoveDirectoryW(fname))
376 377 378 379 380 381 382
            {
                ret = S_OK;
            }
        }
    }
    else
    {
383 384
        TRACE("%s: file\n", debugstr_w(fname));
        if (SetFileAttributesW(fname, FILE_ATTRIBUTE_NORMAL) && DeleteFileW(fname))
385 386 387 388 389 390 391 392 393
        {
            ret = S_OK;
        }
    }
    
    return ret;
}

/***********************************************************************
394
 *              DelNodeA   (ADVPACK.@)
395
 *
396 397
 * See DelNodeW.
 */
398
HRESULT WINAPI DelNodeA(LPCSTR pszFileOrDirName, DWORD dwFlags)
399 400 401 402
{
    UNICODE_STRING fileordirname;
    HRESULT res;

403
    TRACE("(%s, %d)\n", debugstr_a(pszFileOrDirName), dwFlags);
404 405 406 407 408 409 410 411 412 413 414 415 416

    RtlCreateUnicodeStringFromAsciiz(&fileordirname, pszFileOrDirName);

    res = DelNodeW(fileordirname.Buffer, dwFlags);

    RtlFreeUnicodeString(&fileordirname);

    return res;
}

/***********************************************************************
 *              DelNodeW   (ADVPACK.@)
 *
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
 * Deletes a file or directory
 *
 * PARAMS
 *   pszFileOrDirName   [I] Name of file or directory to delete
 *   dwFlags            [I] Flags; see include/advpub.h
 *
 * RETURNS 
 *   Success: S_OK
 *   Failure: E_FAIL
 *
 * BUGS
 *   - Ignores flags
 *   - Native version apparently does a lot of checking to make sure
 *     we're not trying to delete a system directory etc.
 */
432
HRESULT WINAPI DelNodeW(LPCWSTR pszFileOrDirName, DWORD dwFlags)
433
{
434
    WCHAR fname[MAX_PATH];
435 436
    HRESULT ret = E_FAIL;
    
437
    TRACE("(%s, %d)\n", debugstr_w(pszFileOrDirName), dwFlags);
438 439 440 441 442 443
    
    if (dwFlags)
        FIXME("Flags ignored!\n");

    if (pszFileOrDirName && *pszFileOrDirName)
    {
444
        lstrcpyW(fname, pszFileOrDirName);
445 446 447 448 449 450 451 452 453 454

        /* TODO: Should check for system directory deletion etc. here */

        ret = DELNODE_recurse_dirtree(fname, dwFlags);
    }

    return ret;
}

/***********************************************************************
455
 *             DelNodeRunDLL32A   (ADVPACK.@)
456
 *
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
 * See DelNodeRunDLL32W.
 */
HRESULT WINAPI DelNodeRunDLL32A(HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show)
{
    UNICODE_STRING params;
    HRESULT hr;

    TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_a(cmdline), show);

    RtlCreateUnicodeStringFromAsciiz(&params, cmdline);

    hr = DelNodeRunDLL32W(hWnd, hInst, params.Buffer, show);

    RtlFreeUnicodeString(&params);

    return hr;
}

/***********************************************************************
 *             DelNodeRunDLL32W   (ADVPACK.@)
 *
478 479 480 481 482 483 484 485 486 487 488 489
 * Deletes a file or directory, WinMain style.
 *
 * PARAMS
 *   hWnd    [I] Handle to the window used for the display.
 *   hInst   [I] Instance of the process.
 *   cmdline [I] Contains parameters in the order FileOrDirName,Flags.
 *   show    [I] How the window should be shown.
 *
 * RETURNS
 *   Success: S_OK.
 *   Failure: E_FAIL.
 */
490
HRESULT WINAPI DelNodeRunDLL32W(HWND hWnd, HINSTANCE hInst, LPWSTR cmdline, INT show)
491
{
492 493
    LPWSTR szFilename, szFlags;
    LPWSTR cmdline_copy, cmdline_ptr;
494
    DWORD dwFlags = 0;
495 496
    HRESULT res;

497
    TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_w(cmdline), show);
498

499
    cmdline_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
500
    cmdline_ptr = cmdline_copy;
501
    lstrcpyW(cmdline_copy, cmdline);
502 503

    /* get the parameters at indexes 0 and 1 respectively */
504 505
    szFilename = get_parameter(&cmdline_ptr, ',');
    szFlags = get_parameter(&cmdline_ptr, ',');
506

507
    if (szFlags)
508
        dwFlags = atolW(szFlags);
509

510
    res = DelNodeW(szFilename, dwFlags);
511

512
    HeapFree(GetProcessHeap(), 0, cmdline_copy);
513 514

    return res;
515 516
}

Austin English's avatar
Austin English committed
517
/* The following definitions were copied from dlls/cabinet/cabinet.h */
518

519
/* SESSION Operation */
520 521 522
#define EXTRACT_FILLFILELIST  0x00000001
#define EXTRACT_EXTRACTFILES  0x00000002

523 524 525
struct FILELIST{
    LPSTR FileName;
    struct FILELIST *next;
526
    BOOL DoExtract;
527
};
528 529

typedef struct {
530 531 532 533 534 535 536 537 538 539 540 541
    INT FileSize;
    ERF Error;
    struct FILELIST *FileList;
    INT FileCount;
    INT Operation;
    CHAR Destination[MAX_PATH];
    CHAR CurrentFile[MAX_PATH];
    CHAR Reserved[MAX_PATH];
    struct FILELIST *FilterList;
} SESSION;

static HRESULT (WINAPI *pExtract)(SESSION*, LPCSTR);
542 543 544 545 546 547 548

/* removes legal characters before and after file list, and
 * converts the file list to a NULL-separated list
 */
static LPSTR convert_file_list(LPCSTR FileList, DWORD *dwNumFiles)
{
    DWORD dwLen;
549 550
    const char *first = FileList;
    const char *last = FileList + strlen(FileList) - 1;
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
    LPSTR szConvertedList, temp;
    
    /* any number of these chars before the list is OK */
    while (first < last && (*first == ' ' || *first == '\t' || *first == ':'))
        first++;

    /* any number of these chars after the list is OK */
    while (last > first && (*last == ' ' || *last == '\t' || *last == ':'))
        last--;

    if (first == last)
        return NULL;

    dwLen = last - first + 3; /* room for double-null termination */
    szConvertedList = HeapAlloc(GetProcessHeap(), 0, dwLen);
    lstrcpynA(szConvertedList, first, dwLen - 1);
    szConvertedList[dwLen - 1] = '\0';

    /* empty list */
    if (!lstrlenA(szConvertedList))
571 572
    {
        HeapFree(GetProcessHeap(), 0, szConvertedList);
573
        return NULL;
574
    }
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
        
    *dwNumFiles = 1;

    /* convert the colons to double-null termination */
    temp = szConvertedList;
    while (*temp)
    {
        if (*temp == ':')
        {
            *temp = '\0';
            (*dwNumFiles)++;
        }

        temp++;
    }

    return szConvertedList;
}

594
static void free_file_node(struct FILELIST *pNode)
595
{
596
    HeapFree(GetProcessHeap(), 0, pNode->FileName);
597 598 599 600
    HeapFree(GetProcessHeap(), 0, pNode);
}

/* determines whether szFile is in the NULL-separated szFileList */
601
static BOOL file_in_list(LPCSTR szFile, LPCSTR szFileList)
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
{
    DWORD dwLen = lstrlenA(szFile);
    DWORD dwTestLen;

    while (*szFileList)
    {
        dwTestLen = lstrlenA(szFileList);

        if (dwTestLen == dwLen)
        {
            if (!lstrcmpiA(szFile, szFileList))
                return TRUE;
        }

        szFileList += dwTestLen + 1;
    }

    return FALSE;
}

622 623

/* returns the number of files that are in both the linked list and szFileList */
624
static DWORD fill_file_list(SESSION *session, LPCSTR szCabName, LPCSTR szFileList)
625 626
{
    DWORD dwNumFound = 0;
627
    struct FILELIST *pNode;
628

629 630
    session->Operation |= EXTRACT_FILLFILELIST;
    if (pExtract(session, szCabName))
631
    {
632
        session->Operation &= ~EXTRACT_FILLFILELIST;
633 634 635
        return -1;
    }

636
    pNode = session->FileList;
637 638
    while (pNode)
    {
639 640
        if (!file_in_list(pNode->FileName, szFileList))
            pNode->DoExtract = FALSE;
641
        else
642 643 644
            dwNumFound++;

        pNode = pNode->next;
645 646
    }

647
    session->Operation &= ~EXTRACT_FILLFILELIST;
648 649 650 651
    return dwNumFound;
}

/***********************************************************************
652
 *             ExtractFilesA    (ADVPACK.@)
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
 *
 * Extracts the specified files from a cab archive into
 * a destination directory.
 *
 * PARAMS
 *   CabName   [I] Filename of the cab archive.
 *   ExpandDir [I] Destination directory for the extracted files.
 *   Flags     [I] Reserved.
 *   FileList  [I] Optional list of files to extract.  See NOTES.
 *   LReserved [I] Reserved.  Must be NULL.
 *   Reserved  [I] Reserved.  Must be 0.
 *
 * RETURNS
 *   Success: S_OK.
 *   Failure: E_FAIL.
 *
 * NOTES
 *   FileList is a colon-separated list of filenames.  If FileList is
 *   non-NULL, only the files in the list will be extracted from the
 *   cab file, otherwise all files will be extracted.  Any number of
 *   spaces, tabs, or colons can be before or after the list, but
 *   the list itself must only be separated by colons.
 */
676 677
HRESULT WINAPI ExtractFilesA(LPCSTR CabName, LPCSTR ExpandDir, DWORD Flags,
                             LPCSTR FileList, LPVOID LReserved, DWORD Reserved)
678
{   
679
    SESSION session;
680 681 682 683 684 685
    HMODULE hCabinet;
    HRESULT res = S_OK;
    DWORD dwFileCount = 0;
    DWORD dwFilesFound = 0;
    LPSTR szConvertedList = NULL;

686
    TRACE("(%s, %s, %d, %s, %p, %d)\n", debugstr_a(CabName), debugstr_a(ExpandDir),
687
          Flags, debugstr_a(FileList), LReserved, Reserved);
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705

    if (!CabName || !ExpandDir)
        return E_INVALIDARG;

    if (GetFileAttributesA(ExpandDir) == INVALID_FILE_ATTRIBUTES)
        return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);

    hCabinet = LoadLibraryA("cabinet.dll");
    if (!hCabinet)
        return E_FAIL;

    pExtract = (void *)GetProcAddress(hCabinet, "Extract");
    if (!pExtract)
    {
        res = E_FAIL;
        goto done;
    }

706 707
    ZeroMemory(&session, sizeof(SESSION));
    lstrcpyA(session.Destination, ExpandDir);
708 709 710 711 712 713 714 715 716 717

    if (FileList)
    {
        szConvertedList = convert_file_list(FileList, &dwFileCount);
        if (!szConvertedList || dwFileCount == -1)
        {
            res = E_FAIL;
            goto done;
        }

718
        dwFilesFound = fill_file_list(&session, CabName, szConvertedList);
719 720 721 722 723 724 725
        if (dwFilesFound != dwFileCount)
        {
            res = E_FAIL;
            goto done;
        }
    }
    else
726
        session.Operation |= EXTRACT_FILLFILELIST;
727

728 729
    session.Operation |= EXTRACT_EXTRACTFILES;
    res = pExtract(&session, CabName);
730

731
    if (session.FileList)
732
    {
733 734
        struct FILELIST *curr = session.FileList;
        struct FILELIST *next;
735 736 737 738 739 740 741 742 743

        while (curr)
        {
            next = curr->next;
            free_file_node(curr);
            curr = next;
        }
    }

744 745 746 747 748 749 750
done:
    FreeLibrary(hCabinet);
    HeapFree(GetProcessHeap(), 0, szConvertedList);

    return res;
}

751
/***********************************************************************
752
 *      FileSaveMarkNotExistA (ADVPACK.@)
753
 *
754 755 756 757
 * See FileSaveMarkNotExistW.
 */
HRESULT WINAPI FileSaveMarkNotExistA(LPSTR pszFileList, LPSTR pszDir, LPSTR pszBaseName)
{
758 759
    TRACE("(%s, %s, %s)\n", debugstr_a(pszFileList),
          debugstr_a(pszDir), debugstr_a(pszBaseName));
760 761 762 763 764 765 766

    return AddDelBackupEntryA(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY);
}

/***********************************************************************
 *      FileSaveMarkNotExistW (ADVPACK.@)
 *
767 768 769 770 771 772
 * Marks the files in the file list as not existing so they won't be
 * backed up during a save.
 *
 * PARAMS
 *   pszFileList [I] NULL-separated list of filenames.
 *   pszDir      [I] Path of the backup directory.
773
 *   pszBaseName [I] Basename of the INI file.
774 775 776 777 778
 *
 * RETURNS
 *   Success: S_OK.
 *   Failure: E_FAIL.
 */
779
HRESULT WINAPI FileSaveMarkNotExistW(LPWSTR pszFileList, LPWSTR pszDir, LPWSTR pszBaseName)
780
{
781 782
    TRACE("(%s, %s, %s)\n", debugstr_w(pszFileList),
          debugstr_w(pszDir), debugstr_w(pszBaseName));
783

784
    return AddDelBackupEntryW(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY);
785 786 787
}

/***********************************************************************
788
 *      FileSaveRestoreA (ADVPACK.@)
789
 *
790 791 792 793 794 795 796 797
 * See FileSaveRestoreW.
 */
HRESULT WINAPI FileSaveRestoreA(HWND hDlg, LPSTR pszFileList, LPSTR pszDir,
                                LPSTR pszBaseName, DWORD dwFlags)
{
    UNICODE_STRING filelist, dir, basename;
    HRESULT hr;

798
    TRACE("(%p, %s, %s, %s, %d)\n", hDlg, debugstr_a(pszFileList),
799
          debugstr_a(pszDir), debugstr_a(pszBaseName), dwFlags);
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817

    RtlCreateUnicodeStringFromAsciiz(&filelist, pszFileList);
    RtlCreateUnicodeStringFromAsciiz(&dir, pszDir);
    RtlCreateUnicodeStringFromAsciiz(&basename, pszBaseName);

    hr = FileSaveRestoreW(hDlg, filelist.Buffer, dir.Buffer,
                          basename.Buffer, dwFlags);

    RtlFreeUnicodeString(&filelist);
    RtlFreeUnicodeString(&dir);
    RtlFreeUnicodeString(&basename);

    return hr;
}                         

/***********************************************************************
 *      FileSaveRestoreW (ADVPACK.@)
 *
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
 * Saves or restores the files in the specified file list.
 *
 * PARAMS
 *   hDlg        [I] Handle to the dialog used for the display.
 *   pszFileList [I] NULL-separated list of filenames.
 *   pszDir      [I] Path of the backup directory.
 *   pszBaseName [I] Basename of the backup files.
 *   dwFlags     [I] See advpub.h.
 *
 * RETURNS
 *   Success: S_OK.
 *   Failure: E_FAIL.
 *
 * NOTES
 *   If pszFileList is NULL on restore, all files will be restored.
 *
 * BUGS
 *   Unimplemented.
 */
837 838
HRESULT WINAPI FileSaveRestoreW(HWND hDlg, LPWSTR pszFileList, LPWSTR pszDir,
                                LPWSTR pszBaseName, DWORD dwFlags)
839
{
840
    FIXME("(%p, %s, %s, %s, %d) stub\n", hDlg, debugstr_w(pszFileList),
841
          debugstr_w(pszDir), debugstr_w(pszBaseName), dwFlags);
842 843 844 845 846

    return E_FAIL;
}

/***********************************************************************
847
 *      FileSaveRestoreOnINFA (ADVPACK.@)
848
 *
849 850 851 852 853 854 855 856 857 858
 * See FileSaveRestoreOnINFW.
 */
HRESULT WINAPI FileSaveRestoreOnINFA(HWND hWnd, LPCSTR pszTitle, LPCSTR pszINF,
                                    LPCSTR pszSection, LPCSTR pszBackupDir,
                                    LPCSTR pszBaseBackupFile, DWORD dwFlags)
{
    UNICODE_STRING title, inf, section;
    UNICODE_STRING backupdir, backupfile;
    HRESULT hr;

859
    TRACE("(%p, %s, %s, %s, %s, %s, %d)\n", hWnd, debugstr_a(pszTitle),
860 861
          debugstr_a(pszINF), debugstr_a(pszSection), debugstr_a(pszBackupDir),
          debugstr_a(pszBaseBackupFile), dwFlags);
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883

    RtlCreateUnicodeStringFromAsciiz(&title, pszTitle);
    RtlCreateUnicodeStringFromAsciiz(&inf, pszINF);
    RtlCreateUnicodeStringFromAsciiz(&section, pszSection);
    RtlCreateUnicodeStringFromAsciiz(&backupdir, pszBackupDir);
    RtlCreateUnicodeStringFromAsciiz(&backupfile, pszBaseBackupFile);

    hr = FileSaveRestoreOnINFW(hWnd, title.Buffer, inf.Buffer, section.Buffer,
                               backupdir.Buffer, backupfile.Buffer, dwFlags);

    RtlFreeUnicodeString(&title);
    RtlFreeUnicodeString(&inf);
    RtlFreeUnicodeString(&section);
    RtlFreeUnicodeString(&backupdir);
    RtlFreeUnicodeString(&backupfile);

    return hr;
}

/***********************************************************************
 *      FileSaveRestoreOnINFW (ADVPACK.@)
 *
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
 *
 * PARAMS
 *   hWnd              [I] Handle to the window used for the display.
 *   pszTitle          [I] Title of the window.
 *   pszINF            [I] Fully-qualified INF filename.
 *   pszSection        [I] GenInstall INF section name.
 *   pszBackupDir      [I] Directory to store the backup file.
 *   pszBaseBackupFile [I] Basename of the backup files.
 *   dwFlags           [I] See advpub.h
 *
 * RETURNS
 *   Success: S_OK.
 *   Failure: E_FAIL.
 *
 * NOTES
 *   If pszSection is NULL, the default section will be used.
 *
 * BUGS
 *   Unimplemented.
 */
904 905 906
HRESULT WINAPI FileSaveRestoreOnINFW(HWND hWnd, LPCWSTR pszTitle, LPCWSTR pszINF,
                                     LPCWSTR pszSection, LPCWSTR pszBackupDir,
                                     LPCWSTR pszBaseBackupFile, DWORD dwFlags)
907
{
908
    FIXME("(%p, %s, %s, %s, %s, %s, %d): stub\n", hWnd, debugstr_w(pszTitle),
909 910
          debugstr_w(pszINF), debugstr_w(pszSection), debugstr_w(pszBackupDir),
          debugstr_w(pszBaseBackupFile), dwFlags);
911 912 913

    return E_FAIL;
}
914 915

/***********************************************************************
916
 *             GetVersionFromFileA     (ADVPACK.@)
917
 *
918
 * See GetVersionFromFileExW.
919
 */
920
HRESULT WINAPI GetVersionFromFileA(LPCSTR Filename, LPDWORD MajorVer,
921 922
                                   LPDWORD MinorVer, BOOL Version )
{
923
    TRACE("(%s, %p, %p, %d)\n", debugstr_a(Filename), MajorVer, MinorVer, Version);
924
    return GetVersionFromFileExA(Filename, MajorVer, MinorVer, Version);
925 926
}

927 928 929 930 931 932 933 934 935 936 937 938
/***********************************************************************
 *             GetVersionFromFileW     (ADVPACK.@)
 *
 * See GetVersionFromFileExW.
 */
HRESULT WINAPI GetVersionFromFileW(LPCWSTR Filename, LPDWORD MajorVer,
                                   LPDWORD MinorVer, BOOL Version )
{
    TRACE("(%s, %p, %p, %d)\n", debugstr_w(Filename), MajorVer, MinorVer, Version);
    return GetVersionFromFileExW(Filename, MajorVer, MinorVer, Version);
}

939 940 941 942 943 944 945 946
/* data for GetVersionFromFileEx */
typedef struct tagLANGANDCODEPAGE
{
    WORD wLanguage;
    WORD wCodePage;
} LANGANDCODEPAGE;

/***********************************************************************
947
 *             GetVersionFromFileExA   (ADVPACK.@)
948
 *
949 950 951 952 953 954 955 956
 * See GetVersionFromFileExW.
 */
HRESULT WINAPI GetVersionFromFileExA(LPCSTR lpszFilename, LPDWORD pdwMSVer,
                                     LPDWORD pdwLSVer, BOOL bVersion )
{
    UNICODE_STRING filename;
    HRESULT res;

957 958
    TRACE("(%s, %p, %p, %d)\n", debugstr_a(lpszFilename),
          pdwMSVer, pdwLSVer, bVersion);
959 960 961 962 963 964 965 966 967 968 969 970 971

    RtlCreateUnicodeStringFromAsciiz(&filename, lpszFilename);

    res = GetVersionFromFileExW(filename.Buffer, pdwMSVer, pdwLSVer, bVersion);

    RtlFreeUnicodeString(&filename);

    return res;
}

/***********************************************************************
 *             GetVersionFromFileExW   (ADVPACK.@)
 *
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
 * Gets the files version or language information.
 *
 * PARAMS
 *   lpszFilename [I] The file to get the info from.
 *   pdwMSVer     [O] Major version.
 *   pdwLSVer     [O] Minor version.
 *   bVersion     [I] Whether to retrieve version or language info.
 *
 * RETURNS
 *   Always returns S_OK.
 *
 * NOTES
 *   If bVersion is TRUE, version information is retrieved, else
 *   pdwMSVer gets the language ID and pdwLSVer gets the codepage ID.
 */
987
HRESULT WINAPI GetVersionFromFileExW(LPCWSTR lpszFilename, LPDWORD pdwMSVer,
988 989 990 991 992
                                     LPDWORD pdwLSVer, BOOL bVersion )
{
    VS_FIXEDFILEINFO *pFixedVersionInfo;
    LANGANDCODEPAGE *pLangAndCodePage;
    DWORD dwHandle, dwInfoSize;
993 994
    WCHAR szWinDir[MAX_PATH];
    WCHAR szFile[MAX_PATH];
995 996 997 998
    LPVOID pVersionInfo = NULL;
    BOOL bFileCopied = FALSE;
    UINT uValueLen;

999 1000
    static const WCHAR backslash[] = {'\\',0};
    static const WCHAR translation[] = {
1001 1002 1003 1004 1005 1006
        '\\','V','a','r','F','i','l','e','I','n','f','o',
        '\\','T','r','a','n','s','l','a','t','i','o','n',0
    };

    TRACE("(%s, %p, %p, %d)\n", debugstr_w(lpszFilename),
          pdwMSVer, pdwLSVer, bVersion);
1007 1008 1009 1010

    *pdwLSVer = 0;
    *pdwMSVer = 0;

1011
    lstrcpynW(szFile, lpszFilename, MAX_PATH);
1012

1013
    dwInfoSize = GetFileVersionInfoSizeW(szFile, &dwHandle);
1014 1015 1016
    if (!dwInfoSize)
    {
        /* check that the file exists */
1017
        if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES)
1018 1019 1020 1021 1022
            return S_OK;

        /* file exists, but won't be found by GetFileVersionInfoSize,
        * so copy it to the temp dir where it will be found.
        */
1023 1024 1025
        GetWindowsDirectoryW(szWinDir, MAX_PATH);
        GetTempFileNameW(szWinDir, NULL, 0, szFile);
        CopyFileW(lpszFilename, szFile, FALSE);
1026 1027
        bFileCopied = TRUE;

1028
        dwInfoSize = GetFileVersionInfoSizeW(szFile, &dwHandle);
1029 1030 1031 1032 1033 1034 1035 1036
        if (!dwInfoSize)
            goto done;
    }

    pVersionInfo = HeapAlloc(GetProcessHeap(), 0, dwInfoSize);
    if (!pVersionInfo)
        goto done;

1037
    if (!GetFileVersionInfoW(szFile, dwHandle, dwInfoSize, pVersionInfo))
1038 1039 1040 1041
        goto done;

    if (bVersion)
    {
1042
        if (!VerQueryValueW(pVersionInfo, backslash,
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
            (LPVOID *)&pFixedVersionInfo, &uValueLen))
            goto done;

        if (!uValueLen)
            goto done;

        *pdwMSVer = pFixedVersionInfo->dwFileVersionMS;
        *pdwLSVer = pFixedVersionInfo->dwFileVersionLS;
    }
    else
    {
1054
        if (!VerQueryValueW(pVersionInfo, translation,
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
             (LPVOID *)&pLangAndCodePage, &uValueLen))
            goto done;

        if (!uValueLen)
            goto done;

        *pdwMSVer = pLangAndCodePage->wLanguage;
        *pdwLSVer = pLangAndCodePage->wCodePage;
    }

done:
    HeapFree(GetProcessHeap(), 0, pVersionInfo);

    if (bFileCopied)
1069
        DeleteFileW(szFile);
1070 1071 1072

    return S_OK;
}