#include "stdafx.h" #include #include #include #include #include #include #include "cabfunc.h" #include "resource.h" #include "filestuff.h" #include "fdi.h" BOOL test_fdi(char *cabinet_fullpath); //static char dest_dir[MAX_PATH]; BOOL explode_cab(char *cabinet_fullpath, char *destination) { char cab_path[MAX_PATH]; strcpy(dest_dir, destination); strcpy(cab_path, cabinet_fullpath); return test_fdi(cabinet_fullpath); } //--------------------------------------------------------------------------- // The following definitions are pulled from stat.h and types.h. I was having // trouble getting the build tree make to pull them in, so I just copied the // relevant stuff. This may very well need to be changed if those include // files change. //--------------------------------------------------------------------------- #define _S_IREAD 0000400 /* read permission, owner */ #define _S_IWRITE 0000200 /* write permission, owner */ //--------------------------------------------------------------------------- // All of these functions may be called by the unCAB library. //--------------------------------------------------------------------------- FNALLOC(mem_alloc) { return malloc(cb); } FNFREE(mem_free) { free(pv); } FNOPEN(file_open) { return _open(pszFile, oflag, pmode); } FNREAD(file_read) { return _read((int)hf, pv, cb); } FNWRITE(file_write) { return _write((int)hf, pv, cb); } FNCLOSE(file_close) { return _close((int)hf); } FNSEEK(file_seek) { return _lseek((int)hf, dist, seektype); } BOOL DoesFileExist(char *szFile) { USES_CONVERSION; HANDLE handle; handle = CreateFile(A2T(szFile), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); return TRUE; } return FALSE; } //--------------------------------------------------------------------------- // This callback will be called by the unCAB library. //--------------------------------------------------------------------------- FNFDINOTIFY(notification_function) { switch (fdint) { case fdintCABINET_INFO: // general information about the cabinet return 0; case fdintPARTIAL_FILE: // first file in cabinet is continuation return 0; case fdintCOPY_FILE: // file to be copied { int handle = -1; char destination[MAX_PATH]; char szBuffer[MAX_PATH], *szFile; // We no longer want to create a directory tree as we expand the files. // TSHOOT.EXE, which is used to open some of the files in the CAB, likes // to find the files in a nice flat directory. Now we will need to handle // name collisions (although there probably won't be many). If we are // asked to create a second file with the same name as an existing file, // we'll prefix the subdirectory name and an underscore to the new file. if (pfdin->psz1 == NULL) return 0; strcpy(szBuffer, pfdin->psz1); szFile = strrchr(szBuffer, '\\'); while (handle == -1 && szFile != NULL) { sprintf(destination, "%s\\%s", dest_dir, szFile + 1); if (!DoesFileExist(destination)) handle = (int)file_open(destination, _O_BINARY | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL, _S_IREAD | _S_IWRITE); else { *szFile = '_'; szFile = strrchr(szBuffer, '\\'); } } if (handle == -1) { sprintf(destination, "%s\\%s", dest_dir, szBuffer); handle = (int)file_open(destination, _O_BINARY | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL, _S_IREAD | _S_IWRITE); } return handle; // Old code which created the directory structure: #if FALSE // If the file we are supposed to create contains path information, // we may need to create the directories before create the file. char szDir[MAX_PATH], szSubDir[MAX_PATH], *p; p = strrchr(pfdin->psz1, '\\'); if (p) { strncpy(szSubDir, pfdin->psz1, MAX_PATH); p = strchr(szSubDir, '\\'); while (p != NULL) { *p = '\0'; sprintf(szDir, "%s\\%s", dest_dir, szSubDir); CreateDirectory(szDir, NULL); *p = '\\'; p = strchr(p+1, '\\'); } } sprintf(destination, "%s\\%s", dest_dir, pfdin->psz1); #endif } case fdintCLOSE_FILE_INFO: // close the file, set relevant info { HANDLE handle; DWORD attrs; TCHAR destination[MAX_PATH]; _stprintf(destination, _T("%s\\%s"), dest_dir, pfdin->psz1); file_close(pfdin->hf); // Need to use Win32 type handle to set date/time handle = CreateFile(destination, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle != INVALID_HANDLE_VALUE) { FILETIME datetime; if (TRUE == DosDateTimeToFileTime(pfdin->date, pfdin->time, &datetime)) { FILETIME local_filetime; if (TRUE == LocalFileTimeToFileTime(&datetime, &local_filetime)) { (void) SetFileTime(handle, &local_filetime, NULL, &local_filetime); } } CloseHandle(handle); } // Mask out attribute bits other than readonly, // hidden, system, and archive, since the other // attribute bits are reserved for use by // the cabinet format. attrs = pfdin->attribs; // Actually, let's mask out hidden as well. // attrs &= (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH); attrs &= (_A_RDONLY | _A_SYSTEM | _A_ARCH); (void) SetFileAttributes(destination, attrs); return TRUE; } case fdintNEXT_CABINET: // file continued to next cabinet return 0; default: return 0; } } /**/ //--------------------------------------------------------------------------- // This function is used to actually expand the files in a CAB. //--------------------------------------------------------------------------- BOOL test_fdi(char *cabinet_fullpath) { HFDI hfdi; ERF erf; FDICABINETINFO fdici; int hf; char *p; char cabinet_name[256]; char cabinet_path[256]; hfdi = FDICreate(mem_alloc, mem_free, file_open, file_read, file_write, file_close, file_seek, cpu80386, &erf); if (hfdi == NULL) { return FALSE; } hf = (int)file_open(cabinet_fullpath, _O_BINARY | _O_RDONLY | _O_SEQUENTIAL, 0); if (hf == -1) { (void) FDIDestroy(hfdi); // printf("Unable to open '%s' for input\n", cabinet_fullpath); return FALSE; } if (FALSE == FDIIsCabinet(hfdi, hf, &fdici)) { _close(hf); (void) FDIDestroy(hfdi); return FALSE; } else { _close(hf); } p = strrchr(cabinet_fullpath, '\\'); if (p == NULL) { strcpy(cabinet_name, cabinet_fullpath); strcpy(cabinet_path, ""); } else { strcpy(cabinet_name, p+1); strncpy(cabinet_path, cabinet_fullpath, (int) (p-cabinet_fullpath)+1); cabinet_path[ (int) (p-cabinet_fullpath)+1 ] = 0; } if (TRUE != FDICopy(hfdi, cabinet_name, cabinet_path, 0, notification_function, NULL, NULL)) { (void) FDIDestroy(hfdi); return FALSE; } if (FDIDestroy(hfdi) != TRUE) return FALSE; return TRUE; } /* * UNI2UTF -- Unicode to/from UTF conversions * * Copyright (C) 1996, Microsoft Corporation. All rights reserved. * * History: * * 10-14-96 msliger Created. * 12-16-99 moved to .cpp from uni2utf.c * combined with code from cabfunc.c */ /* * Unicode2UTF() * * This function converts any 0-terminated Unicode string to the corresponding * nul-terminated UTF string, and returns a pointer to the resulting string. * The pointer references a single, static internal buffer, so the result will * be destroyed on the next call. * * If the generated UTF string would be too long, NULL is returned. */ char *Unicode2UTF(const wchar_t *unicode) { static char utfbuffer[MAX_UTF_LENGTH + 4]; int utfindex = 0; while (*unicode != 0) { if (utfindex >= MAX_UTF_LENGTH) { return(NULL); } if (*unicode < 0x0080) { utfbuffer[utfindex++] = (char) *unicode; } else if (*unicode < 0x0800) { utfbuffer[utfindex++] = (char) (0xC0 + (*unicode >> 6)); utfbuffer[utfindex++] = (char) (0x80 + (*unicode & 0x3F)); } else { utfbuffer[utfindex++] = (char) (0xE0 + (*unicode >> 12)); utfbuffer[utfindex++] = (char) (0x80 + ((*unicode >> 6) & 0x3F)); utfbuffer[utfindex++] = (char) (0x80 + (*unicode & 0x3F)); } unicode++; } utfbuffer[utfindex] = '\0'; return(utfbuffer); } /* * UTF2Unicode() * * This function converts a valid UTF string into the corresponding unicode string, * and returns a pointer to the resulting unicode. The pointer references a single, * internal static buffer, which will be destroyed on the next call. * * If the generated unicode string would be too long, or if the UTF string contains * any illegal UTF values, NULL is returned. */ wchar_t *UTF2Unicode(const char *utfString) { static wchar_t unicodebuffer[MAX_UNICODE_LENGTH + 1]; int unicodeindex = 0; int c; while (*utfString != 0) { if (unicodeindex >= MAX_UNICODE_LENGTH) { return(NULL); } c = (*utfString++ & 0x00FF); if (c < 0x0080) { unicodebuffer[unicodeindex] = (unsigned short) c; } else if (c < 0x00C0) { return(NULL); /* 0x0080..0x00BF illegal */ } else if (c < 0x00E0) { unicodebuffer[unicodeindex] = (unsigned short) ((c & 0x001F) << 6); c = (*utfString++ & 0x00FF); if ((c < 0x0080) || (c > 0x00BF)) { return(NULL); /* trail must be 0x0080..0x00BF */ } unicodebuffer[unicodeindex] |= (c & 0x003F); } else if (c < 0x00F0) { unicodebuffer[unicodeindex] = (unsigned short) ((c & 0x000F) << 12); c = (*utfString++ & 0x00FF); if ((c < 0x0080) || (c > 0x00BF)) { return(NULL); /* trails must be 0x0080..0x00BF */ } unicodebuffer[unicodeindex] |= ((c & 0x003F) << 6); c = (*utfString++ & 0x00FF); if ((c < 0x0080) || (c > 0x00BF)) { return(NULL); /* trails must be 0x0080..0x00BF */ } unicodebuffer[unicodeindex] |= (c & 0x003F); } else { return(NULL); /* lead can't be > 0x00EF */ } unicodeindex++; } unicodebuffer[unicodeindex] = 0; return(unicodebuffer); } //--------------------------------------------------------------------------- // This function will expand the specified CAB file, putting all of the // files in the specified destination directory. //--------------------------------------------------------------------------- BOOL OpenCABFile(const CString & filename, const CString & destination) { char szFilebuffer[MAX_UTF_LENGTH]; char szDestination[MAX_UTF_LENGTH]; // If the filename has Unicode characters which can't be represented // directly by ANSI characters, we need to make a copy of it to the // temp directory, and give it an ANSI name. The code we've borrowed // for expanding CAB files can't open Unicode named files. BOOL fNonANSICharacter = FALSE; const TCHAR * pChar = (LPCTSTR) filename; while (pChar && *pChar) if (*pChar++ >= (TCHAR)0x0080) { fNonANSICharacter = TRUE; break; } CString strFilename(filename); BOOL fMadeCopy = FALSE; if (fNonANSICharacter) { TCHAR szNewFile[MAX_PATH + 10]; DWORD dwLength = 0; dwLength = ::GetTempPath(MAX_PATH, szNewFile); if (dwLength != 0 && dwLength < MAX_PATH) { _tcscat(szNewFile, _T("msitemp.cab")); fMadeCopy = ::CopyFile((LPCTSTR) strFilename, szNewFile, FALSE); strFilename = szNewFile; } } BOOL fResult = FALSE; //TD: better way to handle UNICODE vs non-unicode strings? // for Unicode system char * szFilename = Unicode2UTF((wchar_t*)(LPCTSTR)strFilename); if (szFilename) { ::strcpy(szFilebuffer, szFilename); szFilename = Unicode2UTF((wchar_t*)(LPCTSTR)destination); if (szFilename) { ::strcpy(szDestination, szFilename); fResult = explode_cab(szFilebuffer, szDestination); } } //for non-Unicode system if (!fResult) { fResult = explode_cab((char *)(LPCTSTR) filename, (char *)(LPCTSTR) destination); } // If we made a copy of the CAB file, we should delete it now. if (fMadeCopy) ::DeleteFile((LPCTSTR)strFilename); return fResult; } //--------------------------------------------------------------------------- // This function looks in the specified directory for an NFO file. If it // finds one, it assigns it to filename and returns TRUE. This function // will only find the first NFO file in a directory. // // If an NFO file cannot be found, then we'll look for another file type // to open. Grab the string entry in the registry = "cabdefaultopen". An // example value would be "*.nfo|hwinfo.dat|*.dat|*.txt" which would be // interpreted as follows: // // 1. First look for any NFO file to open. // 2. Then try to open a file called "hwinfo.dat". // 3. Then try to open any file with a DAT extension. // 4. Then try for any TXT file. // 5. Finally, if none of these can be found, present an open dialog // to the user. //--------------------------------------------------------------------------- LPCTSTR VAL_CABDEFAULTOPEN = _T("cabdefaultopen"); LPCTSTR cszDirSeparator = _T("\\"); BOOL IsDataspecFilePresent(CString strCabExplodedDir) { CStringList filesfound; DirectorySearch(_T("dataspec.xml"), strCabExplodedDir, filesfound); if (filesfound.GetHeadPosition() != NULL) { return TRUE; } return FALSE; } BOOL IsIncidentXMLFilePresent(CString strCabExplodedDir, CString strIncidentFileName) { CStringList filesfound; DirectorySearch(strIncidentFileName, strCabExplodedDir, filesfound); if (filesfound.GetHeadPosition() != NULL) { return TRUE; } return FALSE; } BOOL FindFileToOpen(const CString & destination, CString & filename) { CString strCABDefaultOpen, strRegBase, strDirectory; HKEY hkey; filename.Empty(); strDirectory = destination; if (strDirectory.Right(1) != CString(cszDirSeparator)) strDirectory += CString(cszDirSeparator); // Set up a fallback string of the NFO file type, in case we can't // find the registry entry. strCABDefaultOpen.LoadString(IDS_DEFAULTEXTENSION); strCABDefaultOpen = CString("*.") + strCABDefaultOpen; // Load the string of files and file types to open from the registry. strRegBase.LoadString(IDS_MSI_REG_BASE); if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, strRegBase, 0, KEY_READ, &hkey) == ERROR_SUCCESS) { char szData[MAX_PATH]; DWORD dwType, dwSize = MAX_PATH; if (RegQueryValueEx(hkey, VAL_CABDEFAULTOPEN, NULL, &dwType, (LPBYTE) szData, &dwSize) == ERROR_SUCCESS) if (dwType == REG_SZ) strCABDefaultOpen = szData; RegCloseKey(hkey); } // Look through each of the potential files and file types. If we find // a match, return TRUE after setting filename appropriately. Note that // we need to recurse down through directories. CString strFileSpec; CStringList filesfound; POSITION pos; while (!strCABDefaultOpen.IsEmpty()) { if (strCABDefaultOpen.Find('|') == -1) strFileSpec = strCABDefaultOpen; else strFileSpec = strCABDefaultOpen.Left(strCABDefaultOpen.Find('|')); filesfound.RemoveAll(); DirectorySearch(strFileSpec, strDirectory, filesfound); pos = filesfound.GetHeadPosition(); if (pos != NULL) { filename = filesfound.GetNext(pos); return TRUE; } strCABDefaultOpen = strCABDefaultOpen.Right(strCABDefaultOpen.GetLength() - strFileSpec.GetLength()); if (strCABDefaultOpen.Find('|') == 0) strCABDefaultOpen = strCABDefaultOpen.Right(strCABDefaultOpen.GetLength() - 1); } //a-kjaw ////Look for incident.xml file. It has to be an unicode file. strCABDefaultOpen = _T("*.XML"); TCHAR pBuf[MAX_PATH]; WCHAR pwBuf[MAX_PATH]; HANDLE handle; DWORD dw; while (!strCABDefaultOpen.IsEmpty()) { if (strCABDefaultOpen.Find('|') == -1) strFileSpec = strCABDefaultOpen; else strFileSpec = strCABDefaultOpen.Left(strCABDefaultOpen.Find('|')); filesfound.RemoveAll(); DirectorySearch(strFileSpec, strDirectory, filesfound); pos = filesfound.GetHeadPosition(); while (pos != NULL) { filename = filesfound.GetNext(pos); handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == handle) continue; ReadFile(handle , pBuf , 1 , &dw , NULL); if( pBuf[0] == _T('<')) { do { ReadFile(handle , pBuf , _tcslen(_T("MachineID")) * sizeof(TCHAR) , &dw , NULL); if(_tcsicmp(pBuf , _T("MachineID")) == 0) { CloseHandle( handle ); return TRUE; } else { SetFilePointer(handle , (1 - _tcslen(_T("MachineID")) )* sizeof(TCHAR) , 0 , FILE_CURRENT ); } }while( dw == _tcslen(_T("MachineID")) ); } else //Unicode? { ReadFile(handle , pwBuf , 1 , &dw , NULL); do { ReadFile(handle , pwBuf , lstrlenW(L"MachineID") * sizeof(WCHAR) , &dw , NULL); pwBuf[ lstrlenW(L"MachineID") ] = L'\0'; if(lstrcmpiW(pwBuf , L"MachineID") == 0) { CloseHandle( handle ); return TRUE; } else { SetFilePointer(handle , (1 - lstrlenW(L"MachineID"))* sizeof(WCHAR) , 0 , FILE_CURRENT ); } }while( dw == _tcslen(_T("MachineID")) * sizeof(WCHAR) ); } CloseHandle( handle ); } strCABDefaultOpen = strCABDefaultOpen.Right(strCABDefaultOpen.GetLength() - strFileSpec.GetLength()); if (strCABDefaultOpen.Find('|') == 0) strCABDefaultOpen = strCABDefaultOpen.Right(strCABDefaultOpen.GetLength() - 1); } return FALSE; } //--------------------------------------------------------------------------- // DirectorySearch is used to locate all of the files in a directory or // one of its subdirectories which match a file spec. //--------------------------------------------------------------------------- void DirectorySearch(const CString & strSpec, const CString & strDir, CStringList &results) { // Look for all of the files which match the file spec in the directory // specified by strDir. WIN32_FIND_DATA finddata; CString strSearch, strDirectory; strDirectory = strDir; if (strDirectory.Right(1) != CString(cszDirSeparator)) strDirectory += CString(cszDirSeparator); strSearch = strDirectory + strSpec; HANDLE hFind = FindFirstFile(strSearch, &finddata); if (hFind != INVALID_HANDLE_VALUE) { do { results.AddHead(strDirectory + CString(finddata.cFileName)); } while (FindNextFile(hFind, &finddata)); FindClose(hFind); } // Now call this function recursively, with each of the subdirectories. strSearch = strDirectory + CString(_T("*")); hFind = FindFirstFile(strSearch, &finddata); if (hFind != INVALID_HANDLE_VALUE) { do { if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (::_tcscmp(finddata.cFileName, _T(".")) != 0 && ::_tcscmp(finddata.cFileName, _T("..")) != 0) DirectorySearch(strSpec, strDirectory + CString(finddata.cFileName), results); } while (FindNextFile(hFind, &finddata)); FindClose(hFind); } } //--------------------------------------------------------------------------- // This function gets the directory in which to put exploded CAB files. // This will be the same directory each time, so this function will create // the directory (if necessary) and delete any files in the directory. //--------------------------------------------------------------------------- BOOL GetCABExplodeDir(CString &destination, BOOL fDeleteFiles, const CString & strDontDelete) { CString strMSInfoDir, strExplodeTo, strSubDirName; // Determine the temporary path and add on a subdir name. TCHAR szTempDir[MAX_PATH]; if (::GetTempPath(MAX_PATH, szTempDir) > MAX_PATH) { destination = _T(""); return FALSE; } strSubDirName.LoadString(IDS_CAB_DIR_NAME); strExplodeTo = szTempDir; if (strExplodeTo.Right(1) == CString(cszDirSeparator)) strExplodeTo = strExplodeTo + strSubDirName; else strExplodeTo = strExplodeTo + CString(cszDirSeparator) + strSubDirName; // Kill the directory if it already exists. if (fDeleteFiles) KillDirectory(strExplodeTo, strDontDelete); // Create the subdirectory. if (!CreateDirectoryEx(szTempDir, strExplodeTo, NULL)) { if (GetLastError() != ERROR_ALREADY_EXISTS) { // MSIError(IDS_GENERAL_ERROR, "couldn't create the target directory"); destination = ""; return FALSE; } } destination = strExplodeTo; return TRUE; } //--------------------------------------------------------------------------- // This functions kills a directory by recursively deleting files and // subdirectories. //--------------------------------------------------------------------------- void KillDirectory(const CString & strDir, const CString & strDontDelete) { CString strDirectory = strDir; if (strDirectory.Right(1) == CString(cszDirSeparator)) strDirectory = strDirectory.Left(strDirectory.GetLength() - 1); // Delete any files in directory. CString strFilesToDelete = strDirectory + CString(_T("\\*.*")); CString strDeleteFile; WIN32_FIND_DATA filedata; BOOL bFound = TRUE; HANDLE hFindFile = FindFirstFile(strFilesToDelete, &filedata); while (hFindFile != INVALID_HANDLE_VALUE && bFound) { if ((filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0L) { strDeleteFile = strDirectory + CString(cszDirSeparator) + filedata.cFileName; if (strDontDelete.CompareNoCase(strDeleteFile) != 0) { ::SetFileAttributes(strDeleteFile, FILE_ATTRIBUTE_NORMAL); ::DeleteFile(strDeleteFile); } } bFound = FindNextFile(hFindFile, &filedata); } FindClose(hFindFile); // Now call this function on any subdirectories in this directory. CString strSearch = strDirectory + CString(_T("\\*")); hFindFile = FindFirstFile(strSearch, &filedata); if (hFindFile != INVALID_HANDLE_VALUE) { do { if (filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (::_tcscmp(filedata.cFileName, _T(".")) != 0 && ::_tcscmp(filedata.cFileName, _T("..")) != 0) KillDirectory(strDirectory + CString(cszDirSeparator) + CString(filedata.cFileName)); } while (FindNextFile(hFindFile, &filedata)); FindClose(hFindFile); } // Finally, remove this directory. ::RemoveDirectory(strDirectory); }