mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3236 lines
80 KiB
3236 lines
80 KiB
/*
|
|
Enhanced NCSA Mosaic from Spyglass
|
|
"Guitar"
|
|
|
|
Copyright 1994 Spyglass, Inc.
|
|
All Rights Reserved
|
|
|
|
Author(s):
|
|
Albert Lee [email protected]
|
|
*/
|
|
|
|
#include "all.h"
|
|
#include "oharever.h"
|
|
#include "history.h"
|
|
#include <intshcut.h> //for InetIsOffline()
|
|
|
|
/* version at which we changed cif file format */
|
|
#define VER_PRODUCTVERSION_CIFCHANGE_1 (0x04400000 | 216)
|
|
|
|
#define STATE_FILE_STREAMINIT (STATE_OTHER + 1)
|
|
#define STATE_FILE_COPYING (STATE_OTHER + 2)
|
|
#define MAX_KEY_LEN 25
|
|
|
|
/* Set dcache logging on by default in Debug builds */
|
|
#if defined(TEST_DCACHE_OPTIONS) || defined(XX_DEBUG)
|
|
#ifndef DCACHE_LOG
|
|
#define DCACHE_LOG
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef DCACHE_LOG
|
|
#define DCACHE_LOG_CIF 1
|
|
#define DCACHE_LOG_OTHER 2
|
|
#define DCACHE_LOG_CLOSE 3
|
|
#define DCACHE_LOG_FOUND 4
|
|
#define DCACHE_LOG_PRESORT 5
|
|
#define DCACHE_LOG_SORT 6
|
|
#define DCACHE_LOG_NOFMT 7
|
|
#endif
|
|
|
|
/* Need this to possibly map to notcp.htm */
|
|
extern BOOL bNetwork;
|
|
|
|
struct _HTStream
|
|
{
|
|
CONST HTStreamClass *isa;
|
|
/* ... */
|
|
};
|
|
|
|
static BOOL bInitialized = FALSE; /* Flag indicating initialization */
|
|
static struct hash_table *pFileHash=NULL; /* Hash table containing file substitution rules (doesn't change, ex. on cdrom) */
|
|
static struct hash_table *pDynFileHash=NULL;/* Hash table containing file substitution rules (changes each time we access/save to disk cache) */
|
|
static struct hash_table *pAuxImgHash=NULL; /* Hash table for "obsolete" images */
|
|
static struct CacheRuleList *pRuleList=NULL;/* Linked list of rules */
|
|
static struct CDRomList *pCDRomList=NULL; /* List containing drive substitution rules */
|
|
static struct CDRomList *pCDRomAlias=NULL; /* Alias list containing user-defined substitution rules */
|
|
/* V,pszPrductName,dwProductVer */
|
|
const char cszCIFLineFmtV[]="V,%s,%lu\n";
|
|
const char cszProductName[]=VER_PRODUCTNAME_STR;
|
|
const char cszAppCIF[] = "iexplore.cif";
|
|
static HANDLE hCIFMutex = NULL; /* lock when updating cif info */
|
|
static HANDLE hDCacheMutex = NULL; /* lock when cleaning up dcache dir */
|
|
static HANDLE hDCacheStartEvent = NULL; /* Event signaled when start dcache cleaned up */
|
|
static HANDLE hDCacheDoneEvent = NULL; /* Event signaled when finish dcache cleaned up */
|
|
static HANDLE hDCacheThread = NULL; /* dcache cleanup done in separate thread */
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
static BOOL fCheckDCacheOverflow=TRUE; /* debug option: don't check for dcache overflow */
|
|
#endif
|
|
|
|
static BOOL bStopCleaning = FALSE; // Flag that cache cleanup honors in order to defer to
|
|
// main thread for CIF mutex
|
|
static void ReadDynCIF(void);
|
|
static void WriteDynCIF(void);
|
|
static BOOL FGetAppCIF(PSTR pszAppCIF, BOOL fCreate);
|
|
static int ProcessIndexFile(char *pFilename, BOOL fDynamic);
|
|
static void CleanupDCacheLocal(UINT uFlags);
|
|
static BOOL FDCacheOverflow(UINT uFlags);
|
|
static DWORD DwDirSize(PCSTR pcszDir, DWORD dwClusterSize);
|
|
DWORD WINAPI DwCleanupDCacheProc(LPUINT lpUFlags);
|
|
#ifdef DCACHE_LOG
|
|
static void WriteDCacheLog(PCSTR pcszFn, UINT uFlags);
|
|
#else
|
|
#define WriteDCacheLog(psz, fCif)
|
|
#endif
|
|
static void PropagateFreshnessToCif(void);
|
|
static void InitConnectState(void);
|
|
|
|
#ifdef FEATURE_INTL
|
|
BOOL HasDBCSchar(int codepage, LPCTSTR string, int count)
|
|
{
|
|
while(count-- > 0)
|
|
if (IsDBCSLeadByteEx(codepage, *string++)) return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
InitializeDiskCache
|
|
|
|
Initializes the local disk cache internal structures.
|
|
This function should be called once when Mosaic starts.
|
|
*/
|
|
|
|
BOOL InitializeDiskCache(void)
|
|
{
|
|
DWORD dwId;
|
|
UINT uFlags=0;
|
|
|
|
if (bInitialized)
|
|
return TRUE;
|
|
|
|
bInitialized = TRUE;
|
|
|
|
#ifdef XX_DEBUG
|
|
if (!gPrefs.bEnableDiskCache)
|
|
XX_Assert(FALSE, ("Disk Caching disabled!"));
|
|
#endif
|
|
|
|
#if 0
|
|
/* Make sure we have a min. amount of space for dcache */
|
|
if (FDCacheOverflow(DCACHE_WATERMARK_LOW))
|
|
{
|
|
ERR_ReportError(NULL, errNoDCacheSpace, NULL, NULL);
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
pFileHash = Hash_Create();
|
|
pDynFileHash = Hash_Create();
|
|
if (!pFileHash || !pDynFileHash)
|
|
{
|
|
if (pFileHash)
|
|
Hash_Destroy(pFileHash);
|
|
if (pDynFileHash)
|
|
Hash_Destroy(pDynFileHash);
|
|
goto LError;
|
|
}
|
|
|
|
if (!(hCIFMutex = CreateMutex(NULL, FALSE, NULL)))
|
|
goto LError;
|
|
if (!(hDCacheMutex = CreateMutex(NULL, FALSE, NULL)))
|
|
goto LErrorClose1;
|
|
hDCacheStartEvent = CreateEvent( NULL, //Default security
|
|
TRUE, //manual reset
|
|
TRUE, //initial state signaled
|
|
NULL); //no name
|
|
if (!hDCacheStartEvent)
|
|
goto LErrorClose2;
|
|
hDCacheDoneEvent = CreateEvent( NULL, //Default security
|
|
TRUE, //manual reset
|
|
TRUE, //initial state signaled
|
|
NULL); //no name
|
|
if (!hDCacheDoneEvent)
|
|
goto LErrorClose3;
|
|
hDCacheThread = CreateThread( NULL, //Default security
|
|
0x1000, //stack size
|
|
DwCleanupDCacheProc,
|
|
&uFlags,
|
|
CREATE_SUSPENDED,
|
|
&dwId);
|
|
if (!hDCacheThread)
|
|
{
|
|
CloseHandle(hDCacheDoneEvent);
|
|
LErrorClose3:
|
|
CloseHandle(hDCacheStartEvent);
|
|
LErrorClose2:
|
|
CloseHandle(hDCacheMutex);
|
|
LErrorClose1:
|
|
CloseHandle(hCIFMutex);
|
|
goto LError;
|
|
}
|
|
|
|
pRuleList = NULL;
|
|
pCDRomList = NULL;
|
|
pCDRomAlias = NULL;
|
|
|
|
#ifdef WIN32
|
|
BuildCDRomList();
|
|
ReadCacheIndexFiles();
|
|
ReadDynCIF();
|
|
#endif
|
|
InitConnectState();
|
|
|
|
if ( hDCacheThread )
|
|
ResumeThread( hDCacheThread );
|
|
|
|
return TRUE;
|
|
|
|
LError:
|
|
hDCacheStartEvent = NULL;
|
|
hDCacheDoneEvent = NULL;
|
|
hCIFMutex = NULL;
|
|
hDCacheMutex = NULL;
|
|
hDCacheThread = NULL;
|
|
gPrefs.bEnableDiskCache = FALSE;
|
|
return TRUE; // Always return true. Just disable persistent caching.
|
|
}
|
|
|
|
/*
|
|
TerminateDiskCache
|
|
|
|
Frees all internal structures. This function should
|
|
be called when Mosaic is shutting down.
|
|
*/
|
|
|
|
void TerminateDiskCache(void)
|
|
{
|
|
struct CacheRuleList *p, *pNext;
|
|
HANDLE hTmpCache;
|
|
|
|
if (!bInitialized)
|
|
return;
|
|
|
|
#ifdef XX_DEBUG
|
|
if (!gPrefs.bEnableDiskCache)
|
|
XX_Assert(FALSE, (""));
|
|
#endif
|
|
|
|
WriteDynCIF();
|
|
|
|
if (pFileHash)
|
|
Hash_Destroy(pFileHash);
|
|
if (pDynFileHash)
|
|
Hash_Destroy(pDynFileHash);
|
|
if (hCIFMutex)
|
|
{
|
|
/* It's ok to check for existence of any one because upon creation,
|
|
* if we fail on any one of them, we release all of them.
|
|
*/
|
|
XX_Assert(hDCacheMutex, (""));
|
|
XX_Assert(hDCacheStartEvent, (""));
|
|
XX_Assert(hDCacheDoneEvent, (""));
|
|
XX_Assert(hDCacheThread, (""));
|
|
/* save this as we will need it */
|
|
hTmpCache = hDCacheThread;
|
|
hDCacheThread = NULL; // tell the thread to close down
|
|
SetEvent(hDCacheStartEvent);
|
|
CloseHandle(hTmpCache);
|
|
|
|
CloseHandle(hCIFMutex);
|
|
CloseHandle(hDCacheMutex);
|
|
CloseHandle(hDCacheStartEvent);
|
|
CloseHandle(hDCacheDoneEvent);
|
|
// TerminateThread(hDCacheThread, 0);
|
|
}
|
|
XX_Assert(!pAuxImgHash, ("pAuxImgHash not empty upon exit!"));
|
|
|
|
#ifdef DCACHE_LOG
|
|
WriteDCacheLog(NULL, DCACHE_LOG_CLOSE); //Close log file if open
|
|
#endif
|
|
|
|
p = pRuleList;
|
|
while (p)
|
|
{
|
|
pNext = p->next;
|
|
if (p->pszOriginal)
|
|
GTR_FREE(p->pszOriginal);
|
|
if (p->pszReplacement)
|
|
GTR_FREE(p->pszReplacement);
|
|
GTR_FREE(p);
|
|
p = pNext;
|
|
}
|
|
}
|
|
|
|
static void ReadDynCIF(void)
|
|
{
|
|
char szCIF[_MAX_PATH+1];
|
|
|
|
if (!FGetAppCIF(szCIF, /*fCreate=*/FALSE))
|
|
return;
|
|
if (FExistsFile(szCIF, FALSE, NULL))
|
|
ProcessIndexFile(szCIF, /*fDynamic=*/TRUE);
|
|
}
|
|
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
static void ReReadDynCIF(void)
|
|
{
|
|
int i, iMax;
|
|
|
|
XX_Assert(hCIFMutex, (""));
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Waiting for hCIFMutex");
|
|
#endif
|
|
WaitForSingleObject(hCIFMutex, INFINITE);
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Got hCIFMutex");
|
|
#endif
|
|
for (i=0, iMax=Hash_Count(pDynFileHash); i<iMax; i++)
|
|
Hash_DeleteIndexedEntry(pDynFileHash, i);
|
|
ReadDynCIF();
|
|
ReleaseMutex(hCIFMutex);
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Released hCIFMutex");
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static BOOL FGetAppCIF(PSTR pszAppCIF, BOOL fCreate)
|
|
{
|
|
char *pszT;
|
|
|
|
if (!FExistsDir(gPrefs.szCacheLocation, fCreate, FALSE))
|
|
return FALSE;
|
|
|
|
XX_Assert(pszAppCIF, ("Null pszAppCIF"));
|
|
strcpy(pszAppCIF, gPrefs.szCacheLocation);
|
|
pszT = pszAppCIF + strlen(pszAppCIF);
|
|
/* Tack on a \ if there isn't already one */
|
|
if (!(pszT > pszAppCIF && *(pszT-1) == chBSlash))
|
|
*pszT++ = chBSlash;
|
|
strcpy(pszT, cszAppCIF);
|
|
return TRUE;
|
|
}
|
|
|
|
static void WriteDynCIF(void)
|
|
{
|
|
/* F,pszURL,lFilesize,
|
|
dctimeLastModif.dwDCacheTime1,
|
|
dctimeLastModif.dwDCacheTime2,
|
|
dctimeExpires.dwDCacheTime1,
|
|
dctimeExpires.dwDCacheTime2,
|
|
dctimeLastUsed.dwDCacheTime1,
|
|
dctimeLastUsed.dwDCacheTime2,
|
|
pszMime,
|
|
pszPathResolved,fNoDocCache */
|
|
const char cszCIFLineFmtF[]="F,%s,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%s,%s,%u\n";
|
|
|
|
char szCIF[_MAX_PATH+1];
|
|
char *pszURL;
|
|
int cLine, i;
|
|
FILE *fp;
|
|
CacheFileInformation *pFileInfo;
|
|
|
|
if ( !pDynFileHash
|
|
|| !FGetAppCIF(szCIF, /*fCreate=*/TRUE))
|
|
return;
|
|
|
|
if (!(fp = fopen(szCIF, "w")))
|
|
return;
|
|
|
|
/* Put in version info */
|
|
fprintf(fp, cszCIFLineFmtV, cszProductName, VER_PRODUCTVERSION_DW);
|
|
|
|
for (i = 0, cLine=Hash_Count(pDynFileHash); i<cLine; i++)
|
|
{
|
|
Hash_GetIndexedEntry(pDynFileHash, i, &pszURL, NULL, (void **) &pFileInfo);
|
|
XX_Assert(pszURL, (""));
|
|
XX_Assert(pFileInfo->pszPath, (""));
|
|
XX_Assert(sizeof(DCACHETIME) == 2*sizeof(DWORD), (""));
|
|
fprintf(fp, cszCIFLineFmtF, pszURL,
|
|
pFileInfo->lFilesize,
|
|
pFileInfo->dctLastModified.dwDCacheTime1,
|
|
pFileInfo->dctLastModified.dwDCacheTime2,
|
|
pFileInfo->dctExpires.dwDCacheTime1,
|
|
pFileInfo->dctExpires.dwDCacheTime2,
|
|
pFileInfo->dctLastUsed.dwDCacheTime1,
|
|
pFileInfo->dctLastUsed.dwDCacheTime2,
|
|
pFileInfo->pszMime ? pFileInfo->pszMime : "", //else fprintf puts "(null)"
|
|
pFileInfo->pszPath,
|
|
pFileInfo->fNoDocCache);
|
|
}
|
|
fclose(fp);
|
|
}
|
|
/*
|
|
FreeCDRomList
|
|
|
|
Frees all memory blocks associated with a CD Rom list.
|
|
BuildCDRomList is platform-specific.
|
|
*/
|
|
|
|
static void FreeCDRomList(struct CDRomList *pList)
|
|
{
|
|
struct CDRomList *p;
|
|
struct CDRomList *pNext;
|
|
|
|
p = pList;
|
|
|
|
while (p)
|
|
{
|
|
pNext = p->next;
|
|
if (p->alias)
|
|
GTR_FREE(p->alias);
|
|
if (p->path)
|
|
GTR_FREE(p->path);
|
|
GTR_FREE(p);
|
|
p = pNext;
|
|
}
|
|
}
|
|
|
|
/*
|
|
ResolveFilename
|
|
|
|
Resolves the file name of the given file, using the CD-Rom
|
|
list and custom drive list.
|
|
|
|
THE CALLER MUST FREE THE RETURNED MEMORY BLOCK.
|
|
*/
|
|
|
|
static char *ResolveFilename(char *pFilename)
|
|
{
|
|
struct CDRomList *pCDRom;
|
|
char *pResolved;
|
|
int pass = 0, i;
|
|
|
|
pCDRom = pCDRomList;
|
|
|
|
DoAgain:
|
|
while (pCDRom)
|
|
{
|
|
if (strncmp(pFilename, pCDRom->alias, strlen(pCDRom->alias)) == 0)
|
|
{
|
|
/* Found a match - replace now */
|
|
|
|
pResolved = GTR_MALLOC(strlen(pFilename) + strlen(pCDRom->path + 1));
|
|
if (!pResolved)
|
|
return NULL;
|
|
|
|
strcpy(pResolved, pCDRom->path);
|
|
strcat(pResolved, &pFilename[strlen(pCDRom->alias)]);
|
|
|
|
/* Replace forward slashes with back slashes */
|
|
|
|
for (i = 0; i < strlen(pResolved); i++)
|
|
{
|
|
if (pResolved[i] == '/')
|
|
pResolved[i] = '\\';
|
|
}
|
|
|
|
return pResolved;
|
|
}
|
|
|
|
pCDRom = pCDRom->next;
|
|
}
|
|
|
|
pass++;
|
|
|
|
if (pass == 1)
|
|
{
|
|
pCDRom = pCDRomAlias;
|
|
goto DoAgain;
|
|
}
|
|
|
|
pResolved = GTR_strdup(pFilename);
|
|
return pResolved;
|
|
}
|
|
|
|
/*
|
|
ProcessIndexFile
|
|
|
|
Processes the specified index file. This function assumes
|
|
that pCDRomList and pCDRomAlias have already been populated
|
|
with the correct CD-Rom entries.
|
|
|
|
Returns 1 if file was processed, 0 if not.
|
|
*/
|
|
|
|
static int ProcessIndexFile(char *pFilename, BOOL fDynamic)
|
|
{
|
|
FILE *fp;
|
|
char *pLine; /* Buffer to hold the line content */
|
|
char szTab[3]; /* used in looking for tab delimiters */
|
|
int ret = 0; /* return value */
|
|
char *p1,*p2,*p3,*p41,*p42,*p51,*p52,*p61,*p62,*p7,*p8,*p9; /* temporary vars for parsing */
|
|
CacheFileInformation *pFileInfo; /* entry into */
|
|
struct CacheRuleList *pRuleInfo; /* rule info */
|
|
char *pResolvedFilename; /* resolved filename */
|
|
struct hash_table *pHash;
|
|
DWORD dwVersion=0;
|
|
|
|
if (fDynamic)
|
|
{
|
|
XX_Assert(pDynFileHash, (""));
|
|
pResolvedFilename = pFilename;
|
|
pHash = pDynFileHash;
|
|
}
|
|
else
|
|
{
|
|
pResolvedFilename = ResolveFilename(pFilename);
|
|
pHash = pFileHash;
|
|
}
|
|
if (!pResolvedFilename)
|
|
return 0;
|
|
|
|
szTab[0] = '\t'; /* tab character */
|
|
szTab[1] = ',';
|
|
szTab[2] = '\0'; /* NULL terminator */
|
|
|
|
pLine = GTR_MALLOC(DCACHE_MAXIMUM_LINE_LENGTH);
|
|
if (!pLine)
|
|
return 0;
|
|
|
|
fp = fopen(pResolvedFilename, "rt");
|
|
if (!fDynamic)
|
|
GTR_FREE(pResolvedFilename);
|
|
|
|
if (!fp)
|
|
{
|
|
GTR_FREE(pLine);
|
|
return 0;
|
|
}
|
|
|
|
while (!feof(fp))
|
|
{
|
|
if (NULL == fgets(pLine, DCACHE_MAXIMUM_LINE_LENGTH, fp))
|
|
break;
|
|
|
|
/* Strip the last character if it's a linefeed */
|
|
|
|
if (pLine[strlen(pLine) - 1] == '\n')
|
|
pLine[strlen(pLine) - 1] = '\0';
|
|
|
|
if (!(p1 = strtok(pLine, szTab)))
|
|
continue;
|
|
p2 = strtok(NULL, szTab);
|
|
p3 = strtok(NULL, szTab);
|
|
|
|
if (!p1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(p1, "F") == 0)
|
|
{
|
|
/* This line contains information about a file substitution */
|
|
|
|
/* BUGBUG: don't ship with this code. Just for upgrading all the beta users */
|
|
#ifdef PRERELEASE_CIF
|
|
if (dwVersion < VER_PRODUCTVERSION_CIFCHANGE_1)
|
|
{
|
|
/* older version of cif file: DCACHETIME was dword */
|
|
p41 = strtok(NULL, szTab);
|
|
p42 = NULL;
|
|
p51 = strtok(NULL, szTab);
|
|
p52 = NULL;
|
|
p61 = strtok(NULL, szTab);
|
|
p62 = NULL;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* new version of cif file: DCACHETIME is 2 dwords */
|
|
p41 = strtok(NULL, szTab);
|
|
p42 = strtok(NULL, szTab);
|
|
p51 = strtok(NULL, szTab);
|
|
p52 = strtok(NULL, szTab);
|
|
p61 = strtok(NULL, szTab);
|
|
p62 = strtok(NULL, szTab);
|
|
}
|
|
p7 = strtok(NULL, szTab);
|
|
p8 = strtok(NULL, szTab);
|
|
p9 = strtok(NULL, szTab);
|
|
|
|
if (!p2 || !p3 || !p41 || !p51 || !p61 || !p7 || !p8 || !p9)
|
|
continue; /* incomplete line */
|
|
|
|
#ifdef PRERELEASE_CIF
|
|
if (dwVersion >= VER_PRODUCTVERSION_CIFCHANGE_1)
|
|
#endif
|
|
{
|
|
if (!p42 || !p52 || !p62)
|
|
continue;
|
|
}
|
|
|
|
pFileInfo = GTR_MALLOC(sizeof(struct _CacheFileInformation));
|
|
memset(pFileInfo, 0, sizeof(struct _CacheFileInformation));
|
|
|
|
pFileInfo->pszMime = GTR_strdup(p7);
|
|
|
|
if (fDynamic)
|
|
{
|
|
/* Don't check if file exists here. Check it in GetResolvedURL */
|
|
pFileInfo->pszPath = GTR_MALLOC(strlen(p8) + 1);
|
|
strcpy(pFileInfo->pszPath, p8);
|
|
}
|
|
else
|
|
{
|
|
if (strchr(p8, '\\') == NULL)
|
|
{
|
|
/* There is no path specifier in the file name. In this case,
|
|
we use the same directory as the index file. */
|
|
|
|
pFileInfo->pszPath = GTR_MALLOC(strlen(p8) + strlen(pResolvedFilename) + 1);
|
|
strcpy(pFileInfo->pszPath, pResolvedFilename);
|
|
/* BUGBUG: What if there are subdirs? */
|
|
*(strrchr(pFileInfo->pszPath, '\\') + 1) = '\0';
|
|
strcat(pFileInfo->pszPath, p8);
|
|
}
|
|
else
|
|
pFileInfo->pszPath = ResolveFilename(p8);
|
|
}
|
|
|
|
pFileInfo->lFilesize = atol(p3);
|
|
|
|
pFileInfo->dctLastModified.dwDCacheTime1 = (DWORD)atol(p41);
|
|
pFileInfo->dctLastModified.dwDCacheTime2 = (p42 ? (DWORD)atol(p42) : 0);
|
|
|
|
pFileInfo->dctExpires.dwDCacheTime1 = (DWORD)atol(p51);
|
|
pFileInfo->dctExpires.dwDCacheTime2 = (p52 ? (DWORD)atol(p52) : 0);
|
|
|
|
pFileInfo->dctLastUsed.dwDCacheTime1 = (DWORD)atol(p61);
|
|
pFileInfo->dctExpires.dwDCacheTime2 = (p62 ? (DWORD)atol(p62) : 0);
|
|
pFileInfo->fNoDocCache = (BOOL)atol(p9);
|
|
|
|
Hash_Add(pHash, p2, NULL, pFileInfo);
|
|
}
|
|
else if (strcmp(p1, "R") == 0)
|
|
{
|
|
/* This line contains information about a rule */
|
|
|
|
if (!p2 || !p3)
|
|
continue; /* incomplete line */
|
|
|
|
pRuleInfo = GTR_MALLOC(sizeof(struct CacheRuleList));
|
|
pRuleInfo->pszOriginal = GTR_strdup(p2);
|
|
pRuleInfo->pszReplacement = ResolveFilename(p3);
|
|
|
|
if (pRuleList)
|
|
{
|
|
pRuleInfo->next = pRuleList;
|
|
pRuleList = pRuleInfo;
|
|
}
|
|
else
|
|
{
|
|
pRuleInfo->next = NULL;
|
|
pRuleList = pRuleInfo;
|
|
}
|
|
}
|
|
else if (strcmp(p1, "V") == 0)
|
|
{
|
|
/* Version information */
|
|
if (!p2 || !p3)
|
|
continue; /* incomplete line */
|
|
if ((dwVersion = (DWORD)atol(p3)) > VER_PRODUCTVERSION_DW)
|
|
break; /* don't read newer versions of the cif file */
|
|
}
|
|
else
|
|
{
|
|
/* Illegal entry, since it doesn't start with V, F or R */
|
|
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
MakeURLFromLocalFile
|
|
|
|
Given a local file name, returns a fully qualified URL.
|
|
|
|
THE CALLER MUST FREE THE RETURNED MEMORY BLOCK.
|
|
*/
|
|
|
|
static char *MakeURLFromLocalFile(char *pszLocal)
|
|
{
|
|
char *pszReturn, *p;
|
|
int i;
|
|
|
|
pszReturn = GTR_MALLOC(strlen(pszLocal) + 10);
|
|
strcpy(pszReturn, "file:///");
|
|
strcat(pszReturn, pszLocal);
|
|
|
|
/* Replace the second : with | (first one is file:) */
|
|
|
|
if (p = strrchr(pszReturn, ':'))
|
|
*p = '|';
|
|
|
|
/* Replace backslashes with forward slashes */
|
|
|
|
for (i = 0; i < strlen(pszReturn); i++)
|
|
{
|
|
if (pszReturn[i] == '\\')
|
|
pszReturn[i] = '/';
|
|
}
|
|
|
|
return pszReturn;
|
|
}
|
|
|
|
BOOL FExpired(DCACHETIME dctExpires)
|
|
{
|
|
DCACHETIME dctSystem;
|
|
|
|
/* Get current system time in our proprietary format */
|
|
SetDCacheTime(&dctSystem);
|
|
return (CompareDCacheTime(dctExpires, dctSystem) <= 0);
|
|
}
|
|
|
|
int CompareDCacheTime(DCACHETIME dct1, DCACHETIME dct2)
|
|
{
|
|
if (dct1.dwDCacheTime1 == dct2.dwDCacheTime1)
|
|
{
|
|
if (dct1.uSecs == dct2.uSecs)
|
|
return 0;
|
|
else if (dct1.uSecs < dct2.uSecs)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
else if (dct1.dwDCacheTime1 < dct2.dwDCacheTime1)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* We pull the file from the disk cache if:
|
|
* a) the file exists
|
|
* AND b) it is the same size as what we think (from cif) it is
|
|
* AND c) c1) If we are NOT connected to the net, (ignoring all other flags)
|
|
* OR c2) c21) If we ARE connected to the net,
|
|
* AND c22) it has not "expired"
|
|
*/
|
|
BOOL static FValidDCacheFile( PCSTR pszFullFn,
|
|
CacheFileInformation *pFileInfo,
|
|
BOOL fNetConnected,
|
|
BOOL fLoadFromDCacheOK)
|
|
{
|
|
BY_HANDLE_FILE_INFORMATION bhfi;
|
|
|
|
if (FExistsFile(pszFullFn, FALSE, &bhfi))
|
|
{
|
|
if ( (pFileInfo->lFilesize != (bhfi.nFileSizeHigh << sizeof(WORD)) + bhfi.nFileSizeLow)
|
|
|| ( fNetConnected
|
|
&& (pFileInfo->fNoDocCache || FExpired(pFileInfo->dctExpires))))
|
|
{
|
|
SetFileAttributes(pszFullFn, FILE_ATTRIBUTE_NORMAL);
|
|
DeleteFile(pszFullFn);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#define CONNECT_STATE_UNINIT 0
|
|
#define CONNECT_STATE_CONNECTED 1
|
|
#define CONNECT_STATE_NOTCONNECTED 2
|
|
|
|
static int iCheckConnectState = CONNECT_STATE_UNINIT;
|
|
|
|
/* This will return true if the first time that we checked to see if we are
|
|
* connected we got a false and the next time we check in this routine, we
|
|
* are NOT connected. This means that the user pressed cancel in the Connect
|
|
* dialog (winsock) just before we timed out.
|
|
* This routine should return TRUE exactly once per session of IExplore.
|
|
*/
|
|
BOOL FUserCancelledAutoDialRecently(void)
|
|
{
|
|
BOOL fConn;
|
|
BOOL fRet;
|
|
|
|
fConn = (!InetIsOffline(0));
|
|
fRet = (iCheckConnectState == CONNECT_STATE_CONNECTED && !fConn);
|
|
|
|
XX_Assert(iCheckConnectState != CONNECT_STATE_UNINIT, (""));
|
|
iCheckConnectState = (fConn ? CONNECT_STATE_CONNECTED : CONNECT_STATE_NOTCONNECTED);
|
|
return fRet;
|
|
}
|
|
|
|
static void InitConnectState(void)
|
|
{
|
|
BOOL fConn;
|
|
|
|
fConn = (!InetIsOffline(0));
|
|
iCheckConnectState = (fConn ? CONNECT_STATE_CONNECTED : CONNECT_STATE_NOTCONNECTED);
|
|
}
|
|
|
|
static BOOL FConnected(HWND hwnd, DWORD dwFlags)
|
|
{
|
|
return (!InetIsOffline(0));
|
|
}
|
|
|
|
/*
|
|
GetResolvedURL
|
|
|
|
Returns the name of the resolved URL from the given URL.
|
|
It performs the following functions:
|
|
|
|
1. Put file:/// as the prefix
|
|
2. Replace the drive specifier (:) with |
|
|
3. Replace all backslashes with forward slashes
|
|
|
|
THE CALLER MUST FREE THE RETURNED MEMORY BLOCK AS WELL AS
|
|
ppPath.
|
|
*/
|
|
|
|
char *GetResolvedURL( PCSTR pcszURL,
|
|
HTFormat *pMime,
|
|
long *pFileLength,
|
|
char **ppPath,
|
|
DCACHETIME *pDcTimeLastModif,
|
|
BOOL fLoadFromDCacheOK)
|
|
{
|
|
const char cszFtp[]="ftp";
|
|
const char cszGopher[]="gopher";
|
|
|
|
if (gPrefs.bEnableDiskCache)
|
|
{
|
|
struct CacheRuleList *pRule;
|
|
char *pszResolved, *pszReturn;
|
|
char szFullFn[_MAX_PATH+1];
|
|
struct _CacheFileInformation *pFileInfo;
|
|
#ifdef FEATURE_LOCALONLY_MESSAGE_URL
|
|
char notcpBuff[_MAX_PATH + 1];
|
|
#endif
|
|
int ndx1=-1;
|
|
int ndx2=-1;
|
|
BOOL fConn;
|
|
|
|
AssertDiskCacheEnabled();
|
|
|
|
/* Look through the file list first for resolution */
|
|
|
|
if ( (ndx1 = Hash_Find(pDynFileHash, pcszURL, NULL, (void **) &pFileInfo)) != -1
|
|
|| (ndx2 = Hash_Find(pFileHash, pcszURL, NULL, (void **) &pFileInfo)) != -1)
|
|
{
|
|
if (fConn = FConnected(wg.hWndHidden, 0))
|
|
{
|
|
/* Don't get ftp and gopher data from dcache if we're connected */
|
|
if ( !_strnicmp(pcszURL, cszFtp, sizeof(cszFtp)-1)
|
|
|| !_strnicmp(pcszURL, cszGopher, sizeof(cszGopher)-1))
|
|
{
|
|
FlushDCacheEntry(pcszURL);
|
|
goto LNotFound;
|
|
}
|
|
}
|
|
GetFullDcPath(szFullFn, pFileInfo->pszPath, NULL, _MAX_PATH+1);
|
|
if (!FValidDCacheFile(szFullFn, pFileInfo, fConn, fLoadFromDCacheOK))
|
|
{
|
|
if (ndx1 != -1)
|
|
Hash_DeleteIndexedEntry(pDynFileHash, ndx1);
|
|
else
|
|
Hash_DeleteIndexedEntry(pFileHash, ndx2);
|
|
goto LNotFound;
|
|
}
|
|
|
|
/* fill in last modified date */
|
|
if (pDcTimeLastModif)
|
|
*pDcTimeLastModif = pFileInfo->dctLastModified;
|
|
|
|
if ( fConn
|
|
&& !fLoadFromDCacheOK
|
|
&& ( gPrefs.iCacheUpdFrequency == CACHE_UPDATE_FREQ_ONCEPERSESS
|
|
&& !pFileInfo->fCheckedForFreshness))
|
|
goto LNotFound;
|
|
pszResolved = GTR_strdup(szFullFn);
|
|
pszReturn = MakeURLFromLocalFile(pszResolved);
|
|
|
|
if (pMime)
|
|
*pMime = HTAtom_for(pFileInfo->pszMime);
|
|
if (pFileLength)
|
|
*pFileLength = pFileInfo->lFilesize;
|
|
if (ppPath)
|
|
*ppPath = pszResolved;
|
|
|
|
return pszReturn;
|
|
}
|
|
|
|
/* Look through the rule list for any possible resolution */
|
|
|
|
pRule = pRuleList;
|
|
|
|
while (pRule)
|
|
{
|
|
if (strncmp(pcszURL, pRule->pszOriginal, strlen(pRule->pszOriginal)) == 0)
|
|
{
|
|
pszResolved = GTR_MALLOC(strlen(pcszURL) + strlen(pRule->pszReplacement) + 10);
|
|
|
|
strcpy(pszResolved, pRule->pszReplacement);
|
|
strcat(pszResolved, &pcszURL[strlen(pRule->pszOriginal)]);
|
|
|
|
pszReturn = MakeURLFromLocalFile(pszResolved);
|
|
|
|
if (pMime)
|
|
*pMime = 0; /* No specified MIME type */
|
|
|
|
if (pFileLength)
|
|
*pFileLength = 0; /* File length unknown */
|
|
|
|
if (ppPath)
|
|
*ppPath = pszResolved;
|
|
|
|
return pszReturn;
|
|
}
|
|
|
|
pRule = pRule->next;
|
|
}
|
|
|
|
LNotFound:
|
|
/* No substitute found for the specified URL */
|
|
#ifdef FEATURE_LOCALONLY_MESSAGE_URL
|
|
if (!bNetwork)
|
|
{
|
|
if ((0 == strncmp(pcszURL, "http:", 5)) ||
|
|
(0 == strncmp(pcszURL, "ftp:", 4)) ||
|
|
(0 == strncmp(pcszURL, "gopher:", 7)))
|
|
{
|
|
strcpy(notcpBuff, wg.szRootDirectory);
|
|
/* They asked for this hack. I should put in .INI anyway */
|
|
/* TODO Beta 3 - requires prefs.c change which is OLD (and default.ini) */
|
|
strcat(notcpBuff, "\\");
|
|
strcat(notcpBuff, "notcp.htm");
|
|
pszResolved = GTR_strdup(notcpBuff);
|
|
pszReturn = MakeURLFromLocalFile(pszResolved);
|
|
|
|
if (pMime)
|
|
*pMime = 0; /* No specified MIME type */
|
|
if (pFileLength)
|
|
*pFileLength = 0; /* File length unknown */
|
|
if (ppPath)
|
|
*ppPath = pszResolved;
|
|
return pszReturn;
|
|
}
|
|
}
|
|
#endif /*FEATURE_LOCALONLY_MESSAGE_URL */
|
|
|
|
|
|
if (pMime)
|
|
*pMime = 0; /* No specified MIME type */
|
|
if (pFileLength)
|
|
*pFileLength = 0; /* unknown file length */
|
|
if (ppPath)
|
|
*ppPath = NULL;
|
|
}
|
|
|
|
return(GTR_strdup(pcszURL));
|
|
}
|
|
|
|
/*
|
|
* Input: pcszURL: (ahem...) a url.
|
|
*
|
|
* Output: Full path to the file if it is in the dcache and is valid
|
|
* else NULL (not in dcache, NOMEM, other.
|
|
*
|
|
* Side effects: none
|
|
*
|
|
* Used by: DavidDi's drag and drop code (GetURLFileSystemPath)
|
|
*/
|
|
PSTR PszGetDCachePath( PCSTR pcszURL,
|
|
HTFormat *pMime,
|
|
long *pFileLength)
|
|
{
|
|
char szFullFn[_MAX_PATH+1];
|
|
struct _CacheFileInformation *pFileInfo;
|
|
|
|
if (gPrefs.bEnableDiskCache)
|
|
{
|
|
/* Look through the file list for resolution */
|
|
if (Hash_Find(pDynFileHash, pcszURL, NULL, (void **) &pFileInfo) != -1)
|
|
{
|
|
GetFullDcPath(szFullFn, pFileInfo->pszPath, NULL, _MAX_PATH+1);
|
|
if (FValidDCacheFile(szFullFn, pFileInfo, FALSE, TRUE))
|
|
{
|
|
if (pMime)
|
|
*pMime = HTAtom_for(pFileInfo->pszMime);
|
|
if (pFileLength)
|
|
*pFileLength = pFileInfo->lFilesize;
|
|
return GTR_strdup(szFullFn);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************
|
|
|
|
DCACHE PROTOCOL CODE
|
|
|
|
Borrowed heavily from htfile.c
|
|
|
|
************************************************************/
|
|
|
|
PRIVATE int HTLoadDCache_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
char *addr;
|
|
HTFormat format;
|
|
char *newname = 0; /* Simplified name of file */
|
|
ENCODING encoding;
|
|
HTAtom language;
|
|
char *localname;
|
|
struct Params_LoadAsync *pParams;
|
|
struct Data_LoadFileCache *pData;
|
|
DCACHETIME dct={0,0};
|
|
|
|
AssertDiskCacheEnabled();
|
|
|
|
pData = *ppInfo;
|
|
switch (nState)
|
|
{
|
|
case STATE_INIT:
|
|
pParams = *ppInfo;
|
|
pData = GTR_MALLOC(sizeof(*pData));
|
|
memset(pData, 0, sizeof(*pData));
|
|
pData->request = HTRequest_validate(pParams->request);
|
|
pData->pStatus = pParams->pStatus;
|
|
|
|
*ppInfo = pData;
|
|
GTR_FREE(pParams);
|
|
|
|
if (!pData->request)
|
|
{
|
|
*pData->pStatus = HT_INTERNAL;
|
|
return STATE_DONE;
|
|
}
|
|
|
|
/* Reduce the filename to a basic form (hopefully unique!) */
|
|
|
|
addr = (char *) pData->request->destination->szActualURL;
|
|
{ /* Hack -dpg to pass a long * to GetResolvedURL instead of int */
|
|
long tmp;
|
|
|
|
tmp = pData->request->content_length;
|
|
localname = PszGetDCachePath(addr, &format, &tmp);
|
|
// GetResolvedURL(addr, &format, &tmp, &localname, NULL, TRUE);
|
|
pData->request->content_length = (int) tmp;
|
|
}
|
|
|
|
/* Need protection here for telnet server but not httpd server */
|
|
if (localname)
|
|
{ /* try local file system */
|
|
if (format == 0)
|
|
format = HTFileFormat(localname, &encoding, &language);
|
|
|
|
pData->fp = fopen(localname, "rb");
|
|
if (pData->fp)
|
|
{
|
|
/*
|
|
Since we are reading a local file, we record that fact in the
|
|
request structure. If this request happens to end up in the
|
|
FileWriter class, that class can avoid writing a temp file
|
|
and simply read this file directly.
|
|
|
|
It is the responsibility of FileWriter to make sure that this
|
|
filename is not marked for deletion as a temporary file.
|
|
|
|
It is the responsibility of the caller (loaddoc.c) to make sure
|
|
that this string gets freed.
|
|
*/
|
|
pData->request->szLocalFileName = GTR_strdup(localname);
|
|
}
|
|
else
|
|
{
|
|
pData->request->szLocalFileName = NULL;
|
|
}
|
|
|
|
/* BUGBUG: (deepaka) overlapping functionality of fImgFromDCache
|
|
* and request->szLocalFileName. Remove it!
|
|
*/
|
|
pData->request->fImgFromDCache = TRUE; //external img. from dcache
|
|
pData->request->savefile = NULL;
|
|
localname = NULL;
|
|
|
|
if (pData->fp)
|
|
{
|
|
#ifdef DCACHE_LOG
|
|
{
|
|
const char cszDCFmt[]="Url: %s Fetching from dcache.";
|
|
char szT[_MAX_PATH+1];
|
|
wsprintf(szT, cszDCFmt, pData->request->destination->szActualURL);
|
|
WriteDCacheLog(szT, DCACHE_LOG_NOFMT);
|
|
}
|
|
#endif
|
|
if (pData->request->content_length == 0)
|
|
{
|
|
if (0 == fseek(pData->fp, 0, SEEK_END))
|
|
{
|
|
pData->request->content_length = ftell(pData->fp);
|
|
if (pData->request->content_length < 0)
|
|
pData->request->content_length = 0;
|
|
fseek(pData->fp, 0, SEEK_SET);
|
|
}
|
|
}
|
|
|
|
if (FGopherFormat(format))
|
|
return DoGopherDCache(tw, pData, format);
|
|
else if (FFtpFormat(format))
|
|
return DoFtpDCache(tw, pData, format);
|
|
|
|
if (!(pData->stream = HTStreamStack(tw, format, pData->request)))
|
|
{
|
|
if ( pData->request->iFlags & HTREQ_NULL_STREAM_IS_OK )
|
|
{
|
|
pData->request->iFlags &= ~HTREQ_NULL_STREAM_IS_OK;
|
|
*pData->pStatus = HT_LOADED;
|
|
}
|
|
else
|
|
*pData->pStatus = -501;
|
|
|
|
fclose(pData->fp);
|
|
return STATE_DONE;
|
|
}
|
|
else
|
|
{
|
|
/* Ignore CRLF if necessary
|
|
*/
|
|
if (!(pData->request->iFlags & HTREQ_BINARY) && ((pData->request->content_encoding == ENCODING_7BIT ||
|
|
pData->request->content_encoding == ENCODING_8BIT)))
|
|
{
|
|
pData->stream = HTNetToText(pData->stream);
|
|
}
|
|
|
|
HTSetStreamStatus(tw, pData->stream, pData->request);
|
|
|
|
if (pData->stream->isa->init_Async)
|
|
{
|
|
/* The stream has an async initialization function that needs to be called
|
|
(probably to put up a dialog) before we continue. */
|
|
struct Params_InitStream *pis;
|
|
|
|
pis = GTR_MALLOC(sizeof(*pis));
|
|
pis->me = pData->stream;
|
|
pis->request = pData->request;
|
|
pis->pResult = pData->pStatus;
|
|
pis->fDCache = FALSE;
|
|
Async_DoCall(pData->stream->isa->init_Async, pis);
|
|
return STATE_FILE_STREAMINIT;
|
|
}
|
|
else
|
|
return STATE_FILE_COPYING;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Failed. */
|
|
|
|
*pData->pStatus = -403;
|
|
ERR_ReportError(tw, errFileURLNotFound, pData->request->destination->szActualURL, NULL);
|
|
return STATE_DONE;
|
|
|
|
case STATE_FILE_STREAMINIT:
|
|
if (*pData->pStatus < 0)
|
|
{
|
|
(*pData->stream->isa->abort)(pData->stream, 0);
|
|
return STATE_DONE;
|
|
}
|
|
// #ifdef REVIEW_BY_DEEPAK
|
|
if (*pData->pStatus == 0)
|
|
{
|
|
/*
|
|
This only happens if the async init function told us we were already
|
|
done. In other words, only FileWriter should do this, and only when
|
|
request->szLocalFileName is true.
|
|
*/
|
|
fclose(pData->fp);
|
|
pData->fp = NULL;
|
|
(*pData->stream->isa->free)(pData->stream, dct, dct);
|
|
pData->stream = NULL;
|
|
*pData->pStatus = HT_LOADED;
|
|
return STATE_DONE;
|
|
}
|
|
// #endif REVIEW_BY_DEEPAK
|
|
/* else fall through to STATE_FILE_COPYING */
|
|
case STATE_FILE_COPYING:
|
|
{
|
|
int bytes = 0;
|
|
int status;
|
|
char input_buffer[INPUT_BUFFER_SIZE];
|
|
|
|
if ( !(pData->request->iFlags & HTREQ_IF_IN_CACHE_DONT_READ_DATA) ) {
|
|
while (1)
|
|
{
|
|
status = fread(input_buffer, 1, INPUT_BUFFER_SIZE, pData->fp);
|
|
if (status == 0)
|
|
{ /* EOF or error */
|
|
if (ferror(pData->fp) != 0)
|
|
status = -1;
|
|
break;
|
|
}
|
|
bytes += status;
|
|
if (pData->request->content_length)
|
|
WAIT_SetTherm(tw, bytes);
|
|
(*pData->stream->isa->put_block)(pData->stream, input_buffer, status, FALSE);
|
|
}
|
|
}
|
|
fclose(pData->fp);
|
|
pData->fp = NULL;
|
|
if (status < 0)
|
|
{
|
|
(*pData->stream->isa->abort)(pData->stream, 0);
|
|
pData->stream = NULL;
|
|
*pData->pStatus = -1;
|
|
}
|
|
else
|
|
{
|
|
(*pData->stream->isa->free)(pData->stream, dct, dct);
|
|
pData->stream = NULL;
|
|
*pData->pStatus = HT_LOADED;
|
|
}
|
|
return STATE_DONE;
|
|
}
|
|
|
|
case STATE_ABORT:
|
|
pData->request = HTRequest_validate(pData->request);
|
|
if (pData->stream)
|
|
{
|
|
(*pData->stream->isa->abort)(pData->stream, HTERROR_CANCELLED);
|
|
}
|
|
if (pData->fp)
|
|
{
|
|
fclose(pData->fp);
|
|
}
|
|
*pData->pStatus = -1;
|
|
return STATE_DONE;
|
|
}
|
|
XX_Assert((0), ("Function called with illegal state: %d", nState));
|
|
return STATE_DONE;
|
|
}
|
|
|
|
/************************************************************
|
|
|
|
Windows specific code
|
|
|
|
************************************************************/
|
|
|
|
#ifdef WIN32
|
|
|
|
/*
|
|
ReadProfileSection
|
|
|
|
Read the given section and return the buffer with
|
|
the content. The return value is NULL if there is
|
|
a memory error or if the given section does not
|
|
exist.
|
|
|
|
THE CALLER MUST FREE THE RETURNED MEMORY BLOCK.
|
|
*/
|
|
|
|
static char *ReadProfileSection(char *pSection)
|
|
{
|
|
char *pBuffer;
|
|
int nAllocSize = 1024; /* Memory allocation block size */
|
|
int nRead; /* Number of bytes read from INI section */
|
|
|
|
pBuffer = GTR_MALLOC(nAllocSize);
|
|
if (!pBuffer)
|
|
return NULL;
|
|
|
|
for (;;)
|
|
{
|
|
nRead = (int) regGetPrivateProfileSection(pSection, pBuffer, nAllocSize, HKEY_LOCAL_MACHINE );
|
|
|
|
if (nRead > nAllocSize - 512)
|
|
{
|
|
/* Buffer is too small. Increase the buffer size and try again. */
|
|
|
|
GTR_FREE(pBuffer);
|
|
|
|
nAllocSize *= 2;
|
|
pBuffer = GTR_MALLOC(nAllocSize);
|
|
if (!pBuffer)
|
|
return NULL;
|
|
}
|
|
else if (nRead == 0)
|
|
{
|
|
/* The specified section was not found */
|
|
|
|
GTR_FREE(pBuffer);
|
|
return NULL;
|
|
}
|
|
else
|
|
break; /* Section read in */
|
|
}
|
|
|
|
return pBuffer;
|
|
}
|
|
|
|
/*
|
|
BuildCDRomList
|
|
|
|
Scans all DOS drives and builds a CD-Rom list.
|
|
Returns the number of CD-Rom drives found.
|
|
*/
|
|
|
|
static int BuildCDRomList(void)
|
|
{
|
|
char szRoot[10], szAlias[20];
|
|
UINT dtype;
|
|
int dcount = 0;
|
|
struct CDRomList *pCDRom;
|
|
|
|
strcpy(szRoot, "C:\\");
|
|
dcount = 0;
|
|
|
|
for(;;)
|
|
{
|
|
dtype = GetDriveType(szRoot);
|
|
if (dtype == DRIVE_CDROM)
|
|
{
|
|
/* Found a CD Rom. Add an entry to the CD Rom list. */
|
|
|
|
dcount++;
|
|
|
|
pCDRom = GTR_MALLOC(sizeof(struct CDRomList));
|
|
pCDRom->path = GTR_strdup(szRoot);
|
|
|
|
sprintf(szAlias, "$(CDROM%d)", dcount);
|
|
pCDRom->alias = GTR_strdup(szAlias);
|
|
|
|
pCDRom->next = pCDRomList; /* Even if pCDRomList == NULL */
|
|
pCDRomList = pCDRom;
|
|
}
|
|
|
|
if (szRoot[0] < 'Z')
|
|
szRoot[0]++;
|
|
else
|
|
break;
|
|
}
|
|
|
|
/* Add a non-CDROM to CDROM list for $(EXEDIR) support */
|
|
pCDRom = GTR_MALLOC(sizeof(struct CDRomList));
|
|
pCDRom->path = GTR_strdup(wg.szRootDirectory);
|
|
strcpy(szAlias, "$(EXEDIR)");
|
|
pCDRom->alias = GTR_strdup(szAlias);
|
|
pCDRom->next = pCDRomList; /* Even if pCDRomList == NULL */
|
|
pCDRomList = pCDRom;
|
|
/* Hope we dont use dcount to assume an actual cdrom anywhere */
|
|
dcount++;
|
|
return dcount;
|
|
}
|
|
|
|
/*
|
|
ReadCacheIndexFiles
|
|
|
|
Read all entries from cache index files and put
|
|
the entries in the substitution hash table and
|
|
rule list.
|
|
|
|
Return value: 0 = no entries found
|
|
n = n entries found
|
|
negative values = error
|
|
*/
|
|
|
|
static int ReadCacheIndexFiles(void)
|
|
{
|
|
int nCount = 0; /* Number of index files processed */
|
|
char *p1, *p2, *p3, *p4; /* Temporary variables used for parsing */
|
|
|
|
char *pIndexSection; /* Pointer to [DiskCaches] section */
|
|
char *pCurrentIndex; /* Current index within [DiskCaches] section */
|
|
int nCurrentIndexLength; /* Length of current index */
|
|
|
|
char *pCustomDrive; /* Pointer to custom drive section */
|
|
char *pCurrentDrive; /* Current drive within the custom drive section */
|
|
int nCurrentDriveLength; /* Length of current drive */
|
|
struct CDRomList *pDrive; /* Entry to be added to the custom drive list */
|
|
|
|
pIndexSection = ReadProfileSection("DiskCaches");
|
|
if (!pIndexSection)
|
|
{
|
|
/* No cache is found. */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Parse through the section */
|
|
|
|
pCurrentIndex = pIndexSection;
|
|
nCurrentIndexLength = strlen(pCurrentIndex);
|
|
|
|
while (pCurrentIndex[0])
|
|
{
|
|
/* BUGBUG: nCurrentIndexLength isn't updated */
|
|
|
|
p1 = strtok(pCurrentIndex, "=");
|
|
p2 = strtok(NULL, "=");
|
|
|
|
/* p1 should contain the entry name which can be used as a section name.
|
|
If there is a section with this name, it should contain CD Rom substitution
|
|
rules, so read it in before we start processing the index files. */
|
|
|
|
FreeCDRomList(pCDRomAlias);
|
|
pCDRomAlias = NULL;
|
|
|
|
pCustomDrive = ReadProfileSection(p1);
|
|
if (pCustomDrive)
|
|
{
|
|
/* Build a list of aliases for CD Roms */
|
|
|
|
pCurrentDrive = pCustomDrive;
|
|
nCurrentDriveLength = strlen(pCurrentDrive);
|
|
|
|
while (pCurrentDrive[0])
|
|
{
|
|
p3 = strtok(pCurrentDrive, "=");
|
|
p4 = strtok(NULL, "=");
|
|
|
|
pDrive = GTR_MALLOC(sizeof(struct CDRomList));
|
|
pDrive->alias = GTR_MALLOC(strlen(p3) + 4);
|
|
sprintf(pDrive->alias, "$(%s)", p3);
|
|
pDrive->path = GTR_strdup(p4);
|
|
|
|
if (pCDRomAlias)
|
|
{
|
|
pDrive->next = pCDRomAlias;
|
|
pCDRomAlias = pDrive;
|
|
}
|
|
else
|
|
{
|
|
pDrive->next = NULL;
|
|
pCDRomAlias = pDrive;
|
|
}
|
|
|
|
pCurrentDrive = pCurrentDrive + nCurrentDriveLength + 1;
|
|
}
|
|
}
|
|
|
|
/* Now read in the index file and process each line within the file */
|
|
|
|
if (ProcessIndexFile(p2, /*fDynamic=*/FALSE))
|
|
nCount++;
|
|
|
|
pCurrentIndex = pCurrentIndex + nCurrentIndexLength + 1; /* Skip past the null terminator */
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
#endif
|
|
|
|
|
|
void AbortFileDCache(FILE **pfpDc, PSTR *ppszDcFile)
|
|
{
|
|
AssertDiskCacheEnabled();
|
|
|
|
fclose(*pfpDc);
|
|
remove(*ppszDcFile);
|
|
*pfpDc = NULL;
|
|
GTR_FREE(*ppszDcFile);
|
|
*ppszDcFile = NULL;
|
|
}
|
|
|
|
/* Returns:
|
|
* pszFullDcFile = "c:\internet\dcache\abc.gif"
|
|
* pszDcFile = "abc.gif"
|
|
*/
|
|
BOOL FGetDCacheFilename(PSTR pszFullDcFile,
|
|
int iLenMax,
|
|
PSTR *ppszDcFile,
|
|
PCSTR pcszURL,
|
|
HTFormat mime_type)
|
|
{
|
|
const char cszDcFileFmt[]="%s (%i).%s";
|
|
|
|
char *pszDcFile, *pszExt;
|
|
char szBase[_MAX_PATH+1];
|
|
int i;
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return FALSE;
|
|
|
|
iLenMax = min(iLenMax, _MAX_PATH+1); //because szBase is _MAX_PATH+1
|
|
XX_Assert(pszFullDcFile, (""));
|
|
if (!FExistsDir(gPrefs.szCacheLocation, TRUE, FALSE))
|
|
return FALSE;
|
|
strncpy(pszFullDcFile, gPrefs.szCacheLocation, iLenMax-1);
|
|
pszFullDcFile[iLenMax-1] = '\0';
|
|
pszDcFile = pszFullDcFile + strlen(pszFullDcFile);
|
|
if (!(pszDcFile > pszFullDcFile && *(pszDcFile-1) == chBSlash))
|
|
*pszDcFile++ = chBSlash;
|
|
|
|
/* REVIEW (deepaka): Fix x_get_good_filename to return friendly/long
|
|
* filenames of Win95. It truncates to 8.4 (strange) right now.
|
|
*/
|
|
/*
|
|
* NOTE: cszDcFileFmt has 8 chars of format specs, which balances insert of i
|
|
*/
|
|
x_get_good_filename(szBase,
|
|
iLenMax - (pszDcFile - pszFullDcFile) - strlen(cszDcFileFmt),
|
|
(PSTR)pcszURL,
|
|
mime_type);
|
|
|
|
// Word IA can't handle extensions ending in ".html". Check to see if the first
|
|
// stab at a file name end in ".html". If if does, truncate the string so that it
|
|
// ends in ".htm".
|
|
{
|
|
int len = strlen(szBase);
|
|
|
|
if ( len >= 5 ) {
|
|
if ( _stricmp( &szBase[len-5], ".html" ) == 0 ) {
|
|
szBase[len-1] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
strcpy(pszDcFile, szBase);
|
|
pszExt = strrchr(szBase, chPeriod);
|
|
if (pszExt)
|
|
*pszExt++= '\0'; /* Now points to the extension */
|
|
|
|
for (i=0;;)
|
|
{
|
|
if (!FExistsFile(pszFullDcFile, FALSE, NULL))
|
|
break;
|
|
wsprintf(pszDcFile, cszDcFileFmt, szBase, ++i, pszExt ? pszExt : "");
|
|
}
|
|
|
|
if (ppszDcFile)
|
|
*ppszDcFile = pszDcFile;
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef FEATURE_INTL
|
|
void SetFileDCache(WWW *pdoc, PCSTR pcszActualURL,
|
|
ENCODING content_encoding,
|
|
FILE **pfpDc,
|
|
PSTR *ppszDcFile, //returns filename without root disk cache
|
|
HTFormat mime_type)
|
|
#else
|
|
void SetFileDCache( PCSTR pcszActualURL,
|
|
ENCODING content_encoding,
|
|
FILE **pfpDc,
|
|
PSTR *ppszDcFile, //returns filename without root disk cache
|
|
HTFormat mime_type)
|
|
#endif
|
|
{
|
|
PSTR pszFullDcFile;
|
|
PSTR pszDcFile;
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return;
|
|
|
|
*pfpDc = NULL;
|
|
*ppszDcFile = NULL;
|
|
|
|
#ifdef FEATURE_INTL
|
|
// No way to create dcache file with FE characters on US system.
|
|
//
|
|
if (pdoc && IsFECodePage(GETMIMECP(pdoc)) && !wg.bDBCSEnabled)
|
|
if (HasDBCSchar(GETMIMECP(pdoc), (UCHAR *)pcszActualURL, lstrlen(pcszActualURL)))
|
|
return;
|
|
#endif
|
|
|
|
if (!(pszFullDcFile = GTR_MALLOC(_MAX_PATH + 1)))
|
|
return;
|
|
*pszFullDcFile = '\0';
|
|
if (!FGetDCacheFilename(pszFullDcFile, _MAX_PATH+1, &pszDcFile, pcszActualURL, mime_type))
|
|
goto LErr;
|
|
|
|
// if ( content_encoding == ENCODING_7BIT
|
|
// || content_encoding == ENCODING_8BIT)
|
|
// *pfpDc = fopen(pszFullDcFile, "w");
|
|
// else
|
|
// ascii files convert lf to cr,lf, thus if you already have cr,lf, you end up with
|
|
// cr,cr,lf (different from original bits and messed up)
|
|
*pfpDc = fopen(pszFullDcFile, "wb");
|
|
|
|
if (!*pfpDc)
|
|
{
|
|
LErr:
|
|
ERR_ReportError(NULL, errCantSaveFile, pszFullDcFile, NULL);
|
|
}
|
|
else
|
|
*ppszDcFile = GTR_strdup(pszDcFile);
|
|
GTR_FREE(pszFullDcFile);
|
|
}
|
|
|
|
static BOOL FAllocFileInfo(CacheFileInformation **ppFileInfo, PCSTR pcszDcFile, PCSTR pcszMime)
|
|
{
|
|
CacheFileInformation *pFileInfo;
|
|
|
|
if (pFileInfo = GTR_MALLOC(sizeof(struct _CacheFileInformation)))
|
|
{
|
|
pFileInfo->pszPath = NULL;
|
|
pFileInfo->pszMime = NULL;
|
|
if ( pcszDcFile
|
|
&& !(pFileInfo->pszPath = GTR_MALLOC(strlen(pcszDcFile) + 1)))
|
|
goto LNoMem;
|
|
|
|
if ( pcszMime
|
|
&& !(pFileInfo->pszMime = GTR_MALLOC(strlen(pcszMime) + 1)))
|
|
goto LNoMem;
|
|
|
|
*ppFileInfo = pFileInfo;
|
|
return TRUE;
|
|
}
|
|
|
|
LNoMem:
|
|
if (pFileInfo)
|
|
{
|
|
if (pFileInfo->pszPath)
|
|
GTR_FREE(pFileInfo->pszPath);
|
|
if (pFileInfo->pszMime)
|
|
GTR_FREE(pFileInfo->pszMime);
|
|
GTR_FREE(pFileInfo);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void SetDCacheTime(DCACHETIME *pdctime)
|
|
{
|
|
DCACHETIME dctime;
|
|
SYSTEMTIME st;
|
|
|
|
GetSystemTime(&st);
|
|
|
|
dctime.uMins = st.wMinute;
|
|
dctime.uHrs = st.wHour;
|
|
dctime.uDate = st.wDay;
|
|
dctime.uMonth = st.wMonth;
|
|
dctime.uYear = st.wYear;
|
|
dctime.uSecs = st.wSecond;
|
|
dctime.uUnused = 0;
|
|
*pdctime = dctime;
|
|
}
|
|
|
|
static BOOL FUpdateCIFInfo( PCSTR pcszURL,
|
|
PCSTR pcszDcFile,
|
|
DCACHETIME dctExpires,
|
|
DCACHETIME dctLastModif,
|
|
HTFormat format_inDc,
|
|
BOOL fCurDoc)
|
|
{
|
|
char szFullFn[_MAX_PATH+1];
|
|
CacheFileInformation *pFileInfo;
|
|
BY_HANDLE_FILE_INFORMATION bhfi;
|
|
PSTR pszMime;
|
|
|
|
XX_Assert(pcszDcFile, ("Unexpected NULL filename"));
|
|
|
|
GetFullDcPath(szFullFn, pcszDcFile, NULL, _MAX_PATH+1);
|
|
if (!FExistsFile(szFullFn, FALSE, &bhfi))
|
|
{
|
|
XX_Assert(0, ("DCache: Hey, what happened to %s?", pcszDcFile));
|
|
return FALSE;
|
|
}
|
|
if (((bhfi.nFileSizeHigh << sizeof(WORD)) + bhfi.nFileSizeLow) == 0)
|
|
{
|
|
/* Could be because we got back a null result (ex. from a bogus
|
|
* query) or a form that has no associated METHOD and ACTION
|
|
* (ref. to bug# 272.
|
|
*/
|
|
remove(szFullFn);
|
|
return FALSE;
|
|
}
|
|
|
|
/* filesize not 0 */
|
|
XX_Assert((bhfi.nFileSizeHigh << sizeof(WORD)) + bhfi.nFileSizeLow, (""));
|
|
|
|
if (Hash_Find(pDynFileHash, pcszURL, NULL, (void **)&pFileInfo) == -1)
|
|
{
|
|
if (!FAllocFileInfo(&pFileInfo, pcszDcFile, (pszMime = HTAtom_name(format_inDc))))
|
|
return FALSE;
|
|
pFileInfo->lFilesize = (bhfi.nFileSizeHigh << sizeof(WORD)) + bhfi.nFileSizeLow;
|
|
pFileInfo->fRamDoc = FALSE;
|
|
pFileInfo->fNoDocCache = FALSE;
|
|
pFileInfo->fCurDoc = fCurDoc;
|
|
pFileInfo->fCheckedForFreshness = TRUE; /* hot off the wire */
|
|
strcpy(pFileInfo->pszPath, pcszDcFile);
|
|
XX_Assert(pszMime, ("Unexpected NULL mime string!"));
|
|
strcpy(pFileInfo->pszMime, pszMime);
|
|
Hash_Add(pDynFileHash, pcszURL, NULL, pFileInfo);
|
|
}
|
|
/* Update last modified/created time */
|
|
if (dctLastModif.dwDCacheTime1 == 0 && dctLastModif.dwDCacheTime2 == 0)
|
|
SetDCacheTime(&pFileInfo->dctLastModified);
|
|
else
|
|
pFileInfo->dctLastModified = dctLastModif;
|
|
/* update last access time */
|
|
SetDCacheTime(&pFileInfo->dctLastUsed);
|
|
/* update expires tag from header info. */
|
|
pFileInfo->dctExpires = dctExpires;
|
|
PropagateFreshnessToCif();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL FFreshnessCheckNeeded(PCSTR pcszURL)
|
|
{
|
|
CacheFileInformation *pFileInfo;
|
|
|
|
if ( gPrefs.iCacheUpdFrequency == CACHE_UPDATE_FREQ_NEVER
|
|
|| !FConnected(wg.hWndHidden, 0))
|
|
return FALSE;
|
|
|
|
XX_Assert(gPrefs.iCacheUpdFrequency == CACHE_UPDATE_FREQ_ONCEPERSESS, (""));
|
|
if (Hash_Find(pDynFileHash, pcszURL, NULL, (void **)&pFileInfo) != -1)
|
|
{
|
|
return !(pFileInfo->fCheckedForFreshness);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void UpdateFileDCacheLocal( PCSTR pcszActualURL,
|
|
FILE **pfpDc,
|
|
PSTR *ppszDcFile,
|
|
HTFormat format_inDc,
|
|
DCACHETIME dctExpires,
|
|
DCACHETIME dctLastModif,
|
|
BOOL fAbort,
|
|
BOOL fCurDoc,
|
|
struct Mwin *tw)
|
|
{
|
|
char szFullFn[_MAX_PATH+1];
|
|
struct _www *w3doc=NULL;
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return;
|
|
|
|
if (!*pfpDc)
|
|
{
|
|
XX_Assert(!*ppszDcFile, ("Filename should be null too!"));
|
|
return;
|
|
}
|
|
|
|
#ifdef XX_DEBUG_T
|
|
fgetpos(*pfpDc, &fpos);
|
|
#endif
|
|
if (fclose(*pfpDc))
|
|
{
|
|
// CleanupDCache(0);
|
|
// if (fclose(*pfpDc))
|
|
fAbort = TRUE;
|
|
}
|
|
*pfpDc = NULL;
|
|
|
|
XX_Assert(*ppszDcFile, ("Null filename unexpected"));
|
|
|
|
GetFullDcPath(szFullFn, *ppszDcFile, NULL, _MAX_PATH+1);
|
|
if (fAbort)
|
|
{
|
|
remove(szFullFn);
|
|
}
|
|
else
|
|
{
|
|
SetFileAttributes(szFullFn, FILE_ATTRIBUTE_READONLY);
|
|
FUpdateCIFInfo( pcszActualURL,
|
|
*ppszDcFile,
|
|
dctExpires,
|
|
dctLastModif,
|
|
format_inDc,
|
|
fCurDoc);
|
|
/* Update the expiry time of the w3doc */
|
|
if ( Hash_Count(&tw->doc_cache) > 0
|
|
&& Hash_Find(&tw->doc_cache, pcszActualURL, NULL, (void **)&w3doc) >= 0)
|
|
{
|
|
w3doc->dctExpires.dwDCacheTime1 = dctExpires.dwDCacheTime1;
|
|
w3doc->dctExpires.dwDCacheTime2 = dctExpires.dwDCacheTime2;
|
|
}
|
|
}
|
|
|
|
GTR_FREE(*ppszDcFile);
|
|
*ppszDcFile = NULL;
|
|
}
|
|
|
|
/* Wrapper for UpdateFileDCacheLocal so multiple threads don't update CIF
|
|
* simultaneously.
|
|
*/
|
|
void UpdateFileDCache( PCSTR pcszActualURL,
|
|
FILE **pfpDc,
|
|
PSTR *ppszDcFile,
|
|
HTFormat format_inDc,
|
|
DCACHETIME dctExpires,
|
|
DCACHETIME dctLastModif,
|
|
BOOL fAbort,
|
|
BOOL fCurDoc,
|
|
struct Mwin *tw)
|
|
{
|
|
XX_Assert(hCIFMutex, (""));
|
|
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Waiting for hCIFMutex");
|
|
#endif
|
|
// Set the flag that will cause the cleaning thread to abandon cleaning, which will
|
|
// thus release the hCIFMutex. This favors the main thread with regard to accessing
|
|
// the CIF
|
|
bStopCleaning = TRUE;
|
|
WaitForSingleObject(hCIFMutex, INFINITE);
|
|
|
|
// Once we own the hCIFMutex, we can clear the "request to stop cleaning" flag
|
|
bStopCleaning = FALSE;
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Got hCIFMutex");
|
|
#endif
|
|
UpdateFileDCacheLocal( pcszActualURL,
|
|
pfpDc,
|
|
ppszDcFile,
|
|
format_inDc,
|
|
dctExpires,
|
|
dctLastModif,
|
|
fAbort,
|
|
fCurDoc,
|
|
tw);
|
|
SetEvent(hDCacheStartEvent);
|
|
ReleaseMutex(hCIFMutex);
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Released hCIFMutex");
|
|
#endif
|
|
}
|
|
|
|
static BOOL FUpdateBuiltinDCacheLocal( HTFormat mime_type,
|
|
PCSTR pcszURL,
|
|
char **ppszOrgFile,
|
|
DCACHETIME dctExpires,
|
|
DCACHETIME dctLastModif,
|
|
BOOL fCurDoc,
|
|
struct Mwin *tw)
|
|
{
|
|
char szFullDcFile[_MAX_PATH+1];
|
|
char *pszDcFile;
|
|
|
|
if ( !gPrefs.bEnableDiskCache
|
|
|| !*ppszOrgFile)
|
|
return FALSE;
|
|
|
|
if ( !FGetDCacheFilename(szFullDcFile, sizeof(szFullDcFile), &pszDcFile, pcszURL, mime_type)
|
|
|| !MoveFile(*ppszOrgFile, szFullDcFile))
|
|
{
|
|
ERR_ReportError(tw, errCantSaveFile, szFullDcFile, NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
SetFileAttributes(szFullDcFile, FILE_ATTRIBUTE_READONLY);
|
|
/* fsOrig is also _MAX_PATH + 1 long */
|
|
strcpy(*ppszOrgFile, szFullDcFile);
|
|
|
|
/* Update cache info in hash table here */
|
|
return FUpdateCIFInfo( pcszURL,
|
|
pszDcFile,
|
|
dctExpires,
|
|
dctLastModif,
|
|
mime_type,
|
|
fCurDoc);
|
|
}
|
|
|
|
/* Wrapper for FUpdateBuiltinDCache so multiple threads don't update CIF
|
|
* simultaneously.
|
|
*/
|
|
BOOL FUpdateBuiltinDCache( HTFormat mime_type,
|
|
PCSTR pcszURL,
|
|
char **ppszOrgFile,
|
|
DCACHETIME dctExpires,
|
|
DCACHETIME dctLastModif,
|
|
BOOL fCurDoc,
|
|
struct Mwin *tw)
|
|
{
|
|
BOOL fRet;
|
|
|
|
XX_Assert(hCIFMutex, (""));
|
|
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Waiting for hCIFMutex");
|
|
#endif
|
|
// Set the flag that will cause the cleaning thread to abandon cleaning, which will
|
|
// thus release the hCIFMutex. This favors the main thread with regard to accessing
|
|
// the CIF
|
|
bStopCleaning = TRUE;
|
|
|
|
WaitForSingleObject(hCIFMutex, INFINITE);
|
|
|
|
// Once we own the hCIFMutex, we can clear the "request to stop cleaning" flag
|
|
bStopCleaning = FALSE;
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Got hCIFMutex");
|
|
#endif
|
|
fRet = FUpdateBuiltinDCacheLocal( mime_type,
|
|
pcszURL,
|
|
ppszOrgFile,
|
|
dctExpires,
|
|
dctLastModif,
|
|
fCurDoc,
|
|
tw);
|
|
SetEvent(hDCacheStartEvent);
|
|
ReleaseMutex(hCIFMutex);
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Released hCIFMutex");
|
|
#endif
|
|
return fRet;
|
|
}
|
|
|
|
#if 0
|
|
static BOOL FIgnoreDcFind(LPWIN32_FIND_DATA pwfd)
|
|
{
|
|
if (!(pwfd->dwFileAttributes & FILE_ATTRIBUTE_NORMAL))
|
|
return TRUE;
|
|
|
|
if (pwfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
static void DeleteDCacheFile(PCSTR pszDcFile)
|
|
{
|
|
char szFullFn[_MAX_PATH+1];
|
|
|
|
GetFullDcPath(szFullFn, pszDcFile, NULL, _MAX_PATH+1);
|
|
if ( FExistsFile(szFullFn, FALSE, NULL)
|
|
&& ( !SetFileAttributes(szFullFn, FILE_ATTRIBUTE_NORMAL)
|
|
|| !DeleteFile(szFullFn)))
|
|
{
|
|
ERR_ReportError(NULL, errCantDelete, szFullFn, NULL);
|
|
}
|
|
|
|
}
|
|
|
|
void FlushDCache(HWND hDlg)
|
|
{
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return;
|
|
|
|
if (resourceMessageBox(hDlg, RES_STRING_FLUSH_CACHE, RES_STRING_FLUSH_TITLE, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION) != IDYES)
|
|
return;
|
|
|
|
if (!FExistsDir(gPrefs.szCacheLocation, TRUE, FALSE))
|
|
return;
|
|
|
|
CleanupDCacheLocal(DCACHE_WATERMARK_ZERO);
|
|
}
|
|
|
|
void FlushDCacheEntry(PCSTR pcszURL)
|
|
{
|
|
int ndx;
|
|
CacheFileInformation *pFileInfo;
|
|
|
|
AssertDiskCacheEnabled();
|
|
|
|
if ((ndx = Hash_Find(pDynFileHash, pcszURL, NULL, (void **)&pFileInfo)) == -1)
|
|
return;
|
|
|
|
DeleteDCacheFile(pFileInfo->pszPath);
|
|
Hash_DeleteIndexedEntry(pDynFileHash, ndx);
|
|
}
|
|
|
|
void CmdChangeCacheUpdFrequency(int iCacheUpdFreq)
|
|
{
|
|
gPrefs.iCacheUpdFrequency = iCacheUpdFreq;
|
|
}
|
|
|
|
const char cszFileFilter[] = "*";
|
|
|
|
void UpdateDCacheLocation(PCSTR pszNewDCLoc)
|
|
{
|
|
char szCur[_MAX_PATH+1], szNew[_MAX_PATH+1];
|
|
int i, cFiles;
|
|
CacheFileInformation *pFileInfo;
|
|
HCURSOR hCursorPrev=NULL;
|
|
|
|
extern HCURSOR hCursorWorking;
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return;
|
|
|
|
if (!lstrcmpi(gPrefs.szCacheLocation, pszNewDCLoc))
|
|
return;
|
|
|
|
if (!FExistsDir(pszNewDCLoc, TRUE, FALSE))
|
|
{
|
|
ERR_ReportError(NULL, errNoDir, pszNewDCLoc, NULL);
|
|
return;
|
|
}
|
|
|
|
if (hCursorWorking)
|
|
hCursorPrev = SetCursor(hCursorWorking);
|
|
|
|
GetFullDcPath(szCur, cszAppCIF, NULL, _MAX_PATH+1);
|
|
GetFullDcPath(szNew, cszAppCIF, pszNewDCLoc, _MAX_PATH+1);
|
|
MoveFile(szCur, szNew);
|
|
for (i = 0, cFiles=Hash_Count(pDynFileHash); i<cFiles; i++)
|
|
{
|
|
Hash_GetIndexedEntry(pDynFileHash, i, NULL, NULL, (void **) &pFileInfo);
|
|
XX_Assert(pFileInfo->pszPath, ("Null filename!"));
|
|
GetFullDcPath(szCur, pFileInfo->pszPath, NULL, _MAX_PATH+1);
|
|
GetFullDcPath(szNew, pFileInfo->pszPath, pszNewDCLoc, _MAX_PATH+1);
|
|
if (!MoveFile(szCur, szNew))
|
|
{
|
|
ERR_ReportError(NULL, errCantMoveFile, szCur, szNew);
|
|
}
|
|
}
|
|
|
|
/* RemoveDirectory will fail if dir. is not empty */
|
|
RemoveDirectory(gPrefs.szCacheLocation);
|
|
strcpy(gPrefs.szCacheLocation, pszNewDCLoc);
|
|
FDCacheOverflow(DCACHE_RECALC_SIZE);
|
|
|
|
if (hCursorPrev)
|
|
SetCursor(hCursorPrev);
|
|
|
|
# undef szFileFind
|
|
}
|
|
|
|
static void GetImageInfoKey(PCImageInfo pImgInfo, PSTR pszKey)
|
|
{
|
|
const char szKeyFmt[]="%lx";
|
|
wsprintf(pszKey, szKeyFmt, (long)pImgInfo);
|
|
}
|
|
|
|
static void HashDestroyOnZero(struct hash_table **ppAuxImgHash)
|
|
{
|
|
if (Hash_Count(*ppAuxImgHash) == 0)
|
|
{
|
|
Hash_Destroy(*ppAuxImgHash);
|
|
*ppAuxImgHash = NULL;
|
|
}
|
|
}
|
|
|
|
void MoveDCacheEntryToAux(PCSTR pcszURL, struct ImageInfo *pImgInfo)
|
|
{
|
|
int ndx;
|
|
CacheFileInformation *pFileInfo;
|
|
char szKey[MAX_KEY_LEN];
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return;
|
|
|
|
if ((ndx = Hash_Find(pDynFileHash, pImgInfo->actualURL, NULL, &pFileInfo)) == -1)
|
|
return;
|
|
|
|
if (!pAuxImgHash && !(pAuxImgHash = Hash_Create()))
|
|
return;
|
|
|
|
GetImageInfoKey(pImgInfo, szKey);
|
|
Hash_Add(pAuxImgHash, szKey, NULL, pFileInfo);
|
|
Hash_DeleteIndexedEntry(pDynFileHash, ndx);
|
|
}
|
|
|
|
void MoveAuxEntryToDCache(struct ImageInfo *pImgInfo)
|
|
{
|
|
char szKey[MAX_KEY_LEN];
|
|
int ndx;
|
|
CacheFileInformation *pFileInfo;
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return;
|
|
|
|
if (!pAuxImgHash)
|
|
return;
|
|
|
|
GetImageInfoKey(pImgInfo, szKey);
|
|
|
|
if ((ndx = Hash_Find(pAuxImgHash, szKey, NULL, &pFileInfo)) == -1)
|
|
{
|
|
XX_Assert(FALSE, ("Image info. not found in pAuxImgHash!"));
|
|
return;
|
|
}
|
|
|
|
Hash_Add(pDynFileHash, pImgInfo->actualURL, NULL, pFileInfo);
|
|
Hash_DeleteIndexedEntry(pAuxImgHash, ndx);
|
|
HashDestroyOnZero(&pAuxImgHash);
|
|
}
|
|
|
|
void DeleteAuxEntry(struct ImageInfo *pImgInfo)
|
|
{
|
|
char szKey[MAX_KEY_LEN];
|
|
int ndx;
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return;
|
|
|
|
if (!pAuxImgHash)
|
|
return;
|
|
|
|
GetImageInfoKey(pImgInfo, szKey);
|
|
ndx = Hash_Find(pAuxImgHash, szKey, NULL, NULL);
|
|
XX_Assert(ndx != -1, ("pImgInfo not found in pAuxImgHash!"));
|
|
Hash_DeleteIndexedEntry(pAuxImgHash, ndx);
|
|
HashDestroyOnZero(&pAuxImgHash);
|
|
}
|
|
|
|
|
|
/* Borrowed from GetResolvedURL */
|
|
char *GetResolvedURLAux(PCImageInfo pImgInfo, char **ppszPath)
|
|
{
|
|
CacheFileInformation *pFileInfo;
|
|
char szKey[MAX_KEY_LEN];
|
|
char szFullFn[_MAX_PATH+1];
|
|
int ndx;
|
|
PSTR pszResolved, pszReturn;
|
|
|
|
if (!gPrefs.bEnableDiskCache || !pAuxImgHash)
|
|
goto LNotFound;
|
|
|
|
GetImageInfoKey(pImgInfo, szKey);
|
|
if ((ndx = Hash_Find(pAuxImgHash, szKey, NULL, &pFileInfo)) == -1)
|
|
goto LNotFound;
|
|
GetFullDcPath(szFullFn, pFileInfo->pszPath, NULL, _MAX_PATH+1);
|
|
//For the aux. cache, we fake it and assume that we're not
|
|
// connected to the net.
|
|
if (!FValidDCacheFile(szFullFn, pFileInfo, FALSE, TRUE))
|
|
{
|
|
Hash_DeleteIndexedEntry(pAuxImgHash, ndx);
|
|
HashDestroyOnZero(&pAuxImgHash);
|
|
goto LNotFound;
|
|
}
|
|
|
|
pszResolved = GTR_strdup(szFullFn);
|
|
pszReturn = MakeURLFromLocalFile(pszResolved);
|
|
if (ppszPath)
|
|
*ppszPath = pszResolved;
|
|
return pszReturn;
|
|
|
|
LNotFound:
|
|
if (ppszPath)
|
|
*ppszPath = NULL;
|
|
return (pszReturn = GTR_strdup(pImgInfo->actualURL));
|
|
}
|
|
|
|
void CleanupDCache(UINT uFlags)
|
|
{
|
|
XX_Assert(hDCacheThread, (""));
|
|
XX_Assert(hDCacheDoneEvent, (""));
|
|
XX_Assert(hDCacheStartEvent, (""));
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return;
|
|
|
|
/* Let DwCleanupDCacheProc (separate thread) do all the work */
|
|
ResetEvent(hDCacheDoneEvent);
|
|
SetEvent(hDCacheStartEvent);
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Waiting for hDCacheDoneEvent");
|
|
#endif
|
|
WaitForSingleObject(hDCacheDoneEvent, INFINITE);
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Got hDCacheDoneEvent");
|
|
#endif
|
|
}
|
|
|
|
|
|
int CbWriteDCache( PCSTR pch,
|
|
int cbSize,
|
|
int cb,
|
|
FILE **pfp,
|
|
char **ppszDcFile,
|
|
char *pszActualURL,
|
|
UINT uFlags,
|
|
struct Mwin *tw)
|
|
{
|
|
FILE *fp;
|
|
int cbWritten;
|
|
fpos_t fpos;
|
|
char szFullFn[_MAX_PATH+1];
|
|
|
|
if (!(fp = *pfp))
|
|
return -1;
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return -1;
|
|
|
|
/* Save out current file position. In case of error, new file position
|
|
* cannot be determined later (per fwrite spec).
|
|
*/
|
|
if (fgetpos(fp, &fpos))
|
|
return -1;
|
|
XX_Assert(cbSize == 1, ("Writing non-byte objects, change following line to cb*cbSize"));
|
|
if ((cbWritten = fwrite(pch, cbSize, cb, fp)) != cb)
|
|
{
|
|
/* error: ran out of disk space? */
|
|
CleanupDCache(uFlags);
|
|
if (fsetpos(fp, &fpos))
|
|
goto LError;
|
|
|
|
if ((cbWritten = fwrite(pch, cbSize, cb, fp)) != cb)
|
|
{
|
|
/* failed to write again: give up */
|
|
LError:
|
|
ERR_ReportError(tw, errCantSaveCache, *ppszDcFile, NULL);
|
|
fclose(fp);
|
|
GetFullDcPath(szFullFn, *ppszDcFile, NULL, _MAX_PATH+1);
|
|
remove(szFullFn);
|
|
GTR_FREE(*ppszDcFile);
|
|
*pfp = NULL;
|
|
*ppszDcFile = NULL;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
XX_Assert(!fgetpos(fp, &fpos), (""));
|
|
return cbWritten;
|
|
}
|
|
|
|
DWORD WINAPI DwCleanupDCacheProc(LPUINT lpUFlags)
|
|
{
|
|
XX_Assert(hDCacheMutex, (""));
|
|
XX_Assert(hDCacheStartEvent, (""));
|
|
XX_Assert(hDCacheDoneEvent, (""));
|
|
|
|
while (TRUE)
|
|
{
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Waiting for hDCacheStartEvent");
|
|
#endif
|
|
WaitForSingleObject(hDCacheStartEvent, INFINITE);
|
|
|
|
/* should we close this thread? */
|
|
if(!hDCacheThread)
|
|
return(1);
|
|
|
|
ResetEvent(hDCacheStartEvent);
|
|
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Got hDCacheStartEvent");
|
|
#endif
|
|
if (FDCacheOverflow(DCACHE_WATERMARK_HIGH))
|
|
{
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Waiting for hCIFMutex");
|
|
#endif
|
|
WaitForSingleObject(hCIFMutex, INFINITE);
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Got hCIFMutex");
|
|
#endif
|
|
CleanupDCacheLocal(DCACHE_WATERMARK_HIGH);
|
|
ReleaseMutex(hCIFMutex);
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Released hCIFMutex");
|
|
#endif
|
|
}
|
|
SetEvent(hDCacheDoneEvent);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
GLOBALDEF PUBLIC HTProtocol HTDCache = {"dcache", NULL, HTLoadDCache_Async};
|
|
|
|
#define IsValidDriveCh(ch) isalpha(ch)
|
|
|
|
static void GetRootFromPath(PCSTR pcszPath, PSTR pszRoot)
|
|
{
|
|
const char cszCRoot[] = "c:\\";
|
|
|
|
if ( !pcszPath
|
|
|| strlen(pcszPath) < 3
|
|
|| !IsValidDriveCh(*pcszPath)
|
|
|| *(pcszPath+1) != chColon
|
|
|| *(pcszPath+2) != chBSlash)
|
|
{
|
|
strcpy(pszRoot, cszCRoot);
|
|
}
|
|
else
|
|
{
|
|
strncpy(pszRoot, pcszPath, 3);
|
|
*(pszRoot+3) = '\0';
|
|
}
|
|
}
|
|
|
|
static BOOL FDCacheOverflow(UINT uFlags)
|
|
{
|
|
char szRoot[10];
|
|
DWORD dwDCacheSpaceMax=0;
|
|
static DWORD dwClusterSize=0;
|
|
static DWORD dwClusters=0;
|
|
DWORD dwSectorsPerCluster, dwBytesPerSector;
|
|
DWORD dwFreeClusters;
|
|
int iPercent;
|
|
|
|
# define DW_BYTES_PER_SECTOR_DEF 512 //assume 512 bytes per sector
|
|
# define DW_SECTORS_PER_CLUSTER_DEF 32 //assume >400MB hard disk
|
|
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
/* Not checking for overflow is almost tantamount to saying we're
|
|
* always overflowing.
|
|
*/
|
|
if (!fCheckDCacheOverflow)
|
|
return TRUE;
|
|
#endif
|
|
|
|
if (uFlags == DCACHE_WATERMARK_ZERO)
|
|
return TRUE;
|
|
|
|
if (!dwClusterSize || uFlags == DCACHE_RECALC_SIZE)
|
|
{
|
|
GetRootFromPath(gPrefs.szCacheLocation, szRoot);
|
|
if ( !GetDiskFreeSpace(szRoot, &dwSectorsPerCluster, &dwBytesPerSector, &dwFreeClusters, &dwClusters)
|
|
|| ((dwClusterSize = dwSectorsPerCluster*dwBytesPerSector) == 0))
|
|
dwClusterSize = (DW_SECTORS_PER_CLUSTER_DEF * DW_BYTES_PER_SECTOR_DEF);
|
|
}
|
|
/* dcache is larger of 4Meg or 1% of disk or gPrefs.iCachePercent of disk */
|
|
dwDCacheSpaceMax = max( DCACHE_SIZE_MIN,
|
|
(DWORD)((dwClusterSize*dwClusters/100)*(max(DCACHE_PERCENT_MIN, gPrefs.iCachePercent))));
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
iPercent = (uFlags == DCACHE_WATERMARK_MAX ? 100 :
|
|
uFlags == DCACHE_WATERMARK_LOW ? gPrefs.iCachePercentLow :
|
|
gPrefs.iCachePercentHigh);
|
|
#else
|
|
iPercent = (uFlags == DCACHE_WATERMARK_MAX ? DCACHE_PERCENT_MAX :
|
|
uFlags == DCACHE_WATERMARK_LOW ? DCACHE_PERCENT_LOW :
|
|
DCACHE_PERCENT_HIGH);
|
|
#endif
|
|
|
|
#ifdef DCACHE_LOG
|
|
{
|
|
const char cszDCLogFmt[]="Dir=%s\tDirSize=%lu\tMaxDCache Size=%lu\nCheck if dcache > %i%%\tCleanup Flag=%i";
|
|
char szT[_MAX_PATH+1];
|
|
int iFlag;
|
|
DWORD dwDir = DwDirSize(gPrefs.szCacheLocation, dwClusterSize);
|
|
|
|
iFlag = ((dwDCacheSpaceMax/100)*iPercent < DwDirSize(gPrefs.szCacheLocation, dwClusterSize));
|
|
wsprintf(szT, cszDCLogFmt, gPrefs.szCacheLocation, dwDir, dwDCacheSpaceMax, iPercent, iFlag);
|
|
WriteDCacheLog(szT, DCACHE_LOG_NOFMT);
|
|
}
|
|
#endif
|
|
return ((dwDCacheSpaceMax/100)*iPercent < DwDirSize(gPrefs.szCacheLocation, dwClusterSize));
|
|
}
|
|
|
|
|
|
static BOOL FIgnoreDcFileFind(LPWIN32_FIND_DATA pwfd)
|
|
{
|
|
/* For now don't bother deleting subdirectories */
|
|
return ( pwfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
|
|
|| !lstrcmpi(pwfd->cFileName, cszAppCIF)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Create a hash table that allows lookup of URL given a cache file name
|
|
//
|
|
// On entry:
|
|
// ppFileToURLHash: pointer to pointer to hash table to be created
|
|
//
|
|
// On exit:
|
|
// *ppFileToURLHash: if created, points to new hash table
|
|
// returns: TRUE -> hash table was created
|
|
// FALSE -> hash table creation failed
|
|
//
|
|
// Notes:
|
|
// Although not common, there are times when we need to determine if a given
|
|
// file is in the CIF hash table. However, the CIF hash table allows only
|
|
// looking up by URL, not file name. This routine creates another hash table
|
|
// that allows looking up using the file name as the key. Currently, the only
|
|
// reason needed to perform this lookup is to determine if the file is part of
|
|
// the cache. Consequently we don't actually store any info about the files, just
|
|
// a big hash table of the file names, which can be used to determine if the file
|
|
// is part of the cache or not.
|
|
//
|
|
static BOOL CreateCacheFileToURLHashTable( struct hash_table **ppFileToURLHash )
|
|
{
|
|
int i, iMax;
|
|
CacheFileInformation *pFileInfo;
|
|
char szFullFn[_MAX_PATH+1];
|
|
|
|
// Make a new hash table for reverse lookup (file name -> URL )
|
|
*ppFileToURLHash = Hash_Create();
|
|
|
|
if ( *ppFileToURLHash ) {
|
|
// Iterate through cache hash table, adding each item to the new hash table that
|
|
// is indexed by cache file name instead of by URL
|
|
for (i=0, iMax=Hash_Count(pDynFileHash); i<iMax; i++)
|
|
{
|
|
Hash_GetIndexedEntry(pDynFileHash, i, NULL, NULL, &pFileInfo);
|
|
GetFullDcPath(szFullFn, pFileInfo->pszPath, NULL, _MAX_PATH+1);
|
|
Hash_Add(*ppFileToURLHash, szFullFn, NULL, NULL);
|
|
if ( bStopCleaning )
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Destroy the hash table that allows lookup of URL given a cache file name
|
|
//
|
|
// On entry:
|
|
// pFileToURLHash: pointer to table to be destoyed
|
|
//
|
|
// On exit:
|
|
// *pFileToURLHash: hash table and it's contents have been freed
|
|
//
|
|
static void DestroyCacheFileToURLHashTable( struct hash_table *pFileToURLHash )
|
|
{
|
|
if ( pFileToURLHash )
|
|
Hash_Destroy( pFileToURLHash );
|
|
}
|
|
|
|
static BOOL FInDCacheList(PCSTR pcszFile, struct hash_table *pFileToURLHash)
|
|
{
|
|
int i, iMax;
|
|
CacheFileInformation *pFileInfo;
|
|
char szFullFn[_MAX_PATH+1];
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return FALSE;
|
|
|
|
if ( pFileToURLHash ) {
|
|
// If we have a cache file to URL lookup hash table, use it
|
|
if ( Hash_Find(pFileToURLHash, pcszFile, NULL, NULL) != -1 )
|
|
return TRUE;
|
|
} else {
|
|
// Otherwise look for it by iterating through the regular hash table
|
|
for (i=0, iMax=Hash_Count(pDynFileHash); i<iMax; i++)
|
|
{
|
|
Hash_GetIndexedEntry(pDynFileHash, i, NULL, NULL, &pFileInfo);
|
|
GetFullDcPath(szFullFn, pFileInfo->pszPath, NULL, _MAX_PATH+1);
|
|
if (!lstrcmpi(szFullFn, pcszFile))
|
|
return TRUE;
|
|
// Abandon cleaning effort, assume we would have found it if we kept on looking,
|
|
// as this is the "safe" assumption (i.e. file won't be deleted)
|
|
if ( bStopCleaning )
|
|
return TRUE;
|
|
}
|
|
}
|
|
if (pAuxImgHash)
|
|
{
|
|
for (i=0, iMax=Hash_Count(pAuxImgHash); i<iMax; i++)
|
|
{
|
|
Hash_GetIndexedEntry(pAuxImgHash, i, NULL, NULL, &pFileInfo);
|
|
GetFullDcPath(szFullFn, pFileInfo->pszPath, NULL, _MAX_PATH+1);
|
|
if (!lstrcmpi(szFullFn, pcszFile))
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef DCACHE_LOG
|
|
const char cszLogFile[]="\\dcache.log";
|
|
#endif
|
|
|
|
static BOOL FOkToDeleteDCacheFile(PCSTR pcszFile, PCSTR pcszUrl)
|
|
{
|
|
/* Why do we need this function? */
|
|
#ifdef DCACHE_LOG
|
|
if (!lstrcmpi(pcszFile, &cszLogFile[1]))
|
|
return FALSE;
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef DCACHE_LOG
|
|
|
|
static void WriteDCacheLog(PCSTR pcszFn, UINT uFlags)
|
|
{
|
|
const char cszLineFmtCif[]="Deleting CIF file: %s\n";
|
|
const char cszLineFmtOther[]="Deleting non-CIF file: %s\n";
|
|
const char cszLineFmtClose[]="Closing log file\n";
|
|
const char cszLineFmtFound[]="Found file: %s\n";
|
|
const char cszLineFmtPresort[]="Found file(presorted): %s\n";
|
|
const char cszLineFmtSort[]="Found file(sorted): %s\n";
|
|
const char cszLineFmtNone[]="%s\n";
|
|
static FILE *fpLog=NULL;
|
|
char szLogFile[_MAX_PATH+1];
|
|
char szLine[_MAX_PATH+1];
|
|
|
|
if (uFlags == DCACHE_LOG_CLOSE)
|
|
{
|
|
if (fpLog)
|
|
{
|
|
fwrite(cszLineFmtClose, sizeof(char), strlen(cszLineFmtClose), fpLog);
|
|
fclose(fpLog);
|
|
fpLog=NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!fpLog)
|
|
{
|
|
PREF_GetTempPath(_MAX_PATH, szLogFile);
|
|
strcat(szLogFile, cszLogFile);
|
|
fpLog = fopen(szLogFile, "w");
|
|
}
|
|
|
|
wsprintf( szLine,
|
|
uFlags == DCACHE_LOG_CIF ? cszLineFmtCif :
|
|
uFlags == DCACHE_LOG_OTHER ? cszLineFmtOther :
|
|
uFlags == DCACHE_LOG_PRESORT ? cszLineFmtPresort :
|
|
uFlags == DCACHE_LOG_SORT ? cszLineFmtSort :
|
|
uFlags == DCACHE_LOG_FOUND ? cszLineFmtFound :
|
|
cszLineFmtNone,
|
|
pcszFn);
|
|
fwrite(szLine, sizeof(char), strlen(szLine), fpLog);
|
|
}
|
|
|
|
#endif /* DCACHE_LOG */
|
|
|
|
|
|
/*
|
|
* DCache flushing:
|
|
* There can be four kinds of files in the disk cache directory:
|
|
* 1) Random files that are not part of the disk cache (either user
|
|
* put them there or some errors). These files are the first to be
|
|
* thrown out.
|
|
* 2) Files that are in the CIF (dcache) that are not in the ram cache.
|
|
* These are the next files to be thrown out.
|
|
* 3) Files that are in the CIF and that are in the ram cache, and
|
|
* can be discarded from the ram cache (ex. images with zero refcount)
|
|
* These go next.
|
|
* 4) Files that are in the CIF and in the ram cache and cannot be
|
|
* discarded from ram to reduce memory (either they are currently
|
|
* visible or are being referenced by another doc. that cannot be
|
|
* discarded. These never get thrown out.
|
|
*/
|
|
|
|
static BOOL FDeleteDCacheRamCache(UINT uFlags);
|
|
|
|
static void CleanupDCacheLocal(UINT uFlags)
|
|
{
|
|
char szFileFind[_MAX_PATH+1];
|
|
char szFullFn[_MAX_PATH+1];
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA wfd;
|
|
HCURSOR hCursorPrev=NULL;
|
|
struct hash_table *pFileToURLHash = NULL;
|
|
DebugCode(PSTR pszFn;)
|
|
|
|
extern HCURSOR hCursorWorking;
|
|
|
|
#ifdef XX_DEBUG
|
|
XX_DebugMessage("Cleaning up DCache");
|
|
#endif
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return;
|
|
|
|
if (hCursorWorking)
|
|
hCursorPrev = SetCursor(hCursorWorking);
|
|
|
|
SetCurrentDirectory(gPrefs.szCacheLocation);
|
|
|
|
if (!FDCacheOverflow(uFlags))
|
|
goto LRet;
|
|
|
|
strcpy(szFileFind, cszFileFilter);
|
|
if ((hFind = FindFirstFile(szFileFind, &wfd)) == INVALID_HANDLE_VALUE)
|
|
goto LRet;
|
|
|
|
CreateCacheFileToURLHashTable( &pFileToURLHash );
|
|
|
|
do
|
|
{
|
|
/* Deal with:
|
|
* --random subdirectories that user may have created
|
|
* --readonly, hidden files
|
|
*/
|
|
if ( bStopCleaning )
|
|
break;
|
|
|
|
DebugCode(pszFn = wfd.cFileName);
|
|
GetFullDcPath(szFullFn, wfd.cFileName, NULL, _MAX_PATH+1);
|
|
if (FIgnoreDcFileFind(&wfd) || FInDCacheList(szFullFn,pFileToURLHash))
|
|
continue;
|
|
SetFileAttributes(szFullFn, FILE_ATTRIBUTE_NORMAL);
|
|
remove(szFullFn);
|
|
WriteDCacheLog(szFullFn, DCACHE_LOG_OTHER);
|
|
} while (FindNextFile(hFind, &wfd));
|
|
FindClose(hFind);
|
|
|
|
DestroyCacheFileToURLHashTable( pFileToURLHash );
|
|
|
|
/* REVIEW (deepaka): make FDCacheOverflow incremental so it doesn't
|
|
* recompute used space from scratch.
|
|
*/
|
|
if ( bStopCleaning ||
|
|
(uFlags != DCACHE_WATERMARK_ZERO && !FDCacheOverflow(DCACHE_WATERMARK_LOW))
|
|
)
|
|
goto LRet;
|
|
|
|
FDeleteDCacheRamCache(uFlags);
|
|
|
|
LRet:
|
|
if (hCursorPrev)
|
|
SetCursor(hCursorPrev);
|
|
}
|
|
|
|
|
|
typedef struct _FRESHCHECKEDITEM
|
|
{
|
|
PSTR pszUrl;
|
|
struct _FRESHCHECKEDITEM *pfciNext;
|
|
} FCI, FRESHCHECKEDITEM, *PFRESHCHECKEDITEM, *PFCI;
|
|
|
|
/* Items that are not in cif but were earlier and have been now
|
|
* thrown out because we got a fresher version of that url.
|
|
* Need to propagate this info. into the cif so that we can
|
|
* use the "Update url once per session" feature from the Advanced dlg
|
|
*/
|
|
static PFCI pfcItems=NULL;
|
|
|
|
static void AddToFreshnessCheckedList(PCSTR pcszURL)
|
|
{
|
|
PFCI pfcNewItem;
|
|
|
|
if (!(pfcNewItem = GTR_MALLOC(sizeof(struct _FRESHCHECKEDITEM))))
|
|
return;
|
|
if (!(pfcNewItem->pszUrl = GTR_strdup(pcszURL)))
|
|
GTR_FREE(pfcNewItem);
|
|
pfcNewItem->pfciNext = pfcItems;
|
|
|
|
pfcItems = pfcNewItem;
|
|
}
|
|
|
|
static void PropagateFreshnessToCif(void)
|
|
{
|
|
PFCI pfciCur;
|
|
struct _CacheFileInformation *pFileInfo;
|
|
|
|
while (pfciCur = pfcItems)
|
|
{
|
|
pfcItems = pfcItems->pfciNext;
|
|
|
|
XX_Assert(pfciCur->pszUrl, (""));
|
|
|
|
if (Hash_Find(pDynFileHash, pfciCur->pszUrl, NULL, (void **) &pFileInfo) != -1)
|
|
pFileInfo->fCheckedForFreshness=TRUE;
|
|
GTR_FREE(pfciCur->pszUrl);
|
|
GTR_FREE(pfciCur);
|
|
}
|
|
|
|
pfcItems = NULL;
|
|
}
|
|
|
|
void UpdateDCacheFreshness(PCSTR pcszURL, BOOL fDel)
|
|
{
|
|
struct _CacheFileInformation *pFileInfo;
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return;
|
|
|
|
#ifdef DCACHE_LOG
|
|
{
|
|
const char cszFreshDelFmt[]="Url: %s has been modified. Deleting from dcache and fetching from wire.";
|
|
const char cszFreshMarkFmt[]="Url: %s has not been modified. Fetching from dcache.";
|
|
char szT[_MAX_PATH+1];
|
|
PCSTR pcszFmt;
|
|
|
|
if (Hash_Find(pDynFileHash, pcszURL, NULL, (void **) &pFileInfo) != -1)
|
|
{
|
|
if (fDel)
|
|
pcszFmt = cszFreshDelFmt;
|
|
else
|
|
pcszFmt = cszFreshMarkFmt;
|
|
wsprintf(szT, pcszFmt, pcszURL);
|
|
WriteDCacheLog(szT, DCACHE_LOG_NOFMT);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (fDel)
|
|
{
|
|
AddToFreshnessCheckedList(pcszURL);
|
|
FlushDCacheEntry(pcszURL);
|
|
}
|
|
else
|
|
{
|
|
if (Hash_Find(pDynFileHash, pcszURL, NULL, (void **) &pFileInfo) != -1)
|
|
pFileInfo->fCheckedForFreshness=TRUE;
|
|
}
|
|
}
|
|
|
|
void ResetCIFEntryCurDoc(PCSTR pcszURL)
|
|
{
|
|
struct _CacheFileInformation *pFileInfo;
|
|
|
|
if ( gPrefs.bEnableDiskCache
|
|
&& Hash_Find(pDynFileHash, pcszURL, NULL, (void **) &pFileInfo) != -1)
|
|
{
|
|
pFileInfo->fCurDoc=FALSE;
|
|
}
|
|
}
|
|
|
|
static void SetCIFEntryRam(PCSTR pcszURL)
|
|
{
|
|
struct _CacheFileInformation *pFileInfo;
|
|
|
|
if ( gPrefs.bEnableDiskCache
|
|
&& Hash_Find(pDynFileHash, pcszURL, NULL, (void **) &pFileInfo) != -1)
|
|
{
|
|
pFileInfo->fRamDoc=TRUE;
|
|
}
|
|
}
|
|
|
|
static void ResetCIFEntryRam(void)
|
|
{
|
|
int i, iMax;
|
|
struct _CacheFileInformation *pFileInfo;
|
|
|
|
AssertDiskCacheEnabled();
|
|
|
|
for (i=0, iMax=Hash_Count(pDynFileHash); i<iMax; i++)
|
|
{
|
|
if (Hash_GetIndexedEntry(pDynFileHash, i, NULL, NULL, &pFileInfo) != -1)
|
|
pFileInfo->fRamDoc = FALSE;
|
|
else
|
|
XX_Assert(FALSE, (""));
|
|
}
|
|
}
|
|
|
|
static PCSTR PcszURLFromRamItem(RAMITEM *pRamItem)
|
|
{
|
|
if (pRamItem->fImage)
|
|
return (pRamItem->pImgInfo->actualURL);
|
|
return (pRamItem->pw3doc->szActualURL);
|
|
}
|
|
|
|
static UINT UDeleteNonRamDCache(UINT uFlags)
|
|
{
|
|
int i;
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
int iMax;
|
|
#endif
|
|
PSTR pszUrl;
|
|
struct _CacheFileInformation *pFileInfo;
|
|
UINT uRet=FLUSH_DCACHE_CONTINUE;
|
|
DebugCode(PSTR pszFn);
|
|
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
for (i=0, iMax=Hash_Count(pDynFileHash); i<iMax; i++)
|
|
{
|
|
Hash_GetIndexedEntry(pDynFileHash, i, &pszUrl, NULL, &pFileInfo);
|
|
WriteDCacheLog(pFileInfo->pszPath, DCACHE_LOG_PRESORT);
|
|
}
|
|
#endif
|
|
|
|
Hash_SortByDataAscending(pDynFileHash);
|
|
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
for (i=0, iMax=Hash_Count(pDynFileHash); i<iMax; i++)
|
|
{
|
|
Hash_GetIndexedEntry(pDynFileHash, i, &pszUrl, NULL, &pFileInfo);
|
|
WriteDCacheLog(pFileInfo->pszPath, DCACHE_LOG_SORT);
|
|
}
|
|
#endif
|
|
|
|
for (i=Hash_Count(pDynFileHash)-1; i>=0; i--)
|
|
{
|
|
DebugCode(pFileInfo=NULL);
|
|
Hash_GetIndexedEntry(pDynFileHash, 0, &pszUrl, NULL, &pFileInfo);
|
|
XX_Assert(pFileInfo, (""));
|
|
DebugCode(pszFn = pFileInfo->pszPath);
|
|
|
|
// WriteDCacheLog(pFileInfo->pszPath, DCACHE_LOG_FOUND);
|
|
/* As soon as we hit the first doc. in ram, we know we
|
|
* are done. All the remaining docs. will also be in ram.
|
|
*/
|
|
if (pFileInfo->fRamDoc || pFileInfo->fCurDoc)
|
|
break;
|
|
|
|
if (FOkToDeleteDCacheFile(pFileInfo->pszPath, pszUrl))
|
|
{
|
|
if (FExistsFile(pFileInfo->pszPath, FALSE, NULL))
|
|
{
|
|
SetFileAttributes(pFileInfo->pszPath, FILE_ATTRIBUTE_NORMAL);
|
|
remove(pFileInfo->pszPath);
|
|
WriteDCacheLog(pFileInfo->pszPath, DCACHE_LOG_CIF);
|
|
}
|
|
Hash_DeleteIndexedEntry(pDynFileHash, 0);
|
|
|
|
if ( bStopCleaning ||
|
|
(uFlags != DCACHE_WATERMARK_ZERO && !FDCacheOverflow(DCACHE_WATERMARK_LOW))
|
|
)
|
|
{
|
|
uRet = FLUSH_DCACHE_DONE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return uRet;
|
|
}
|
|
|
|
static UINT UDeleteRamDCacheItem(RAMITEM *pRamItem, UINT uFlags)
|
|
{
|
|
/* if (w3Doc) FreeW3Doc
|
|
* Update CIF
|
|
* if DONE return DONE
|
|
* while there are images with refcount == 0
|
|
* Delete Image
|
|
* Update CIF
|
|
* If DONE return DONE
|
|
* return CONTINUE
|
|
*
|
|
* if (fImage)
|
|
* Make sure refcount == 0 before freeing it.
|
|
*/
|
|
int ndx;
|
|
|
|
XX_Assert(!pRamItem->fImage, (""));
|
|
XX_Assert(pRamItem->pw3doc, (""));
|
|
XX_Assert(pRamItem->tw, (""));
|
|
if (!pRamItem->pw3doc)
|
|
return FLUSH_DCACHE_CONTINUE;
|
|
|
|
ndx = Hash_FindByData(&pRamItem->tw->doc_cache, NULL, NULL, pRamItem->pw3doc);
|
|
XX_Assert(ndx != -1, (""));
|
|
W3Doc_FreeContents(pRamItem->tw, pRamItem->pw3doc);
|
|
Hash_DeleteIndexedEntry(&pRamItem->tw->doc_cache, ndx);
|
|
GTR_FREE(pRamItem->pw3doc);
|
|
pRamItem->pw3doc = NULL;
|
|
|
|
/* throw away all images with zero refcount. */
|
|
Image_ReduceMemory(0, /*fOKToDelW3Docs=*/FALSE);
|
|
|
|
if ( uFlags != DCACHE_WATERMARK_ZERO
|
|
&& !FDCacheOverflow(DCACHE_WATERMARK_LOW))
|
|
return FLUSH_DCACHE_DONE;
|
|
|
|
return FLUSH_DCACHE_CONTINUE;
|
|
}
|
|
|
|
/*
|
|
* returns:
|
|
* -1 if RamItem1 < RamItem2
|
|
* 0 if RamItem1 = RamItem2
|
|
* +1 if RamItem1 > RamItem2
|
|
*/
|
|
static int CompRamItem(RAMITEM *pRamItem1, RAMITEM *pRamItem2)
|
|
{
|
|
/* For version 1, images are sorted so that in the victim list
|
|
* they come after w3docs in the dcache flushing algo.
|
|
*/
|
|
if (pRamItem1->fImage ^ pRamItem2->fImage)
|
|
return (pRamItem1->fImage - pRamItem2->fImage);
|
|
|
|
/* Throw out docs. that shouldn't be cached (per the server) first. */
|
|
if (pRamItem1->fNoDocCache ^ pRamItem2->fNoDocCache)
|
|
return (pRamItem1->fNoDocCache - pRamItem2->fNoDocCache);
|
|
|
|
/* Throw out item with older lastAccess stamp */
|
|
return CompareDCacheTime(pRamItem1->dctLastUsed, pRamItem2->dctLastUsed);
|
|
}
|
|
|
|
static BOOL FW3DocInRamItemList(RAMITEM **ppRamItem,
|
|
struct _www *pw3doc)
|
|
{
|
|
RAMITEM *pRamItemT;
|
|
|
|
XX_Assert(ppRamItem, (""));
|
|
for (pRamItemT = *ppRamItem; pRamItemT; pRamItemT = pRamItemT->next)
|
|
{
|
|
if (pRamItemT->pw3doc == pw3doc)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL FInsertW3Doc( RAMITEM **ppRamItem,
|
|
struct _www *pw3doc,
|
|
struct Mwin *tw)
|
|
{
|
|
RAMITEM *pRamItemNew;
|
|
RAMITEM *pRamItemT;
|
|
struct _CacheFileInformation *pFileInfo=NULL;
|
|
|
|
XX_Assert(ppRamItem, (""));
|
|
|
|
/* Bulletproofing */
|
|
if (!gPrefs.bEnableDiskCache)
|
|
goto LInserted;
|
|
|
|
/* Don't insert same w3doc twice.
|
|
* Also: play it safe and don't insert current w3doc either. Screws up
|
|
* TW_DrawBackground.
|
|
*/
|
|
if ( FW3DocInRamItemList(ppRamItem, pw3doc)
|
|
|| tw->w3doc == pw3doc)
|
|
goto LInserted;
|
|
|
|
if (!(pRamItemNew = (RAMITEM *)GTR_MALLOC(sizeof(RAMITEM))))
|
|
return FALSE;
|
|
|
|
pRamItemNew->fImage=FALSE;
|
|
pRamItemNew->pw3doc = pw3doc;
|
|
pRamItemNew->tw = tw;
|
|
pRamItemNew->next = NULL;
|
|
if (Hash_Find(pDynFileHash, PcszURLFromRamItem(pRamItemNew), NULL, (void **) &pFileInfo) != -1)
|
|
{
|
|
pRamItemNew->fNoDocCache = pFileInfo->fNoDocCache;
|
|
pRamItemNew->dctLastUsed = pFileInfo->dctLastUsed;
|
|
pRamItemNew->dwSize = (DWORD)pFileInfo->lFilesize;
|
|
}
|
|
else
|
|
{
|
|
pRamItemNew->fNoDocCache = FALSE;
|
|
pRamItemNew->dctLastUsed.dwDCacheTime1 =
|
|
pRamItemNew->dctLastUsed.dwDCacheTime2 = 0;
|
|
pRamItemNew->dwSize = 0;
|
|
}
|
|
|
|
pRamItemT = *ppRamItem;
|
|
if (!pRamItemT || CompRamItem(pRamItemNew, pRamItemT) < 0)
|
|
{
|
|
pRamItemNew->next = pRamItemT;
|
|
*ppRamItem = pRamItemNew;
|
|
goto LInserted;
|
|
}
|
|
|
|
while (pRamItemT->next)
|
|
{
|
|
if (CompRamItem(pRamItemNew, pRamItemT->next) < 0)
|
|
{
|
|
pRamItemNew->next = pRamItemT->next;
|
|
pRamItemT->next = pRamItemNew;
|
|
goto LInserted;
|
|
}
|
|
pRamItemT = pRamItemT->next;
|
|
}
|
|
|
|
pRamItemT->next = pRamItemNew;
|
|
LInserted:
|
|
return TRUE;
|
|
}
|
|
|
|
#define FBuildW3DocList(ppRamItem) \
|
|
W3Doc_ReduceMemory(0, ppRamItem)
|
|
|
|
static BOOL FCreateRamCacheVictimList(RAMITEM **ppRamItem)
|
|
{
|
|
/* For version 1, we are going to simply walk down the list
|
|
* of w3docs and insert them into the ram list. Everytime we
|
|
* free a w3doc, we walk the list of images in cache and free
|
|
* the ones with a zero ref.count. For ver 2, we can implement
|
|
* a more sophisticated algo. with this same framework.
|
|
*/
|
|
|
|
if (!FBuildW3DocList(ppRamItem))
|
|
return FALSE; //Couldn't create list of w3doc's in ram
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void FreeRamCacheVictimList(RAMITEM *pRamItem)
|
|
{
|
|
RAMITEM *pRamItemCur;
|
|
|
|
while (pRamItemCur = pRamItem)
|
|
{
|
|
pRamItem = pRamItem->next;
|
|
GTR_FREE(pRamItemCur);
|
|
}
|
|
}
|
|
|
|
static BOOL FDeleteDCacheRamCache(UINT uFlags)
|
|
{
|
|
RAMITEM *pRamItem=NULL;
|
|
RAMITEM *pRamItemT;
|
|
BOOL fRet = TRUE;
|
|
int i, iMax;
|
|
PSTR pszUrl;
|
|
struct Mwin *mw;
|
|
|
|
AssertDiskCacheEnabled();
|
|
|
|
if (!FCreateRamCacheVictimList(&pRamItem))
|
|
{
|
|
fRet=FALSE;
|
|
goto LDone;
|
|
}
|
|
|
|
/* reset the fRamDoc values of all the CIF files */
|
|
ResetCIFEntryRam();
|
|
/* Set all files in ram cache to be marked in CIF as being in ram. */
|
|
for (mw = Mlist; mw && mw->wintype == GHTML; mw = mw->next)
|
|
{
|
|
for (i=0, iMax=Hash_Count(&mw->doc_cache); i<iMax; i++)
|
|
{
|
|
Hash_GetIndexedEntry(&mw->doc_cache, i, &pszUrl, NULL, NULL);
|
|
XX_Assert(pszUrl, (""));
|
|
SetCIFEntryRam(pszUrl);
|
|
}
|
|
}
|
|
|
|
if (UDeleteNonRamDCache(uFlags) == FLUSH_DCACHE_DONE)
|
|
goto LDone;
|
|
|
|
for (pRamItemT = pRamItem; pRamItemT; pRamItemT = pRamItemT->next)
|
|
{
|
|
if (UDeleteRamDCacheItem(pRamItemT, uFlags) == FLUSH_DCACHE_DONE)
|
|
break;
|
|
}
|
|
|
|
LDone:
|
|
FreeRamCacheVictimList(pRamItem);
|
|
return fRet;
|
|
}
|
|
|
|
/* if (elem1 ">" elem 2) return 1;
|
|
* if (elem1 "<" elem 2) return -1;
|
|
* return 0;
|
|
*
|
|
* The dcache flushing algo. will throw out the "smaller" entry first.
|
|
*/
|
|
int _cdecl x_compare_entries_dcache_ascending(const void *elem1, const void *elem2)
|
|
{
|
|
struct _CacheFileInformation *pFileInfo1, *pFileInfo2;
|
|
|
|
pFileInfo1 = ((struct hash_entry *) elem1)->data;
|
|
pFileInfo2 = ((struct hash_entry *) elem2)->data;
|
|
|
|
if (pFileInfo1->fCurDoc ^ pFileInfo2->fCurDoc)
|
|
return (pFileInfo1->fCurDoc - pFileInfo2->fCurDoc);
|
|
|
|
if (pFileInfo1->fRamDoc ^ pFileInfo2->fRamDoc)
|
|
return (pFileInfo1->fRamDoc - pFileInfo2->fRamDoc);
|
|
|
|
if (pFileInfo1->fNoDocCache ^ pFileInfo2->fNoDocCache)
|
|
return (pFileInfo2->fNoDocCache - pFileInfo1->fNoDocCache);
|
|
|
|
/* file1 was accessed more recently than file2. It should be deleted
|
|
* after file2. Hence file1 > file2.
|
|
*/
|
|
return CompareDCacheTime(pFileInfo2->dctLastUsed, pFileInfo1->dctLastUsed);
|
|
}
|
|
|
|
static DWORD DwDirSize(PCSTR pcszDir, DWORD dwClusterSize)
|
|
{
|
|
char const cszStarDotStar[] = "\\*.*";
|
|
char const cszBSlash[] = "\\";
|
|
HANDLE hfind;
|
|
char szPath[MAX_PATH];
|
|
WIN32_FIND_DATA wfd;
|
|
DWORD dwSize = 0;
|
|
#ifdef XX_DEBUG
|
|
static BOOL fShowAssert=TRUE;
|
|
static BOOL fAssertShown=FALSE;
|
|
#endif
|
|
|
|
|
|
strcpy(szPath, pcszDir);
|
|
strcat(szPath, cszStarDotStar);
|
|
hfind = FindFirstFile(szPath, &wfd);
|
|
if (hfind != INVALID_HANDLE_VALUE)
|
|
{
|
|
do
|
|
{
|
|
if (wfd.cFileName[0] != '.')
|
|
{
|
|
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
#ifdef XX_DEBUG
|
|
if (fShowAssert)
|
|
{
|
|
// XX_Assert(FALSE, ("\nUnexpected subdir %s found in %s", wfd.cFileName, pcszDir));
|
|
fAssertShown=TRUE;
|
|
}
|
|
#endif
|
|
strcpy(szPath, pcszDir);
|
|
strcat(szPath, "\\");
|
|
strcat(szPath, wfd.cFileName);
|
|
// DwDirSize(szPath, dwClusterSize);
|
|
}
|
|
else
|
|
{
|
|
/* Cluster rounded */
|
|
XX_Assert(dwClusterSize, (""));
|
|
dwSize += (wfd.nFileSizeLow + dwClusterSize) -
|
|
(wfd.nFileSizeLow % dwClusterSize);
|
|
}
|
|
}
|
|
} while (FindNextFile(hfind, &wfd));
|
|
|
|
FindClose(hfind);
|
|
#ifdef XX_DEBUG
|
|
if (fAssertShown)
|
|
fShowAssert=FALSE;
|
|
#endif
|
|
}
|
|
|
|
return dwSize;
|
|
}
|
|
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
BOOL CALLBACK TestDCacheOptionsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
long hnpHigh, hnpLow;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SendDlgItemMessage( hDlg, IDC_ADVANCED_TEST_HIGHWATER, UDM_SETRANGE, 0,
|
|
MAKELPARAM(100, 0));
|
|
SendDlgItemMessage( hDlg, IDC_ADVANCED_TEST_HIGHWATER, UDM_SETPOS,
|
|
0, (LPARAM) MAKELONG((short) gPrefs.iCachePercentHigh, 0));
|
|
SendDlgItemMessage( hDlg, IDC_ADVANCED_TEST_LOWWATER, UDM_SETRANGE, 0,
|
|
MAKELPARAM(100, 0));
|
|
SendDlgItemMessage( hDlg, IDC_ADVANCED_TEST_LOWWATER, UDM_SETPOS,
|
|
0, (LPARAM) MAKELONG((short) gPrefs.iCachePercentLow, 0));
|
|
/* By default (for this dlg. only) ignore overflow condition. */
|
|
CheckDlgButton( hDlg, IDC_ADVANCED_TEST_OVERFLOW, TRUE);
|
|
}
|
|
// fall through
|
|
return TRUE;
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
NMHDR *lpnm = (NMHDR *) lParam;
|
|
|
|
switch (lpnm->code)
|
|
{
|
|
case PSN_QUERYCANCEL:
|
|
SetWindowLong( hDlg, DWL_MSGRESULT, FALSE );
|
|
return TRUE;
|
|
|
|
case PSN_KILLACTIVE:
|
|
SetWindowLong( hDlg,DWL_MSGRESULT, FALSE );
|
|
return TRUE;
|
|
|
|
case PSN_RESET:
|
|
break;
|
|
|
|
case PSN_APPLY:
|
|
{
|
|
// Get history field values
|
|
hnpHigh = SendDlgItemMessage( hDlg, IDC_ADVANCED_TEST_HIGHWATER, UDM_GETPOS, 0, 0 );
|
|
hnpLow = SendDlgItemMessage( hDlg, IDC_ADVANCED_TEST_LOWWATER, UDM_GETPOS, 0, 0 );
|
|
if ( HIWORD(hnpHigh) == 0 )
|
|
gPrefs.iCachePercentHigh = LOWORD(hnpHigh);
|
|
if ( HIWORD(hnpLow) == 0 )
|
|
gPrefs.iCachePercentLow = LOWORD(hnpLow);
|
|
if (gPrefs.iCachePercentHigh < gPrefs.iCachePercentLow)
|
|
gPrefs.iCachePercentHigh = gPrefs.iCachePercentLow;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDC_ADVANCED_TEST_HIGHWATER:
|
|
case IDC_ADVANCED_TEST_LOWWATER:
|
|
{
|
|
hnpHigh = SendDlgItemMessage( hDlg, IDC_ADVANCED_TEST_HIGHWATER, UDM_GETPOS, 0, 0 );
|
|
hnpLow = SendDlgItemMessage( hDlg, IDC_ADVANCED_TEST_LOWWATER, UDM_GETPOS, 0, 0 );
|
|
if ( (HIWORD(hnpHigh) == 0)
|
|
&& (HIWORD(hnpLow) == 0))
|
|
{
|
|
if (LOWORD(hnpHigh) < LOWORD(hnpLow))
|
|
SendDlgItemMessage( hDlg, IDC_ADVANCED_TEST_HIGHWATER, UDM_SETPOS,
|
|
0, (LPARAM) MAKELONG((short) LOWORD(hnpLow), 0));
|
|
else if (LOWORD(hnpLow) > LOWORD(hnpHigh))
|
|
SendDlgItemMessage( hDlg, IDC_ADVANCED_TEST_LOWWATER, UDM_SETPOS,
|
|
0, (LPARAM) MAKELONG((short) LOWORD(hnpHigh), 0));
|
|
}
|
|
break;
|
|
}
|
|
case IDC_ADVANCED_TEST_FLUSH_DCACHE_THREAD:
|
|
{
|
|
/* Clean up dcache in a thread (retail scenario) */
|
|
fCheckDCacheOverflow = !IsDlgButtonChecked( hDlg, IDC_ADVANCED_TEST_OVERFLOW );
|
|
CleanupDCache(DCACHE_WATERMARK_MAX);
|
|
fCheckDCacheOverflow = TRUE;
|
|
break;
|
|
}
|
|
case IDC_ADVANCED_TEST_FLUSH_DCACHE:
|
|
{
|
|
/* Clean up dcache in current thread itself. Just for testing.
|
|
*
|
|
* Should we put up a message box saying that we're waiting?
|
|
*/
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Waiting for hCIFMutex");
|
|
#endif
|
|
WaitForSingleObject(hCIFMutex, INFINITE);
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Got hCIFMutex");
|
|
#endif
|
|
fCheckDCacheOverflow = !IsDlgButtonChecked( hDlg, IDC_ADVANCED_TEST_OVERFLOW );
|
|
CleanupDCacheLocal(DCACHE_WATERMARK_MAX);
|
|
ReleaseMutex(hCIFMutex);
|
|
#ifdef TEMP0
|
|
XX_DebugMessage("Released hCIFMutex");
|
|
#endif
|
|
fCheckDCacheOverflow = TRUE;
|
|
break;
|
|
}
|
|
case IDC_ADVANCED_TEST_REREAD_CIF:
|
|
{
|
|
/* Discard current contents of (in mem.) cif file
|
|
* and re-read from disk
|
|
*/
|
|
ReReadDynCIF();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Stolen from Advanced Options dialog procs. */
|
|
#define NUM_OPTION_PAGES 1
|
|
|
|
void CC_OnItem_TestDCacheOptions(HWND hWnd)
|
|
{
|
|
const char cszCaption[] = "Test Disk Cache Options";
|
|
HPROPSHEETPAGE hOptPage[NUM_OPTION_PAGES]; // array to hold handles to pages
|
|
PROPSHEETPAGE psPage; // struct used to create prop sheet pages
|
|
PROPSHEETHEADER psHeader; // struct used to run property sheet
|
|
UINT nPageIndex;
|
|
|
|
// zero out structures
|
|
memset(&hOptPage,0,sizeof(hOptPage));
|
|
memset(&psPage,0,sizeof(psPage));
|
|
memset(&psHeader,0,sizeof(psHeader));
|
|
|
|
// fill out common data property sheet page struct
|
|
psPage.dwSize = sizeof(psPage);
|
|
psPage.dwFlags = 0;
|
|
psPage.hInstance = wg.hInstance;
|
|
|
|
// create a property sheet page for each page
|
|
for (nPageIndex = 0;nPageIndex < NUM_OPTION_PAGES;nPageIndex++) {
|
|
switch ( nPageIndex )
|
|
{
|
|
case 0:
|
|
psPage.pfnDlgProc = TestDCacheOptionsDlgProc;
|
|
psPage.pszTemplate = MAKEINTRESOURCE( IDD_ADVANCED_TEST );
|
|
break;
|
|
}
|
|
|
|
// set a pointer to the PAGEINFO struct as the private data for this
|
|
// page
|
|
psPage.lParam = (LPARAM) nPageIndex;
|
|
|
|
hOptPage[nPageIndex] = CreatePropertySheetPage(&psPage);
|
|
|
|
if (!hOptPage[nPageIndex]) {
|
|
UINT nFreeIndex;
|
|
for (nFreeIndex=0;nFreeIndex<nPageIndex;nFreeIndex++)
|
|
DestroyPropertySheetPage(hOptPage[nFreeIndex]);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// fill out property sheet header struct
|
|
psHeader.dwSize = sizeof(psHeader);
|
|
psHeader.dwFlags = 0;
|
|
psHeader.hwndParent = hWnd;
|
|
psHeader.hInstance = wg.hInstance;
|
|
psHeader.nPages = NUM_OPTION_PAGES;
|
|
psHeader.phpage = hOptPage;
|
|
psHeader.pszCaption = cszCaption;
|
|
|
|
PropertySheet(&psHeader);
|
|
|
|
}
|
|
|
|
|
|
#endif /* TEST_DCACHE_OPTIONS */
|
|
|