/* * 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 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "windef.h" #include "winbase.h" #include "winuser.h" #include "winreg.h" #include "winver.h" #include "setupapi.h" #include "advpub.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(advpack); /*********************************************************************** * AddDelBackupEntry (ADVPACK.@) * * Either appends the files in the file list to the backup section of * the specified INI, or deletes the entries from the INI file. * * PARAMS * lpcszFileList [I] NULL-separated list of filenames. * lpcszBackupDir [I] Path of the backup directory. * 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. * * RETURNS * Success: S_OK. * Failure: E_FAIL. * * 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. * * BUGS * Unimplemented. */ HRESULT WINAPI AddDelBackupEntry(LPCSTR lpcszFileList, LPCSTR lpcszBackupDir, LPCSTR lpcszBaseName, DWORD dwFlags) { FIXME("(%p, %p, %p, %ld) stub\n", lpcszFileList, lpcszBackupDir, lpcszBaseName, dwFlags); return E_FAIL; } /* FIXME: this is only for the local case, X:\ */ #define ROOT_LENGTH 3 UINT CALLBACK pQuietQueueCallback(PVOID Context, UINT Notification, UINT_PTR Param1, UINT_PTR Param2) { return 1; } UINT CALLBACK pQueueCallback(PVOID Context, UINT Notification, UINT_PTR Param1, UINT_PTR Param2) { /* only be verbose for error notifications */ if (!Notification || Notification == SPFILENOTIFY_RENAMEERROR || Notification == SPFILENOTIFY_DELETEERROR || Notification == SPFILENOTIFY_COPYERROR) { return SetupDefaultQueueCallbackA(Context, Notification, Param1, Param2); } return 1; } /*********************************************************************** * AdvInstallFile (ADVPACK.@) * * 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. */ HRESULT WINAPI AdvInstallFile(HWND hwnd, LPCSTR lpszSourceDir, LPCSTR lpszSourceFile, LPCSTR lpszDestDir, LPCSTR lpszDestFile, DWORD dwFlags, DWORD dwReserved) { PSP_FILE_CALLBACK_A pFileCallback; LPSTR szPath, szDestFilename; char szRootPath[ROOT_LENGTH]; DWORD dwLen, dwLastError; HSPFILEQ fileQueue; PVOID pContext; TRACE("(%p,%p,%p,%p,%p,%ld,%ld)\n", hwnd, debugstr_a(lpszSourceDir), debugstr_a(lpszSourceFile), debugstr_a(lpszDestDir), debugstr_a(lpszDestFile), dwFlags, dwReserved); if (!lpszSourceDir || !lpszSourceFile || !lpszDestDir) return E_INVALIDARG; fileQueue = SetupOpenFileQueue(); if (fileQueue == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32(GetLastError()); pContext = NULL; dwLastError = ERROR_SUCCESS; lstrcpynA(szRootPath, lpszSourceDir, ROOT_LENGTH); szPath = (LPSTR)lpszSourceDir + ROOT_LENGTH; /* use lpszSourceFile as destination filename if lpszDestFile is NULL */ if (lpszDestFile) { dwLen = lstrlenA(lpszDestFile); szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen); lstrcpyA(szDestFilename, lpszDestFile); } else { dwLen = lstrlenA(lpszSourceFile); szDestFilename = HeapAlloc(GetProcessHeap(), 0, dwLen); lstrcpyA(szDestFilename, lpszSourceFile); } /* add the file copy operation to the setup queue */ if (!SetupQueueCopyA(fileQueue, szRootPath, szPath, lpszSourceFile, NULL, 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 */ if (!SetupCommitFileQueueA(hwnd, fileQueue, pFileCallback, pContext)) { dwLastError = GetLastError(); goto done; } done: SetupTermDefaultQueueCallback(pContext); SetupCloseFileQueue(fileQueue); HeapFree(GetProcessHeap(), 0, szDestFilename); return HRESULT_FROM_WIN32(dwLastError); } static HRESULT DELNODE_recurse_dirtree(LPSTR fname, DWORD flags) { DWORD fattrs = GetFileAttributesA(fname); HRESULT ret = E_FAIL; if (fattrs & FILE_ATTRIBUTE_DIRECTORY) { HANDLE hFindFile; WIN32_FIND_DATAA w32fd; BOOL done = TRUE; int fname_len = lstrlenA(fname); /* Generate a path with wildcard suitable for iterating */ if (CharPrevA(fname, fname + fname_len) != "\\") { lstrcpyA(fname + fname_len, "\\"); ++fname_len; } lstrcpyA(fname + fname_len, "*"); if ((hFindFile = FindFirstFileA(fname, &w32fd)) != INVALID_HANDLE_VALUE) { /* Iterate through the files in the directory */ for (done = FALSE; !done; done = !FindNextFileA(hFindFile, &w32fd)) { TRACE("%s\n", w32fd.cFileName); if (lstrcmpA(".", w32fd.cFileName) != 0 && lstrcmpA("..", w32fd.cFileName) != 0) { lstrcpyA(fname + fname_len, w32fd.cFileName); 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) { TRACE("%s: directory\n", fname); if (SetFileAttributesA(fname, FILE_ATTRIBUTE_NORMAL) && RemoveDirectoryA(fname)) { ret = S_OK; } } } else { TRACE("%s: file\n", fname); if (SetFileAttributesA(fname, FILE_ATTRIBUTE_NORMAL) && DeleteFileA(fname)) { ret = S_OK; } } return ret; } /*********************************************************************** * DelNode (ADVPACK.@) * * 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. */ HRESULT WINAPI DelNode( LPCSTR pszFileOrDirName, DWORD dwFlags ) { CHAR fname[MAX_PATH]; HRESULT ret = E_FAIL; TRACE("(%s, 0x%08lx)\n", debugstr_a(pszFileOrDirName), dwFlags); if (dwFlags) FIXME("Flags ignored!\n"); if (pszFileOrDirName && *pszFileOrDirName) { lstrcpyA(fname, pszFileOrDirName); /* TODO: Should check for system directory deletion etc. here */ ret = DELNODE_recurse_dirtree(fname, dwFlags); } return ret; } /* returns the parameter at dwIndex in a list of parameters * separated by the cSeparator character */ static LPSTR get_parameter(LPSTR szParameters, CHAR cSeparator, DWORD dwIndex) { LPSTR szParam = NULL; DWORD i = 0; while (*szParameters && i < dwIndex) { if (*szParameters == cSeparator) i++; szParameters++; } if (!*szParameters) return NULL; szParam = HeapAlloc(GetProcessHeap(), 0, lstrlenA(szParameters)); lstrcpyA(szParam, szParameters); return szParam; } /*********************************************************************** * DelNodeRunDLL32 (ADVPACK.@) * * 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. */ HRESULT WINAPI DelNodeRunDLL32( HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show ) { LPSTR szFilename, szFlags; DWORD dwFlags; HRESULT res; TRACE("(%s)\n", debugstr_a(cmdline)); /* get the parameters at indexes 0 and 1 respectively */ szFilename = get_parameter(cmdline, ',', 0); szFlags = get_parameter(cmdline, ',', 1); dwFlags = atol(szFlags); res = DelNode(szFilename, dwFlags); HeapFree(GetProcessHeap(), 0, szFilename); HeapFree(GetProcessHeap(), 0, szFlags); return res; } /* The following defintions were copied from dlls/cabinet/cabinet.h */ /* EXTRACTdest flags */ #define EXTRACT_FILLFILELIST 0x00000001 #define EXTRACT_EXTRACTFILES 0x00000002 struct ExtractFileList { LPSTR filename; struct ExtractFileList *next; BOOL unknown; /* always 1L */ } ; /* the first parameter of the function Extract */ typedef struct { long result1; /* 0x000 */ long unknown1[3]; /* 0x004 */ struct ExtractFileList *filelist; /* 0x010 */ long filecount; /* 0x014 */ DWORD flags; /* 0x018 */ char directory[0x104]; /* 0x01c */ char lastfile[0x20c]; /* 0x120 */ } EXTRACTdest; static HRESULT (WINAPI *pExtract)(EXTRACTdest*, LPCSTR); /* 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; char *first = (char *)FileList; char *last = (char *)FileList + strlen(FileList) - 1; 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'; szConvertedList[dwLen] = '\0'; /* empty list */ if (!lstrlenA(szConvertedList)) return NULL; *dwNumFiles = 1; /* convert the colons to double-null termination */ temp = szConvertedList; while (*temp) { if (*temp == ':') { *temp = '\0'; (*dwNumFiles)++; } temp++; } return szConvertedList; } static void free_file_node(struct ExtractFileList *pNode) { HeapFree(GetProcessHeap(), 0, pNode->filename); HeapFree(GetProcessHeap(), 0, pNode); } /* determines whether szFile is in the NULL-separated szFileList */ static BOOL file_in_list(LPSTR szFile, LPSTR szFileList) { 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; } /* removes nodes from the linked list that aren't specified in szFileList * returns the number of files that are in both the linked list and szFileList */ static DWORD fill_file_list(EXTRACTdest *extractDest, LPCSTR szCabName, LPSTR szFileList) { DWORD dwNumFound = 0; struct ExtractFileList *pNode; struct ExtractFileList *prev = NULL; extractDest->flags |= EXTRACT_FILLFILELIST; if (pExtract(extractDest, szCabName)) { extractDest->flags &= ~EXTRACT_FILLFILELIST; return -1; } pNode = extractDest->filelist; while (pNode) { if (file_in_list(pNode->filename, szFileList)) { prev = pNode; pNode = pNode->next; dwNumFound++; } else if (prev) { prev->next = pNode->next; free_file_node(pNode); pNode = prev->next; } else { extractDest->filelist = pNode->next; free_file_node(pNode); pNode = extractDest->filelist; } } extractDest->flags &= ~EXTRACT_FILLFILELIST; return dwNumFound; } /*********************************************************************** * ExtractFiles (ADVPACK.@) * * 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. */ HRESULT WINAPI ExtractFiles ( LPCSTR CabName, LPCSTR ExpandDir, DWORD Flags, LPCSTR FileList, LPVOID LReserved, DWORD Reserved) { EXTRACTdest extractDest; HMODULE hCabinet; HRESULT res = S_OK; DWORD dwFileCount = 0; DWORD dwFilesFound = 0; LPSTR szConvertedList = NULL; TRACE("(%p %p %ld %p %p %ld)\n", CabName, ExpandDir, Flags, FileList, LReserved, Reserved); 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; } ZeroMemory(&extractDest, sizeof(EXTRACTdest)); lstrcpyA(extractDest.directory, ExpandDir); if (FileList) { szConvertedList = convert_file_list(FileList, &dwFileCount); if (!szConvertedList || dwFileCount == -1) { res = E_FAIL; goto done; } dwFilesFound = fill_file_list(&extractDest, CabName, szConvertedList); if (dwFilesFound != dwFileCount) { res = E_FAIL; goto done; } } else extractDest.flags |= EXTRACT_FILLFILELIST; extractDest.flags |= EXTRACT_EXTRACTFILES; res = pExtract(&extractDest, CabName); done: FreeLibrary(hCabinet); HeapFree(GetProcessHeap(), 0, szConvertedList); return res; } /*********************************************************************** * FileSaveMarkNotExist (ADVPACK.@) * * 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. * pszBaseName [I] Basename of the INI file. * * RETURNS * Success: S_OK. * Failure: E_FAIL. */ HRESULT WINAPI FileSaveMarkNotExist(LPSTR pszFileList, LPSTR pszDir, LPSTR pszBaseName) { TRACE("(%p, %p, %p)\n", pszFileList, pszDir, pszBaseName); return AddDelBackupEntry(pszFileList, pszDir, pszBaseName, AADBE_DEL_ENTRY); } /*********************************************************************** * FileSaveRestore (ADVPACK.@) * * 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. */ HRESULT WINAPI FileSaveRestore(HWND hDlg, LPSTR pszFileList, LPSTR pszDir, LPSTR pszBaseName, DWORD dwFlags) { FIXME("(%p, %p, %p, %p, %ld) stub\n", hDlg, pszFileList, pszDir, pszBaseName, dwFlags); return E_FAIL; } /*********************************************************************** * FileSaveRestoreOnINF (ADVPACK.@) * * * 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. */ HRESULT WINAPI FileSaveRestoreOnINF(HWND hWnd, PCSTR pszTitle, PCSTR pszINF, PCSTR pszSection, PCSTR pszBackupDir, PCSTR pszBaseBackupFile, DWORD dwFlags) { FIXME("(%p, %p, %p, %p, %p, %p, %ld) stub\n", hWnd, pszTitle, pszINF, pszSection, pszBackupDir, pszBaseBackupFile, dwFlags); return E_FAIL; } /*********************************************************************** * GetVersionFromFile (ADVPACK.@) * * See GetVersionFromFileEx. */ HRESULT WINAPI GetVersionFromFile( LPSTR Filename, LPDWORD MajorVer, LPDWORD MinorVer, BOOL Version ) { TRACE("(%s, %p, %p, %d)\n", Filename, MajorVer, MinorVer, Version); return GetVersionFromFileEx(Filename, MajorVer, MinorVer, Version); } /* data for GetVersionFromFileEx */ typedef struct tagLANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; } LANGANDCODEPAGE; /*********************************************************************** * GetVersionFromFileEx (ADVPACK.@) * * 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. */ HRESULT WINAPI GetVersionFromFileEx( LPSTR lpszFilename, LPDWORD pdwMSVer, LPDWORD pdwLSVer, BOOL bVersion ) { VS_FIXEDFILEINFO *pFixedVersionInfo; LANGANDCODEPAGE *pLangAndCodePage; DWORD dwHandle, dwInfoSize; CHAR szWinDir[MAX_PATH]; CHAR szFile[MAX_PATH]; LPVOID pVersionInfo = NULL; BOOL bFileCopied = FALSE; UINT uValueLen; TRACE("(%s, %p, %p, %d)\n", lpszFilename, pdwMSVer, pdwLSVer, bVersion); *pdwLSVer = 0; *pdwMSVer = 0; lstrcpynA(szFile, lpszFilename, MAX_PATH); dwInfoSize = GetFileVersionInfoSizeA(szFile, &dwHandle); if (!dwInfoSize) { /* check that the file exists */ if (GetFileAttributesA(szFile) == INVALID_FILE_ATTRIBUTES) return S_OK; /* file exists, but won't be found by GetFileVersionInfoSize, * so copy it to the temp dir where it will be found. */ GetWindowsDirectoryA(szWinDir, MAX_PATH); GetTempFileNameA(szWinDir, NULL, 0, szFile); CopyFileA(lpszFilename, szFile, FALSE); bFileCopied = TRUE; dwInfoSize = GetFileVersionInfoSizeA(szFile, &dwHandle); if (!dwInfoSize) goto done; } pVersionInfo = HeapAlloc(GetProcessHeap(), 0, dwInfoSize); if (!pVersionInfo) goto done; if (!GetFileVersionInfoA(szFile, dwHandle, dwInfoSize, pVersionInfo)) goto done; if (bVersion) { if (!VerQueryValueA(pVersionInfo, "\\", (LPVOID *)&pFixedVersionInfo, &uValueLen)) goto done; if (!uValueLen) goto done; *pdwMSVer = pFixedVersionInfo->dwFileVersionMS; *pdwLSVer = pFixedVersionInfo->dwFileVersionLS; } else { if (!VerQueryValueA(pVersionInfo, "\\VarFileInfo\\Translation", (LPVOID *)&pLangAndCodePage, &uValueLen)) goto done; if (!uValueLen) goto done; *pdwMSVer = pLangAndCodePage->wLanguage; *pdwLSVer = pLangAndCodePage->wCodePage; } done: HeapFree(GetProcessHeap(), 0, pVersionInfo); if (bFileCopied) DeleteFileA(szFile); return S_OK; }