Windows2000/private/inet/urlmon/download/extract.c
2020-09-30 17:12:32 +02:00

965 lines
21 KiB
C

/*
* EXTRACT.C borrowed from TWEX\wextract.c
* Has the CAB extraction capabilty for Code Downloader; uses FDI.LIB
*/
#include <urlmon.h>
#include <io.h>
#include <stdio.h>
#include <fcntl.h>
#include "cdl.h"
#ifdef unix
#include "unixfile.h"
#endif /* unix */
// single theaded access to the FDI lib
static BOOL fCritCreated = FALSE;
CRITICAL_SECTION g_mxsFDI;
/*
* W i n 3 2 O p e n ( )
* Routine: Win32Open()
* Purpose: Translate a C-Runtime _open() call into appropriate Win32
* CreateFile()
* Returns: Handle to file on success
* INVALID_HANDLE_VALUE on failure
* BUGBUG: Doesn't fully implement C-Runtime _open() capability but it
* BUGBUG: currently supports all callbacks that FDI will give us
*/
HANDLE
Win32Open(char *pszFile, int oflag, int pmode )
{
HANDLE FileHandle = INVALID_HANDLE_VALUE;
BOOL fExists = FALSE;
DWORD fAccess;
DWORD fCreate;
ASSERT( pszFile );
// BUGBUG: No Append Mode Support
if (oflag & _O_APPEND)
return( INVALID_HANDLE_VALUE );
// Set Read-Write Access
if ((oflag & _O_RDWR) || (oflag & _O_WRONLY))
fAccess = GENERIC_WRITE;
else
fAccess = GENERIC_READ;
// Set Create Flags
if (oflag & _O_CREAT) {
if (oflag & _O_EXCL)
fCreate = CREATE_NEW;
else if (oflag & _O_TRUNC)
fCreate = CREATE_ALWAYS;
else
fCreate = OPEN_ALWAYS;
} else {
if (oflag & _O_TRUNC)
fCreate = TRUNCATE_EXISTING;
else
fCreate = OPEN_EXISTING;
}
#ifdef unix
UnixEnsureDir(pszFile);
#endif /* unix */
//BUGBUG: seterrormode to no crit errors and then catch sharing violations and access denied
// Call Win32
FileHandle = CreateFile(pszFile, fAccess, FILE_SHARE_READ, NULL, fCreate, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
if (FileHandle == INVALID_HANDLE_VALUE && SetFileAttributes(pszFile, FILE_ATTRIBUTE_NORMAL))
FileHandle = CreateFile(pszFile, fAccess, FILE_SHARE_READ, NULL, fCreate, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
return( FileHandle );
}
/*
* O p e n F u n c ( )
* Routine: OpenFunc()
* Purpose: Open File Callback from FDI
* Returns: File Handle (small integer index into file table)
* -1 on failure
*/
int FAR DIAMONDAPI openfunc(char FAR *pszFile, int oflag, int pmode )
{
int rc;
HANDLE hf;
ASSERT( pszFile );
//BUGBUG Spill File Support for Quantum?
if ((*pszFile == '*') && (*(pszFile+1) != 'M')) {
DEBUGTRAP("Spill File Support for Quantum Not Supported");
}
hf = Win32Open(pszFile, oflag, pmode );
if (hf != INVALID_HANDLE_VALUE) {
// SUNDOWN: typecast problem
rc = PtrToLong(hf);
} else {
rc = -1;
}
return( rc );
}
/*
* R E A D F U N C ( )
* Routine: readfunc()
* Purpose: FDI read() callback
*/
UINT FAR DIAMONDAPI readfunc(int hf, void FAR *pv, UINT cb)
{
int rc;
ASSERT( pv );
if (! ReadFile((HANDLE)hf, pv, cb, (DWORD *) &cb, NULL))
rc = -1;
else
rc = cb;
return( rc );
}
/*
* W r i t e F u n c ( )
* Routine: WriteFunc()
* Purpose: FDI Write() callback
*/
UINT FAR DIAMONDAPI
writefunc(int hf, void FAR *pv, UINT cb)
{
int rc;
ASSERT( pv );
if (! WriteFile((HANDLE)hf, pv, cb, (DWORD *) &cb, NULL))
rc = -1;
else
rc = cb;
// BUGBUG: implement OnProgress notification
return( rc );
}
/*
* C l o s e F u n c ( )
* Routine: CloseFunc()
* Purpose: FDI Close File Callback
*/
int FAR DIAMONDAPI closefunc( int hf )
{
int rc;
if (CloseHandle( (HANDLE)hf )) {
rc = 0;
} else {
rc = -1;
}
return( rc );
}
/*
* S e e k F u n c ( )
* Routine: seekfunc()
* Purpose: FDI Seek Callback
*/
long FAR DIAMONDAPI seekfunc( int hf, long dist, int seektype )
{
long rc;
DWORD W32seektype;
switch (seektype) {
case SEEK_SET:
W32seektype = FILE_BEGIN;
break;
case SEEK_CUR:
W32seektype = FILE_CURRENT;
break;
case SEEK_END:
W32seektype = FILE_END;
break;
}
rc = SetFilePointer((HANDLE)hf, dist, NULL, W32seektype);
if (rc == 0xffffffff)
rc = -1;
return( rc );
}
/*
* A d j u s t F i l e T i m e ( )
* Routine: AdjustFileTime()
* Purpose: Change the time info for a file
*/
BOOL
AdjustFileTime(INT_PTR hf, USHORT date, USHORT time )
{
FILETIME ft;
FILETIME ftUTC;
if (! DosDateTimeToFileTime( date, time, &ft ))
return( FALSE );
if (! LocalFileTimeToFileTime(&ft, &ftUTC))
return( FALSE );
if (! SetFileTime((HANDLE)hf,&ftUTC,&ftUTC,&ftUTC))
return( FALSE );
return( TRUE );
}
/*
* A t t r 3 2 F r o m A t t r F A T ( )
* Translate FAT attributes to Win32 Attributes
*/
DWORD Attr32FromAttrFAT(WORD attrMSDOS)
{
//** Quick out for normal file special case
if (attrMSDOS == _A_NORMAL) {
return FILE_ATTRIBUTE_NORMAL;
}
//** Otherwise, mask off read-only, hidden, system, and archive bits
// NOTE: These bits are in the same places in MS-DOS and Win32!
return attrMSDOS & ~(_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH);
}
/*
* A l l o c F u n c ( )
* FDI Memory Allocation Callback
*/
FNALLOC(allocfunc)
{
void *pv;
pv = (void *) CoTaskMemAlloc( cb );
DEBUGMSG("%d = ALLOC( %d )", (DWORD_PTR) pv, cb );
return( pv );
}
/*
* F r e e F u n c ( )
* FDI Memory Deallocation Callback
* XXX Return Value?
*/
FNFREE(freefunc)
{
ASSERT(pv);
DEBUGMSG("FREE( %d )", pv );
CoTaskMemFree( pv );
}
/*
* D O G E T N E X T C A B ( )
* Routine: doGetNextCab()
* Purpose: Get Next Cabinet in chain
* Returns: -1
* BUGBUG: CLEANUP: STUB THIS OUT
* BUGBUG: STUBBED OUT IN WEXTRACT - CHAINED CABINETS NOT SUPPORTED
*/
FNFDINOTIFY(doGetNextCab)
{
return( -1 );
}
/*** updateCabinetInfo - update history of cabinets seen
* Entry:
* psess - Session
* pfdin - FDI info structurue
* Exit:
* Returns 0;
*/
int updateCabinetInfo(PSESSION psess, PFDINOTIFICATION pfdin)
{
ASSERT(psess);
// Don't need any of this!
//** Save cabinet info
//lstrcpy(psess->acab.achCabPath ,pfdin->psz3);
//lstrcpy(psess->acab.achCabFilename ,pfdin->psz1);
//lstrcpy(psess->acab.achDiskName ,pfdin->psz2);
//psess->acab.setID = pfdin->setID;
//psess->acab.iCabinet = pfdin->iCabinet;
return 0;
}
/*
* A P P E N D P A T H S E P A R A T O R ( )
* Routine: appendPathSeparator()
* Purpose: Append a path separator only if necessary
* Returns: TRUE - Path Separator Added
* FALSE No Path Separator added
*/
BOOL
appendPathSeparator(char *pszPathEnd)
{
//** Add path separator if necessary
if ((*pszPathEnd != '\0') && // Path is not empty
(*pszPathEnd != chPATH_SEP1) && // Not a path separator
(*pszPathEnd != chPATH_SEP2) && // Not a path separator
(*pszPathEnd != chDRIVE_SEP) ) { // Not a drive separator
#ifdef unix
*(++pszPathEnd) = chPATH_SEP2; // Add Unix path separator
#else
*(++pszPathEnd) = chPATH_SEP1; // Add path separator
#endif /* !unix */
*(++pszPathEnd) = '\0'; // Terminate path
return TRUE; // Account for path separator
}
//** No separator added
return FALSE;
}
/*
* C A T D I R A N D F I L E ( )
* Routine: catDirAndFile()
* Purpose: Concatenate a directory with a filename!
*/
BOOL
catDirAndFile( char *pszResult,
int cbResult,
char *pszDir,
char *pszFile
)
{
int cch = 0;
//** Handle directory
pszResult[0] = '\0'; // No filespec, yet
if (pszDir)
cch = lstrlen(pszDir); // Get length of dir
if (cch != 0) { // Have to concatenate path
lstrcpy(pszResult,pszDir); // Copy destination dir to buffer
cbResult -= cch; // Account for dir
//** Add path separator if necessary, adjust remaining size
cbResult -= appendPathSeparator(&(pszResult[cch-1]));
if (cbResult <= 0) {
return FALSE;
}
}
//** Append file name, using default if primary one not supplied
if (*pszFile == '\0') {
return( FALSE );
}
lstrcat(pszResult,pszFile); // Append file name
cbResult -= lstrlen(pszFile); // Update remaining size
if (cbResult <= 0) {
return FALSE;
}
//** Success
return TRUE;
}
/*
* IsExtracted
* Look for pszName in psess->pFileList and see if extracted
* Returns:
* Success: TRUE, failure: FALSE
*/
static
BOOL
IsExtracted( PSESSION ps, LPCSTR pszName)
{
PFNAME CurName = ps->pFileList;
ASSERT(pszName);
ASSERT(CurName); // atleast one file needed
// search for filename in list of files in this CAB
do {
if (lstrcmpi(pszName, CurName->pszFilename) == 0) {
if (CurName->status == SFNAME_EXTRACTED)
return TRUE;
else
return FALSE;
}
} while (CurName = CurName->pNextName);
ASSERT(TRUE); // if here not found in list!
return FALSE;
}
/*
* NeedFile
* search for pszName in psess->pFilesToExtract (list of PFNAMEs)
* Returns:
* TRUE - need file, extract it
* FALSE - don't need file, skip it
*/
static
BOOL
NeedFile( PSESSION ps, LPCSTR pszName)
{
PFNAME CurName;
ASSERT(pszName);
if (IsExtracted(ps, pszName) )
return FALSE;
if ( ps->flags & SESSION_FLAG_EXTRACT_ALL )
return TRUE;
// search for filename in list of files needed
for (CurName = ps->pFilesToExtract; CurName; CurName = CurName->pNextName){
ASSERT(CurName->pszFilename);
if (lstrcmpi(CurName->pszFilename, pszName) == 0)
return TRUE;
}
return FALSE;
}
/*
* MarkExtracted
* Look for pszName in psess->pFileList and mark status = status_passed_in
* really can be use to mark status as anything else as well (not just
* extracted.)
* Returns:
* Success: TRUE, failure: FALSE
*/
static
BOOL
MarkExtracted( PSESSION ps, LPCSTR pszName , DWORD status)
{
PFNAME CurName = ps->pFileList;
ASSERT(pszName);
ASSERT(CurName); // atleast one file needed
// search for filename in list of files in this CAB
do {
if (lstrcmpi(pszName, CurName->pszFilename) == 0) {
CurName->status = status;
return TRUE;
}
} while (CurName = CurName->pNextName);
ASSERT(TRUE); // if here not found in list!
return FALSE;
}
/*
* A d d F i l e ( )
* Add a file to the list of files we have in the CAB file
* Singly linked list - items added at front
*/
static
BOOL
AddFile( PSESSION ps, LPCSTR pszName , long cb)
{
PFNAME NewName;
ASSERT(pszName);
if (!(ps->flags & SESSION_FLAG_ENUMERATE))
return TRUE;
// Allocate Node
NewName = (PFNAME) CoTaskMemAlloc(sizeof(FNAME) );
if (NewName == NULL) {
DEBUGMSG("AddFile(): Memory Allocation of structure failed");
return( FALSE );
}
// Allocate String Space
NewName->pszFilename = (LPSTR) CoTaskMemAlloc(lstrlen(pszName) + 1);
if (NewName->pszFilename == NULL) {
DEBUGMSG("AddFile(): Memory Allocation of name failed");
return( FALSE );
}
NewName->status = SFNAME_INIT;
// Copy Filename
lstrcpy( (char *)NewName->pszFilename, pszName );
// Link into list
NewName->pNextName = ps->pFileList;
ps->pFileList = NewName;
ps->cFiles++;
ps->cbCabSize += cb;
return( TRUE );
}
/*
* f d i N o t i f y E x t r a c t()
* Routine: fdiNotifyExtract()
* Purpose: Principle FDI Callback in file extraction
*/
FNFDINOTIFY(fdiNotifyExtract)
{
int fh;
PSESSION psess;
psess = (PSESSION) pfdin->pv;
switch (fdint) {
case fdintCABINET_INFO:
return updateCabinetInfo(psess,pfdin);
case fdintCOPY_FILE:
// BUGBUG: implement OnProgress?
#ifdef unix
UnixifyFileName(pfdin->psz1);
#endif /* unix */
if (!catDirAndFile(psess->achFile, // Buffer for output filespec
sizeof(psess->achFile), // Size of output buffer
psess->achLocation, // Output directory
pfdin->psz1)) {
return -1; // Abort with error;
}
// always add the file (enumeration)
if (! AddFile(psess, pfdin->psz1, pfdin->cb))
return( -1 );
// check if this is the file we are looking for if any
if (!NeedFile(psess, pfdin->psz1))
return( 0 );
if (StrStrA(pfdin->psz1, "\\")) {
// cab contains dir struct for this file
// create struct on dest dir as well.
char *pBaseFileName = NULL;
char szDir[MAX_PATH];
LPSTR pchStart;
LPSTR pchSlash = NULL;
lstrcpy(szDir, psess->achFile);
pchStart = szDir + lstrlen(psess->achLocation) + 1;
while (*pchStart && (pchSlash = StrStrA(pchStart, "\\"))) {
*pchSlash = '\0';
// don't care if this fails. may even already exist!
CreateDirectory(szDir, NULL);
*pchSlash = '\\';
pchStart = pchSlash +1;
}
}
//** Do overwrite processing
fh = openfunc( psess->achFile, _O_BINARY | _O_TRUNC | _O_RDWR |
_O_CREAT, 0 );
return(fh); // -1 if error on open
case fdintCLOSE_FILE_INFO:
if (!catDirAndFile(psess->achFile, // Buffer for output filespec
sizeof(psess->achFile), // Size of output buffer
psess->achLocation, // Output directory
pfdin->psz1)) {
return -1; // Abort with error;
}
if (! AdjustFileTime( pfdin->hf, pfdin->date, pfdin->time )) {
return( -1 );
}
closefunc( (int) pfdin->hf );
if (! SetFileAttributes(psess->achFile, Attr32FromAttrFAT(pfdin->attribs)))
return( -1 );
MarkExtracted(psess, pfdin->psz1, SFNAME_EXTRACTED);
return(TRUE);
case fdintPARTIAL_FILE:
return( 0 );
case fdintNEXT_CABINET:
return doGetNextCab(fdint,pfdin);
default:
DEBUGTRAP("fdiNotifyExtract(): Unknown Callback Type");
break;
}
return( 0 );
}
#ifdef DEBUG
/*
* V E R I F Y C A B I N E T ( )
* Routine: VerifyCabinet()
* Purpose: Check that cabinet is properly formed
* Returns: TRUE - Cabinet OK
* FALSE - Cabinet invalid
*/
BOOL
VerifyCabinet( PSESSION psess, LPCSTR lpCabName )
{
HFDI hfdi;
ERF erf;
FDICABINETINFO cabinfo;
INT fh;
hfdi = FDICreate(allocfunc,freefunc,openfunc,readfunc,writefunc,closefunc,seekfunc,cpu80386,&erf);
if (hfdi == NULL) {
DEBUGMSG("VerifyCabinet(): FDICreate() Failed");
//BUGBUG Error Handling?
return( FALSE );
}
fh = openfunc((char FAR *)lpCabName, _O_BINARY | _O_RDONLY, 0 );
if (fh == -1) {
DEBUGMSG("VerifyCabinet(): Open of Memory File Failed");
return( FALSE );
}
if (FDIIsCabinet(hfdi, fh, &cabinfo ) == FALSE) {
DEBUGMSG("VerifyCabinet(): FDIIsCabinet() Returned FALSE");
return( FALSE );
}
if (cabinfo.cbCabinet != (long) psess->cbCabSize) {
DEBUGMSG("VerifyCabinet(): cabinfo.cbCabinet != cbCabSize");
return( FALSE );
}
if (cabinfo.hasprev || cabinfo.hasnext) {
DEBUGMSG("VerifyCabinet(): Cabinet is chained");
return( FALSE );
}
if (closefunc( fh ) == -1) {
DEBUGMSG("VerifyCabinet(): Closefunc() Failed");
return( FALSE );
}
if (FDIDestroy(hfdi) == FALSE) {
DEBUGMSG("VerifyCabinet(): FDIDestroy() Failed");
return( FALSE );
}
return( TRUE );
}
#endif /* DEBUG */
/*
* E X T R A C T ( )
* Routine: Extract()
* Parameters:
* PSESSION ps = session information tied to this extract session
* IN params
* ps->pFilesToExtract = linked list of PFNAMEs that point to
* upper case filenames that need extraction
* ps->flags SESSION_FLAG_ENUMERATE = whether need to enumerate
* files in CAB (ie. create a pFileList
* ps->flags SESSION_FLAG_EXTRACTALL = all
* OUT params
* ps->pFileList = global alloced list of files in CAB
* caller needs to call DeleteExtractedFiles
* to free memory and temp files
* LPCSTR lpCabName = name of cab file
* Returns:
* S_OK: sucesss
*/
HRESULT
Extract(PSESSION ps, LPCSTR lpCabName )
{
HFDI hfdi;
BOOL fExtractResult = FALSE;
HRESULT hr = S_OK;
if (ps->flags & SESSION_FLAG_EXTRACTED_ALL) {
// already extracted all files in this CAB
// nothing to do!
return S_OK;
}
memset(&(ps->erf), 0, sizeof(ERF));
if (ps->flags & SESSION_FLAG_ENUMERATE) {
ps->cbCabSize = 0;
}
// don't enumerate when pFileList already pre-populated
ASSERT( (!(ps->flags & SESSION_FLAG_ENUMERATE)) || (!ps->pFileList));
{
HRESULT hrOut = NOERROR;
if (fCritCreated == FALSE)
{
fCritCreated = TRUE;
InitializeCriticalSection(&g_mxsFDI);
}
EnterCriticalSection(&g_mxsFDI);
// Extract the files
hfdi = FDICreate(allocfunc,freefunc,openfunc,readfunc,writefunc,closefunc,seekfunc,cpu80386, &(ps->erf));
if (hfdi == NULL) {
// Error value will be retrieved from ps->erf
hrOut = STG_E_UNKNOWN;
goto done;
}
fExtractResult = FDICopy(hfdi, (char FAR *)lpCabName, "", 0, fdiNotifyExtract, NULL, (void *) ps );
if (FDIDestroy(hfdi) == FALSE) {
// Error value will be retrieved from ps->erf
hrOut = STG_E_UNKNOWN;
}
done:
LeaveCriticalSection(&g_mxsFDI);
// leave now if this failed!
if (hrOut != NOERROR)
{
return hrOut;
}
}
if (fExtractResult && (!ps->erf.fError))
return S_OK;
hr = HRESULT_FROM_WIN32(GetLastError());
if (SUCCEEDED(hr)) {
// not a win32 failure but a cabinet failure
// convert CABINET failure to disk full or STG_E_UNKNOWN.
// On win95 writefile failing with disk full is not
// setting the last error correctly
if (ps->erf.fError && (ps->erf.erfOper == FDIERROR_TARGET_FILE))
hr = HRESULT_FROM_WIN32(ERROR_DISK_FULL);
else
hr = STG_E_UNKNOWN;
}
return hr;
}
/*
* D E L E T E E X T R A C T E D F I L E S ( )
* Routine: DeleteExtractedFiles()
* Purpose: Delete the files that were extracted
* into the temporary directory
* FREE all the memory in pFileList
* make pFileList = NULL.
* Paramaters:
* psess - Pointer to Session Structure containing
* all state about this extraction session
* Returns: None
*/
VOID
DeleteExtractedFiles(PSESSION psess)
{
PFNAME rover = psess->pFileList;
PFNAME roverprev;
char szBuf[MAX_PATH];
ASSERT(psess);
DEBUGMSG("Deleting Extracted Files");
while (rover != NULL) {
// skip if this is not a tmp file
if ( rover->status == SFNAME_EXTRACTED) {
// Get full filename
if (catDirAndFile(szBuf, MAX_PATH, psess->achLocation,
rover->pszFilename)) {
if (SetFileAttributes(szBuf, FILE_ATTRIBUTE_NORMAL))
DeleteFile(szBuf);
}
}
CoTaskMemFree(rover->pszFilename);
roverprev = rover; // save for free'ing current rover below
rover = rover->pNextName;
CoTaskMemFree(roverprev);
}
psess->pFileList = NULL; // prevent use after deletion!
}