Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2341 lines
61 KiB

/*
Enhanced NCSA Mosaic from Spyglass
"Guitar"
Copyright 1994 Spyglass, Inc.
All Rights Reserved
Author(s):
Albert Lee [email protected]
Eric W. Sink [email protected]
*/
#include "all.h"
#define STATE_FILE_STREAMINIT (STATE_OTHER + 1)
#define STATE_FILE_COPYING (STATE_OTHER + 2)
#ifdef WIN32
#define PATH_SEP '\\'
#endif
#ifdef UNIX
#define PATH_SEP '/'
#endif
#ifdef MAC
#define PATH_SEP ':'
#endif
#ifdef MAC
#include "resequ.h"
int BuildAliasList (short resRefNum);
int ReadCacheIndexFiles (short resRefNum);
#endif
/* Need this to possibly map to notcp.htm */
extern BOOL bNetwork;
/*****************************************************************************
external functions
*****************************************************************************/
extern int
HTLoadFile_Async_SetFileInfo
(struct Mwin* tw,
struct Data_LoadFile* pData,
char* pszURL,
char** pszLocalname);
extern int
HTLoadFile_Async_Init
(struct Mwin *tw,
void **ppInfo,
int openType);
extern int
HTLoadFile_Async_File_Copy
(struct Mwin *tw,
struct Data_LoadFile *pData);
extern int
HTLoadFile_Async_File_StreamInit
(struct Mwin *tw,
struct Data_LoadFile *pData);
extern int
HTLoadFile_Async_Abort
(struct Mwin *tw,
struct Data_LoadFile *pData);
static char *ResolveFilename(char *pFilename);
/*****************************************************************************
global variables
*****************************************************************************/
struct AliasMap *pGlobalAliasList; /* List containing global/drive substitutions $(CDROMx), $(EXEDIR), etc... */
struct AliasMap *pIndexAliasList; /* List containing user-defined, index-specific substitution rules */
static struct hash_table *pFileHash; /* Hash table containing file substitution rules */
static struct CacheRuleList *pRuleList; /* Linked list of rules */
static BOOL bInitialized = FALSE; /* Flag indicating initialization */
static unsigned long gNumBytesInMainCache; /* Number of bytes currently in the main cache */
/*****************************************************************************
global functions
*****************************************************************************/
unsigned long DCACHE_CurrentCacheSize();
BOOL DCACHE_MakeRoomFor(unsigned long expected_length);
void DCACHE_GetMainCacheIndexFileName(char *s);
void DCACHE_DestroyCacheFileInformation(struct CacheFileInformation *cfi);
void DCACHE_SaveMainCache(void);
void DCACHE_GetNewCacheObjectFilename(char *s);
int DCACHE_FinishNewCacheEntry(struct CacheFileInformation *cfi, char *url);
int DCACHE_ChooseItemForPurging(unsigned long space_needed);
BOOL DCACHE_DeleteIndexedItem(int i);
BOOL DCACHE_LooksLikeCacheFile(HT_DirEntry *dir_ent);
void DCACHE_FormFullPath(char *buf, char *dir, char *basename);
void DCACHE_GarbageCollect(void);
static char *MakeURLFromLocalFile(char *pszLocal);
#ifdef WIN32
void VerifyMainCacheDir(void);
#endif /* WIN32 */
/*
InitializeDiskCache
Initializes the local disk cache internal structures.
This function should be called once when Mosaic starts.
*/
BOOL InitializeDiskCache(void)
{
if (bInitialized)
return TRUE;
//bInitialized = TRUE;
gNumBytesInMainCache = 0;
if (!gPrefs.bEnableDiskCache)
return TRUE;
bInitialized = TRUE;
pFileHash = Hash_Create();
if (!pFileHash)
return FALSE;
pRuleList = NULL;
pGlobalAliasList = NULL;
pIndexAliasList = NULL;
#ifdef WIN32
BuildAliasList();
DOS_EnforceEndingSlash(gPrefs.szMainCacheDir);
VerifyMainCacheDir();
DOS_EnforceEndingSlash(gPrefs.szMainCacheDir);
ReadCacheIndexFiles();
#endif /* WIN32 */
#ifdef MAC
{
char path[256];
char szMainCacheIndexFile[_MAX_PATH + 1];
(void) BuildAliasList (MacGlobals.prefref); /* preference file takes precedence */
(void) BuildAliasList (MacGlobals.applref); /* over application file */
PathNameFromDirID (0, MacGlobals.gCurrentDirectory, path);
(void) AddHomeDirToCacheList (path);
DCACHE_GetMainCacheIndexFileName (szMainCacheIndexFile);
if (szMainCacheIndexFile[0])
ProcessIndexFile (szMainCacheIndexFile, TRUE);
(void) ReadCacheIndexFiles (MacGlobals.applref);
(void) ReadCacheIndexFiles (MacGlobals.prefref);
}
#endif /* MAC */
/*
This line verifies that the main cache is not already bigger than it's supposed
to be
*/
DCACHE_MakeRoomFor(0);
return TRUE;
}
/*
TerminateDiskCache
Frees all internal structures. This function should
be called when Mosaic is shutting down.
*/
void TerminateDiskCache(void)
{
struct CacheRuleList *p, *pNext;
//if (!bInitialized || !gPrefs.bEnableDiskCache)
// return;
if (!bInitialized)
{
return;
}
if (gPrefs.bClearMainCacheOnExit)
{
DCACHE_ClearMainCache();
}
else
{
DCACHE_SaveMainCache();
#ifndef MAC /* This is way to so on the Mac */
DCACHE_GarbageCollect();
#endif
}
if (pGlobalAliasList)
{
FreeAliasMap(pGlobalAliasList);
pGlobalAliasList = NULL;
}
if (pIndexAliasList)
{
FreeAliasMap(pIndexAliasList);
pIndexAliasList = NULL;
}
if (pFileHash)
{
int i;
int count;
struct CacheFileInformation *cfi;
count = Hash_Count(pFileHash);
for (i=0; i<count; i++)
{
Hash_GetIndexedEntry(pFileHash, i, NULL, NULL, (void **) &cfi);
if (cfi)
{
DCACHE_DestroyCacheFileInformation(cfi);
}
}
Hash_Destroy(pFileHash);
}
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;
}
bInitialized = FALSE;
}
/*
FreeAliasMap
Frees all memory blocks associated with an alias list.
BuildAliasList is platform-specific.
*/
void FreeAliasMap(struct AliasMap *pList)
{
struct AliasMap *p;
struct AliasMap *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;
}
}
#ifdef WIN32
BOOL is_directory(char *path)
{
DWORD dw;
dw = GetFileAttributes(path);
if (dw == 0xffffffff)
{
return FALSE;
}
if (dw & FILE_ATTRIBUTE_DIRECTORY)
{
return TRUE;
}
return FALSE;
}
/*
gPrefs.szMainCacheDir has already been read from preferences (prefs.c), but
the dir might contain $(EXEDIR). Also, the dir might not already exist.
*/
void VerifyMainCacheDir(void)
{
char *p;
if (gPrefs.szMainCacheDir[0])
{
p = ResolveFilename(gPrefs.szMainCacheDir);
if (p)
{
strcpy(gPrefs.szMainCacheDir, p);
GTR_FREE(p);
}
if (!is_directory(gPrefs.szMainCacheDir))
{
char *p;
char *q;
char szPath[_MAX_PATH+1];
XX_DMsg(DBG_DCACHE, ("VerifyMainCacheDir: %s is not a directory.\n", gPrefs.szMainCacheDir));
p = gPrefs.szMainCacheDir;
q = szPath;
while (*p)
{
*q++ = *p++;
*q = 0;
if ((*p == '\\') && (*(q-1) != ':'))
{
if (!is_directory(szPath))
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
CreateDirectory(szPath, &sa);
}
}
}
if (!is_directory(gPrefs.szMainCacheDir))
{
gPrefs.szMainCacheDir[0] = 0;
}
}
}
}
#endif /* WIN32 */
void DCACHE_GetMainCacheIndexFileName(char *s)
{
#ifdef WIN32
if (gPrefs.szMainCacheDir[0])
{
strcpy(s, gPrefs.szMainCacheDir);
strcat(s, "MAIN.NDX");
}
else
{
s[0] = 0;
}
#endif /* WIN32 */
#ifdef MAC
s[0] = 0;
if (gPrefs.dcache_size_kilobytes)
{
strcpy(s, gPrefs.szMainCacheDir);
if (s[strlen(s)-1] == ':')
strcat(s, "main.ndx");
else
strcat(s, ":main.ndx");
}
#endif /* MAC */
#ifdef UNIX
s[0] = 0;
if (gPrefs.dcache_size_kilobytes)
{
strcpy(s, gPrefs.szMainCacheDir);
if (s[strlen(s)-1] == '/')
strcat(s, "main.ndx");
else
strcat(s, "/main.ndx");
}
#endif /* UNIX */
}
#ifdef MAC
BOOL DCACHE_LooksLikeCacheFileName(char *szFileName)
{
if (0 == strncmp(szFileName, "Cache_", 6))
{
char *p;
p = szFileName + strlen(szFileName) - 7;
if (0 == strcmp(p, ".Mosaic"))
{
return TRUE;
}
}
return FALSE;
}
#endif
/*
This function should look at the filename and try to determine
if it looks like it is or was once a cached object file. Even though people
should NOT be putting other stuff in their cache directory, nor should
they be setting their cache directory to a place where other files
exist, we don't want to delete anything we didn't create.
This function should return TRUE IF AND ONLY IF it is certain that
the dir_ent refers to a file which really is a cached object, because
the file may be deleted.
For now, the MAC and UNIX versions of this function always return FALSE,
meaning that garbage collection will essentially do nothing until this
function is implemented for the platform.
*/
BOOL DCACHE_LooksLikeCacheFile(HT_DirEntry *dir_ent)
{
#ifdef WIN32
if (0 == strncmp(dir_ent->name, "gtr", 3))
{
char *p;
p = dir_ent->name + strlen(dir_ent->name) - 4;
if (0 == strcmp(p, ".TMP"))
{
return TRUE;
}
}
return FALSE;
#endif /* WIN32 */
#ifdef MAC
return DCACHE_LooksLikeCacheFileName (dir_ent->name);
#endif /* MAC */
#ifdef UNIX
return FALSE; /* TODO fix comment above when you change this */
#endif /* UNIX */
}
void DCACHE_FormFullPath(char *buf, char *dir, char *basename)
{
#ifdef WIN32
strcpy(buf, dir);
DOS_EnforceEndingSlash(buf);
strcat(buf, basename);
#endif /* WIN32 */
#ifdef UNIX
strcpy(buf, dir);
/* TODO is the separator correct ? */
strcat(buf, basename);
#endif /* UNIX */
#ifdef MAC
strcpy(buf, dir);
/* TODO is the separator correct ? */
strcat(buf, basename);
#endif /* MAC */
}
/*
DCACHE_GarbageCollect simply scans the main disk cache directory and
looks for cache files which are not referenced in the index file, and deletes
them.
This function uses the routines declared in htdir.h
*/
void DCACHE_GarbageCollect(void)
{
void *dir;
HT_DirEntry dir_ent;
int i;
int count;
struct CacheFileInformation *cfi;
BOOL bFound;
dir = Dir_OpenDirectory(gPrefs.szMainCacheDir);
if (dir && pFileHash)
{
count = Hash_Count(pFileHash);
while (Dir_NextEntry(dir, &dir_ent))
{
if (dir_ent.type == HTDIR_FILE)
{
XX_DMsg(DBG_DCACHE, ("Found file %s of length %d in the main cache dir\n", dir_ent.name, dir_ent.size));
if (DCACHE_LooksLikeCacheFile(&dir_ent))
{
char buf[_MAX_PATH + 1];
DCACHE_FormFullPath(buf, gPrefs.szMainCacheDir, dir_ent.name);
/*
OK, we've found a file which looks like a valid cached object.
If it's not referenced in the main cache index, then let's delete it.
*/
bFound = FALSE;
for (i=0; i<count; i++)
{
cfi = NULL;
if (Hash_GetIndexedEntry(pFileHash, i, NULL, NULL, (void **) &cfi) >= 0)
{
if (cfi)
{
if (0 == strcmp(cfi->pszPath, buf))
{
bFound = TRUE;
break;
}
}
}
}
if (!bFound)
{
XX_DMsg(DBG_DCACHE, ("DCACHE_GarbageCollect: Removing file %s\n", buf));
if (remove(buf))
{
XX_DMsg(DBG_DCACHE, ("DCACHE_GarbageCollect: Failed removing file %s\n", buf));
}
}
}
}
}
Dir_CloseDirectory(dir);
}
}
void DCACHE_DestroyCacheFileInformation(struct CacheFileInformation *cfi)
{
GTR_FREE(cfi->pszPath);
GTR_FREE(cfi);
}
void DCACHE_FlushMainCache(void)
{
BOOL saveClear = TRUE;
saveClear = gPrefs.bClearMainCacheOnExit;
gPrefs.bClearMainCacheOnExit = TRUE;
TerminateDiskCache();
bInitialized = FALSE;
InitializeDiskCache();
#ifdef UNIX
/**
*** get local cache definitions
*** from the preference file FEATURE_LOCAL_CACHE will determine whether
*** they are read in.
**/
LoadCachePreferences(&gPrefs);
#endif
gPrefs.bClearMainCacheOnExit = saveClear;
}
void DCACHE_ClearMainCache(void)
{
char szMainCacheIndexFile[_MAX_PATH + 1];
DCACHE_GetMainCacheIndexFileName(szMainCacheIndexFile);
if (szMainCacheIndexFile[0])
{
int count;
int i;
struct CacheFileInformation *cfi;
count = Hash_Count(pFileHash);
for (i=0;;)
{
if (i>=count)
break;
Hash_GetIndexedEntry(pFileHash, i, NULL, NULL, (void **) &cfi);
if (cfi && cfi->bDynamic)
{
remove(cfi->pszPath);
gNumBytesInMainCache -= cfi->lFilesize;
/**
DCACHE_DestroyCacheFileInformation(cfi);
**/
DCACHE_DeleteIndexedItem(i);
count = Hash_Count(pFileHash);
i = 0;
}
else
i++;
}
remove(szMainCacheIndexFile);
/**
Hash_Destroy(pFileHash);
pFileHash = Hash_Create();
**/
}
}
/*
The format of a CIF 'file' entry is currently
F URL FileSize LastModified Expires LastUsed MIMEType Pathname HitCount Flags
*/
void DCACHE_SaveMainCache(void)
{
char szMainCacheIndexFile[_MAX_PATH + 1];
DCACHE_GetMainCacheIndexFileName(szMainCacheIndexFile);
if (szMainCacheIndexFile[0])
{
int count;
int i;
char *url;
struct CacheFileInformation *cfi;
FILE *fp;
fp = fopen(szMainCacheIndexFile, "w");
if (!fp)
{ /* TODO flag an error?? */
return;
}
#ifdef MAC /* need to set the creator and type */
SetGuitarFileType (szMainCacheIndexFile, 'TEXT');
#endif
count = Hash_Count(pFileHash);
for (i=0; i<count; i++)
{
Hash_GetIndexedEntry(pFileHash, i, &url, NULL, (void **) &cfi);
if (cfi && cfi->bDynamic)
{
fprintf(fp, "F\t%s\t%lu\t%lu\t%lu\t%lu\t%s\t%s\t%lu\t%lu\n",
url,
cfi->lFilesize,
cfi->tLastModified,
cfi->tExpires,
cfi->tLastUsed,
HTAtom_name(cfi->atomMIMEType),
cfi->pszPath,
cfi->nHits,
cfi->lFlags);
}
}
fclose(fp);
}
}
void DCACHE_RegisterCacheHit(struct CacheFileInformation *pFileInfo)
{
XX_DMsg(DBG_DCACHE, ("DCACHE_RegisterCacheHit: %s\n", pFileInfo->pszPath));
pFileInfo->nHits++;
pFileInfo->tLastUsed = time(NULL);
}
struct CacheFileInformation *DCACHE_CheckForCachedURL(char *pszURL, HTFormat *pMime, long *pFileLength, char **ppPath)
{
struct CacheFileInformation *pFileInfo;
/* Look through the file list first for resolution */
if (pFileHash && Hash_Find(pFileHash, pszURL, NULL, (void **) &pFileInfo) != -1)
{
if (!pFileInfo)
{
return NULL;
}
if (pFileInfo->tExpires && (pFileInfo->tExpires < time(NULL)))
{
/* The object is in the cache, but it has expired. Remove it. */
if (pFileInfo->bDynamic)
{
DCACHE_DeleteCachedURL(pszURL);
}
return NULL;
}
if (pMime)
*pMime = pFileInfo->atomMIMEType;
if (pFileLength)
*pFileLength = pFileInfo->lFilesize;
if (ppPath)
{
char *pszResolved;
pszResolved = GTR_strdup(pFileInfo->pszPath);
*ppPath = pszResolved;
}
return pFileInfo;
}
return NULL;
}
char *DCACHE_CheckForRuleMatch(char *pszURL, HTFormat *pMime, long *pFileLength, char **ppPath)
{
struct CacheRuleList *pRule;
char *pszResolved, *pszReturn;
/* Look through the rule list for any possible resolution */
pRule = pRuleList;
while (pRule)
{
if (strncmp(pszURL, pRule->pszOriginal, strlen(pRule->pszOriginal)) == 0)
{
pszResolved = GTR_MALLOC(strlen(pszURL) + strlen(pRule->pszReplacement) + 10);
if (pszResolved)
{
strcpy(pszResolved, pRule->pszReplacement);
strcat(pszResolved, &pszURL[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;
else
GTR_FREE(pszResolved);
return pszReturn;
}
else
{
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return NULL;
}
}
pRule = pRule->next;
}
return NULL;
}
/*
This is called only to retrieve an appropriate filename for
a newly cached object to be written into.
*/
void DCACHE_GetNewCacheObjectFilename(char *s)
{
#ifdef WIN32
if (gPrefs.szMainCacheDir[0])
{
GetTempFileName(gPrefs.szMainCacheDir, "gtr", 0, s);
}
else
{
s[0] = 0;
}
#endif /* WIN32 */
#ifdef MAC
char name[32];
strcpy (s, gPrefs.szMainCacheDir);
sprintf (name, "%s_%08lx.Mosaic", "Cache", TickCount());
strcat (s, name);
#endif /* MAC */
#ifdef UNIX
s[0] = 0;
if (gPrefs.szMainCacheDir[0])
{
xgtr_build_tempfile_name( (char *)gPrefs.szMainCacheDir, (char *)NULL,
(char *)NULL, s);
}
#endif /* UNIX */
}
/*
This routine simply deletes an item from the main cache. It doesn't make
any decisions about which item to delete.
*/
BOOL DCACHE_DeleteIndexedItem(int i)
{
int count;
struct CacheFileInformation *cfi;
char *url;
count = Hash_Count(pFileHash);
if ((i >= 0) && (i < count))
{
Hash_GetIndexedEntry(pFileHash, i, &url, NULL, (void **) &cfi);
if (cfi)
{
XX_DMsg(DBG_DCACHE, ("DCACHE_DeleteIndexedItem: deleting %s(%s) to gain %d bytes\n", url, cfi->pszPath, cfi->lFilesize));
gNumBytesInMainCache -= cfi->lFilesize;
remove(cfi->pszPath);
DCACHE_DestroyCacheFileInformation(cfi);
}
Hash_DeleteIndexedEntry(pFileHash, i);
return TRUE;
}
return FALSE;
}
/*
Given a URL, purge it from the cache.
*/
BOOL DCACHE_DeleteCachedURL(char *s)
{
int ndx;
ndx = Hash_Find(pFileHash, s, NULL, NULL);
if (ndx != -1)
{
return DCACHE_DeleteIndexedItem(ndx);
}
return FALSE;
}
/*
When the cache is too full to add something, then some item must
be purged from the cache. This is the code which decides which
item should be purged. It is based on the number of hits for a given
item, and also takes into account when the last time each item was
hit.
*/
int DCACHE_ChooseItemForPurging(unsigned long space_needed)
{
int count;
int i;
struct CacheFileInformation *cfi;
unsigned long min_hits;
time_t tLastUsed;
int candidate;
candidate = -1;
min_hits = 0xffffffff;
tLastUsed = 0xffffffff;
count = Hash_Count(pFileHash);
for (i=0; i<count; i++)
{
Hash_GetIndexedEntry(pFileHash, i, NULL, NULL, (void **) &cfi);
if (cfi)
{
if (cfi->bDynamic)
{
if (cfi->nHits == min_hits)
{
if (cfi->tLastUsed < tLastUsed)
{
candidate = i;
tLastUsed = cfi->tLastUsed;
}
}
else if (cfi->nHits < min_hits)
{
candidate = i;
tLastUsed = cfi->tLastUsed;
min_hits = cfi->nHits;
}
}
}
}
return candidate;
}
/*
This function simply makes sure the cache has room for
a new object of size expected_length. It purges items
until there is room
*/
BOOL DCACHE_MakeRoomFor(unsigned long expected_length)
{
unsigned long limit;
int ndx;
limit = gPrefs.dcache_size_kilobytes * 1024;
while ((gNumBytesInMainCache + expected_length) > limit)
{
XX_DMsg(DBG_DCACHE, ("DCACHE_MakeRoomFor(%d): gNumBytesInMainCache=%d limit=%d\n", expected_length, gNumBytesInMainCache, limit));
ndx = DCACHE_ChooseItemForPurging(expected_length);
if (!DCACHE_DeleteIndexedItem(ndx))
{
return FALSE;
}
}
return TRUE;
}
unsigned long DCACHE_GetCurrentSize(void)
{
return gNumBytesInMainCache;
}
/*
When a new object is being retrieved over HTTP, this function is called to
obtain a new cache entry for that object. The object isn't really available
in the cache until DCACHE_FinishNewCacheEntry() is called. This function
just makes sure there is room in the cache for the new item, and provides
the caller with a filename to write the new object into.
*/
struct CacheFileInformation *DCACHE_BeginNewCacheEntry(char *url, HTFormat atomMIMEType, unsigned long expected_length)
{
struct CacheFileInformation *cfi;
struct CacheFileInformation *old_cfi;
char buf[_MAX_PATH + 1];
old_cfi = DCACHE_CheckForCachedURL(url, NULL, NULL, NULL);
if (old_cfi)
{
if (!old_cfi->bDynamic)
{
/*
We refuse to store and object in the main cache which
is present in a static cache like a CD-ROM.
*/
return NULL;
}
}
if (!DCACHE_MakeRoomFor(expected_length))
{
return NULL;
}
buf[0] = 0;
DCACHE_GetNewCacheObjectFilename(buf);
if (!buf[0])
{
return NULL;
}
cfi = GTR_CALLOC(sizeof(*cfi), 1);
if (cfi)
{
/*
expected_length may be wrong now. We pass it into this function to allow
the cache to purge an old object if necessary to make room. lFilesize will
be set correctly after the object is retrieved.
*/
cfi->lFilesize = expected_length;
cfi->atomMIMEType = atomMIMEType;
cfi->pszPath = GTR_strdup(buf);
cfi->bDynamic = TRUE;
}
return cfi;
}
/*
Once a new object has been retrieved via HTTP, this function is called
to finish putting the object into the cache.
*/
int DCACHE_FinishNewCacheEntry(struct CacheFileInformation *cfi, char *url)
{
FILE *fp;
int len;
fp = fopen(cfi->pszPath, "rb");
if (fp)
{
/* get the length of the data */
if (0 == fseek(fp, 0, SEEK_END))
{
len = ftell(fp);
fclose(fp);
if (len > 0)
{
XX_DMsg(DBG_DCACHE, ("DCACHE_FinishNewCacheEntry: Adding %s(%s), %d bytes\n", url, cfi->pszPath, len));
cfi->lFilesize = len;
gNumBytesInMainCache += len;
cfi->bVerifiedThisSession = TRUE;
DCACHE_DeleteCachedURL(url);
return Hash_Add(pFileHash, url, NULL, cfi);
}
}
}
DCACHE_DestroyCacheFileInformation(cfi);
return 0;
}
#ifdef UNIX
BOOL
DCACHE_VerifyCacheDir(char *dirN)
{
struct _stat sbuf;
int status;
char message[MAX_URL_STRING + 1];
char dirName[_MAX_PATH + 2];
if (!dirN|| !(*dirN))
{
ERR_ReportError (NULL,
SID_ERR_SIMPLY_SHOW_ARGUMENTS_S_S,
GTR_GetString(SID_ERR_DCACHE_MAIN_CACHE_NO_DIR), "");
return FALSE;
}
strncpy(dirName, dirN, sizeof(dirName) - 2);
if (dirName[strlen(dirName) - 1] != '/')
strcat(dirName, "/");
if (_stat (dirName, &sbuf) < 0)
{
/** create dir **/
#ifdef HP_UX
status = _mkdir(dirName, (S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP));
#else
status = _mkdir(dirName, (S_IFDIR | S_IREAD | S_IWRITE | S_IEXEC | S_IRGRP | S_IWGRP | S_IXGRP));
#endif
if (status < 0)
{
ERR_ReportError (NULL,
SID_ERR_SIMPLY_SHOW_ARGUMENTS_S_S,
GTR_GetString(SID_ERR_DCACHE_MAIN_CACHE_ERR_NEW_DIR), dirName);
return FALSE;
}
sprintf(message, GTR_GetString(SID_DCACHE_MAIN_CACHE_CREATED_NEW_DIR_S), dirName);
Support_LogAdd(message, FALSE, FALSE);
return TRUE;
}
else
{
#ifdef HPUX
status = ( (sbuf.st_mode & S_IRUSR) &&
(sbuf.st_mode & S_IWUSR) &&
(sbuf.st_mode & S_IXUSR) &&
(sbuf.st_mode & S_IFDIR))
? 1 : 0;
if (!_stat)
status = ( (sbuf.st_mode & S_IFDIR) &&
(sbuf.st_mode & S_IRGRP) &&
(sbuf.st_mode & S_IWGRP) &&
(sbuf.st_mode & S_IXGRP))
? 1 : 0;
if (!_stat)
status = ( (sbuf.st_mode & S_IFDIR) &&
(sbuf.st_mode & S_IROTH) &&
(sbuf.st_mode & S_IWOTH) &&
(sbuf.st_mode & S_IXOTH))
? 1 : 0;
#else
status = ( (sbuf.st_mode & S_IFDIR) &&
(sbuf.st_mode & S_IREAD) &&
(sbuf.st_mode & S_IWRITE) &&
(sbuf.st_mode & S_IEXEC))
? 1 : 0;
if (!_stat)
status = ( (sbuf.st_mode & S_IFDIR) &&
(sbuf.st_mode & S_IRGRP) &&
(sbuf.st_mode & S_IWGRP) &&
(sbuf.st_mode & S_IXGRP))
? 1 : 0;
if (!_stat)
status = ( (sbuf.st_mode & S_IFDIR) &&
(sbuf.st_mode & S_IROTH) &&
(sbuf.st_mode & S_IWOTH) &&
(sbuf.st_mode & S_IXOTH))
? 1 : 0;
#endif
if (!status)
{
ERR_ReportError (NULL,
SID_ERR_SIMPLY_SHOW_ARGUMENTS_S_S,
GTR_GetString(SID_ERR_DCACHE_MAIN_CACHE_ERR_BAD_DIR), dirName);
return FALSE;
}
}
return TRUE;
}
#endif
/*
ResolveFilename
Resolves the file name of the given file, using the global
and index-specific alias lists.
THE CALLER MUST FREE THE RETURNED MEMORY BLOCK.
*/
static char *ResolveFilename(char *pFilename)
{
struct AliasMap *pAlias;
char *pResolved;
int pass = 0;
pAlias = pGlobalAliasList;
DoAgain:
while (pAlias)
{
if (strncmp(pFilename, pAlias->alias, strlen(pAlias->alias)) == 0)
{ /* Found a match - replace now */
pResolved = GTR_MALLOC(strlen(pFilename) + strlen(pAlias->path + 1));
if (!pResolved)
return NULL;
strcpy (pResolved, pAlias->path);
strcat (pResolved, &pFilename[strlen(pAlias->alias)]);
#ifdef WIN32
{
int i;
/* Replace forward slashes with back slashes */
for (i = 0; i < strlen(pResolved); i++)
{
if (pResolved[i] == '/')
pResolved[i] = '\\';
}
}
#endif
return pResolved;
}
pAlias = pAlias->next;
}
pass++;
if (pass == 1)
{
pAlias = pIndexAliasList;
goto DoAgain;
}
pResolved = GTR_strdup(pFilename);
return pResolved;
}
/*
VerifyFileSize
Verify that the size of the specified file is what we think it is.
Returns TRUE if file size is corrent, FALSE if not.
*/
static BOOL
VerifyFileSize
(char* pszFilePath,
int iFileSize)
{
#ifdef MAC
/* ask the system for the length rather than checking the file directly */
CInfoPBRec pb;
HFileInfo* fpb = (HFileInfo*) &pb;
Str255 fileName;
OSErr error;
strncpy (fileName, pszFilePath, 255);
c2pstr (fileName);
fpb->ioCompletion = NULL;
fpb->ioNamePtr = fileName;
fpb->ioVRefNum = 0;
fpb->ioFDirIndex = 0;
fpb->ioDirID = 0;
error = PBGetCatInfo (&pb, FALSE);
if (error) return FALSE;
return (iFileSize == fpb->ioFlLgLen);
#else
BOOL bOK = TRUE; /* assume success */
FILE* fp;
int len;
fp = fopen (pszFilePath, "rb");
if (!fp) return FALSE;
/* get the length of the data */
if (0 == fseek (fp, 0, SEEK_END))
{
len = ftell (fp);
if (len != iFileSize)
{
bOK = FALSE;
}
}
fclose (fp);
return bOK;
#endif
} /* VerifyFileSize */
/*
ProcessIndexFile
Processes the specified index file. This function assumes
that pGlobalAliasList and pIndexAliasList have already been populated
with the correct CD-Rom entries.
Returns 1 if file was processed, 0 if not.
*/
int ProcessIndexFile(char *pFilename, BOOL bDynamic)
{
FILE *fp;
struct CacheFileInformation *pFileInfo; /* entry into */
struct CacheRuleList *pRuleInfo; /* rule info */
char *pLine; /* Buffer to hold the line content */
char *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8; /* temporary vars for parsing */
char *p9, *p10;
char *pResolvedFilename; /* resolved filename */
char szTab[4]; /* used in looking for tab/space delimiters */
int ret = 0; /* return value */
pResolvedFilename = ResolveFilename(pFilename);
if (!pResolvedFilename || !pFileHash)
return 0;
szTab[0] = '\t'; /* tab character */
szTab[1] = ' '; /* ######## A SPACE IS A LEGAL FIELD SEPARATOR IN A CACHE INDEX FILE */
szTab[2] = 0; /* string terminator */
#if defined(MAC) || defined(WIN32)
szTab[1] = 0; /* ######## EXCEPT ON THE MAC and WIN32 (they allow spaces IN filenames) */
#endif
pLine = GTR_MALLOC(DCACHE_MAXIMUM_LINE_LENGTH);
if (!pLine)
return 0;
fp = fopen(pResolvedFilename, "rt");
if (!fp)
{
GTR_FREE(pLine);
GTR_FREE(pResolvedFilename);
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';
p1 = strtok(pLine, szTab); /* F or R */
p2 = strtok(NULL, szTab); /* URL */
p3 = strtok(NULL, szTab); /* FileSize */
p4 = strtok(NULL, szTab); /* LastModified */
p5 = strtok(NULL, szTab); /* Expires */
p6 = strtok(NULL, szTab); /* LastUsed */
p7 = strtok(NULL, szTab); /* MIMEType */
p8 = strtok(NULL, szTab); /* Path */
p9 = strtok(NULL, szTab); /* HitCount */
p10 = strtok(NULL, szTab); /* Flags */
/*
Note that p9 and p10 point to fields which did not exist in the
first revision of this file format, so they are not required to
exist.
*/
if (!p1) continue;
if (strcmp(p1, "F") == 0)
{ /* This line contains information about a file substitution */
if (!p2 || !p3 || !p4 || !p5 || !p6 || !p7 || !p8)
continue; /* incomplete line */
if (strchr(p7, '/') == NULL)
{
XX_Assert((0), ("Bad MIME type %s ", p7));
}
pFileInfo = GTR_CALLOC(sizeof(struct CacheFileInformation), 1);
if (pFileInfo)
{
BOOL bOK;
pFileInfo->atomMIMEType = HTAtom_for(p7);
pFileInfo->bDynamic = bDynamic;
if (p9)
{
pFileInfo->nHits = atol(p9);
}
if (p10)
{
pFileInfo->lFlags = atol(p10);
}
pFileInfo->tLastModified = atol(p4);
pFileInfo->tExpires = atol(p5);
pFileInfo->tLastUsed = atol(p6);
if (strchr(p8, PATH_SEP) == 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);
if (pFileInfo->pszPath)
{
strcpy(pFileInfo->pszPath, pResolvedFilename);
*(strrchr(pFileInfo->pszPath, PATH_SEP) + 1) = '\0';
strcat(pFileInfo->pszPath, p8);
}
}
else
{
pFileInfo->pszPath = ResolveFilename(p8);
}
pFileInfo->lFilesize = atol(p3);
bOK = TRUE;
if (bDynamic)
{
gNumBytesInMainCache += pFileInfo->lFilesize;
bOK = VerifyFileSize (pFileInfo->pszPath, pFileInfo->lFilesize);
}
if (bOK)
{
Hash_Add(pFileHash, p2, NULL, pFileInfo);
}
/* TODO is there a memory leak here (pFileInfo) ?? */
}
else
{
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
}
}
else if (strcmp(p1, "R") == 0)
{ /* This line contains information about a rule */
if (!p2 || !p3)
continue; /* incomplete line */
pRuleInfo = GTR_CALLOC(sizeof(struct CacheRuleList), 1);
if (pRuleInfo)
{
pRuleInfo->pszOriginal = GTR_strdup(p2);
pRuleInfo->pszReplacement = ResolveFilename(p3);
pRuleInfo->next = pRuleList;
pRuleList = pRuleInfo;
}
else
{
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
}
}
else
{
/* Illegal entry, since it doesn't start with F or R */
}
}
GTR_FREE(pLine);
GTR_FREE(pResolvedFilename);
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;
int i;
pszReturn = GTR_MALLOC(strlen(pszLocal) + 10);
if (!pszReturn) return NULL;
strcpy(pszReturn, "file:///");
strcat(pszReturn, pszLocal);
#ifdef WIN32
{
char *p;
/* Replace the second : with | (first one is file:) */
if (p = strrchr(pszReturn, ':'))
*p = '|';
}
#endif
#ifndef UNIX
/* Replace backslashes with forward slashes */
/* starting after the "file:///" part of the string */
for (i = 7; i < strlen(pszReturn); i++)
{
if (pszReturn[i] == PATH_SEP)
pszReturn[i] = '/';
}
#endif
return pszReturn;
}
/*
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(char *pszURL, HTFormat *pMime, long *pFileLength, char **ppPath)
{
char *pszResolved, *pszReturn;
struct CacheFileInformation *pFileInfo;
#ifdef FEATURE_LOCALONLY_MESSAGE_URL
char notcpBuff[_MAX_PATH + 1];
#endif
pFileInfo = DCACHE_CheckForCachedURL(pszURL, pMime, pFileLength, ppPath);
if (pFileInfo)
{
pszResolved = GTR_strdup(pFileInfo->pszPath);
pszReturn = MakeURLFromLocalFile(pszResolved);
GTR_FREE(pszResolved);
DCACHE_RegisterCacheHit(pFileInfo);
return pszReturn;
}
pszReturn = DCACHE_CheckForRuleMatch(pszURL, pMime, pFileLength, ppPath);
if (pszReturn)
{
return pszReturn;
}
/* No substitute found for the specified URL */
#ifdef FEATURE_LOCALONLY_MESSAGE_URL
if (!bNetwork)
{
if ((0 == strncmp(pszURL, "http:", 5)) ||
(0 == strncmp(pszURL, "ftp:", 4)) ||
(0 == strncmp(pszURL, "gopher:", 7)))
{
#ifdef MAC
long dirid;
Str255 filename;
getwddirid (MacGlobals.gCurrentDirectory, &dirid);
strcpy (notcpBuff, "file:///");
GetIndString (filename, OEM_FILES_STR_LIST, OEM_NOTCP_STR);
if (filename[0] != 0)
{
p2cstr (filename);
strcpy (notcpBuff, filename);
PathNameFromDirID (dirid, MacGlobals.gCurrentDirectory, notcpBuff);
}
else /* no filename provided or found */
{
strcpy (notcpBuff, pszURL);
}
#else
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, PATH_SEP);
strcat(notcpBuff, "notcp.htm");
#endif
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;
else
GTR_FREE(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;
pszReturn = GTR_strdup(pszURL);
return pszReturn;
}
/*****************************************************************************
HTLoadDCache_Async_SetFileInfo
NOTES:
Contains Hack -dpg to pass a long * to GetResolvedURL instead of int
*****************************************************************************/
int
HTLoadDCache_Async_SetFileInfo
(struct Mwin* tw,
struct Data_LoadFile* pData,
char* pszURL,
char** pszLocalname)
{
HTFormat format;
long tmp = pData->request->content_length;
char *p;
p = GetResolvedURL (pszURL, &format, &tmp, pszLocalname);
if (p)
{
GTR_FREE(p);
}
pData->request->content_length = (int) tmp;
if (!pszLocalname)
{
*pData->pStatus = -403;
ERR_ReportError (tw, SID_ERR_FILE_NOT_FOUND_S, pData->request->destination->szActualURL, NULL);
return STATE_DONE;
}
if (format != 0)
{
pData->request->content_type = format;
return STATE_INIT;
}
/* unknown MIMEtype -- need to go figure it out */
return HTLoadFile_Async_SetFileInfo (tw, pData, pszURL, pszLocalname);
} /* HTLoadDCache_Async_SetFileInfo */
/************************************************************
DCACHE PROTOCOL CODE
Uses many routines contained in htfile.c
************************************************************/
static int HTLoadDCache_Async(struct Mwin *tw, int nState, void **ppInfo)
{
int result;
struct Data_LoadFile *pData;
pData = *ppInfo;
switch (nState)
{
case STATE_INIT:
pData->request->iFlags |= HTREQ_USINGCACHE;
result = HTLoadFile_Async_Init (tw, ppInfo, 1);
break;
case STATE_FILE_STREAMINIT:
result = HTLoadFile_Async_File_StreamInit (tw, pData);
break;
case STATE_FILE_COPYING:
result = HTLoadFile_Async_File_Copy (tw, pData);
break;
case STATE_ABORT:
result = HTLoadFile_Async_Abort (tw, pData);
break;
default:
XX_Assert((0), ("Function called with illegal state: %d", nState));
result = STATE_DONE;
break;
}
return result;
}
#ifdef UNIX
/**************************************************************************/
/**
Add a path to the local-cache list
**/
/**************************************************************************/
BOOL AddToLocalCacheList(char *alias, char *value)
{
static int cacheNum = 0;
static int mainCacheSetUp = 0;
char szAlias[257];
struct AliasMap *pAlias;
char *pResolvedFilename; /* resolved filename */
char szMainCacheIndexFile[_MAX_PATH + 1];
char *p;
if (gPrefs.bEnableDiskCache != TRUE)
return FALSE;
if (!cacheNum)
{
AddHomeDirToCacheList(gPrefs.szRootDirectory);
cacheNum++;
}
if (!mainCacheSetUp) /** This may refer to EXEDIR **/
{
if (gPrefs.szMainCacheDir[0] == '\0')
{
strcpy(gPrefs.szMainCacheDir, "$(EXEDIR)");
}
p = ResolveFilename(gPrefs.szMainCacheDir);
if (p)
{
strcpy(gPrefs.szMainCacheDir, p);
GTR_FREE(p);
}
if (!DCACHE_VerifyCacheDir(gPrefs.szMainCacheDir))
return FALSE;
DCACHE_GetMainCacheIndexFileName(szMainCacheIndexFile);
if (szMainCacheIndexFile[0])
{
ProcessIndexFile(szMainCacheIndexFile, TRUE);
mainCacheSetUp = 1;
}
}
#ifdef FEATURE_LOCAL_CACHE
if (!value)
return FALSE;
if (!(*value))
return FALSE;
/* Found a CD Rom. Add an entry to the CD Rom list. */
pAlias = GTR_CALLOC(sizeof(struct AliasMap), 1);
if (pAlias)
{
pResolvedFilename = ResolveFilename(value);
if (!pResolvedFilename)
pAlias->path = GTR_strdup(value);
else
{
pAlias->path = GTR_strdup(pResolvedFilename);
GTR_FREE(pResolvedFilename);
}
if (!alias || !(*alias))
sprintf(szAlias, "$(CDROM%d)", cacheNum);
else
strcpy(szAlias, alias);
pAlias->alias = GTR_strdup(szAlias);
/** Add to the list **/
pAlias->next = pGlobalAliasList; /* Even if pGlobalAliasList == NULL */
pGlobalAliasList = pAlias;
}
else
{
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return FALSE;
}
cacheNum++;
#endif /* FEATURE_LOCAL_CACHE */
return TRUE;
}
#endif /* UNIX */
/**************************************************************************/
/**
Add home directory of the executable to the local-cache list
**/
/**************************************************************************/
BOOL AddHomeDirToCacheList(char *szRootDirectory)
{
struct AliasMap *pAlias;
char szAlias[20];
/* Add a non-Drive to Drive list for $(EXEDIR) support */
pAlias = GTR_CALLOC(sizeof(struct AliasMap), 1);
if (pAlias)
{
pAlias->path = GTR_strdup(szRootDirectory);
strcpy(szAlias, "$(EXEDIR)");
pAlias->alias = GTR_strdup(szAlias);
/** Add to the list **/
pAlias->next = pGlobalAliasList;
pGlobalAliasList = pAlias;
}
else
{
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return FALSE;
}
return TRUE;
}
/**************************************************************************/
/************************************************************
Windows specific code
************************************************************/
#ifdef WIN32
/*
BuildAliasList
Scans all DOS drives and builds a global alias list.
Returns the number of CD-Rom drives found.
*/
static int BuildAliasList(void)
{
char szRoot[10], szAlias[20];
UINT dtype;
int dcount = 0;
struct AliasMap *pAlias;
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++;
pAlias = GTR_CALLOC(sizeof(struct AliasMap), 1);
if (pAlias)
{
pAlias->path = GTR_strdup(szRoot);
sprintf(szAlias, "$(CDROM%d)", dcount);
pAlias->alias = GTR_strdup(szAlias);
pAlias->next = pGlobalAliasList; /* Even if pGlobalAliasList == NULL */
pGlobalAliasList = pAlias;
}
else
{
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
}
}
if (szRoot[0] < 'Z')
szRoot[0]++;
else
break;
}
/* Add a non-Drive to Drive list for $(EXEDIR) support */
pAlias = GTR_CALLOC(sizeof(struct AliasMap), 1);
if (pAlias)
{
pAlias->path = GTR_strdup(wg.szRootDirectory);
strcpy(szAlias, "$(EXEDIR)");
pAlias->alias = GTR_strdup(szAlias);
pAlias->next = pGlobalAliasList; /* Even if pGlobalAliasList == NULL */
pGlobalAliasList = pAlias;
/* Hope we dont use dcount to assume an actual CDROM anywhere */
dcount++;
}
else
{
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
}
return dcount;
}
/*
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_CALLOC(1, nAllocSize);
if (!pBuffer)
return NULL;
for (;;)
{
nRead = (int) GetPrivateProfileSection(pSection, pBuffer, nAllocSize, AppIniFile);
if (nRead == nAllocSize - 2)
{
/* Buffer is too small. Increase the buffer size and try again. */
GTR_FREE(pBuffer);
nAllocSize *= 2;
pBuffer = GTR_CALLOC(1, 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;
}
/*
ReadCacheIndexFiles
Read all entries from cache index files and put
the entries in the substitution hash table and
rule list.3
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 *pCustomAlias; /* Pointer to custom drive section */
char *pCurrentDrive; /* Current drive within the custom drive section */
int nCurrentDriveLength; /* Length of current drive */
struct AliasMap *pAlias; /* Entry to be added to the custom drive list */
char szMainCacheIndexFile[_MAX_PATH + 1];
DCACHE_GetMainCacheIndexFileName(szMainCacheIndexFile);
if (szMainCacheIndexFile[0] && ProcessIndexFile(szMainCacheIndexFile, TRUE))
{
nCount++;
}
pIndexSection = ReadProfileSection("DiskCaches");
if (!pIndexSection)
{ /* No cache is found. */
return 0;
}
/* Parse through the section */
pCurrentIndex = pIndexSection;
nCurrentIndexLength = strlen(pCurrentIndex);
while (pCurrentIndex[0])
{
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.
*/
FreeAliasMap(pIndexAliasList);
pIndexAliasList = NULL;
pCustomAlias = ReadProfileSection(p1);
if (pCustomAlias)
{ /* Build a list of aliases for CD Roms */
pCurrentDrive = pCustomAlias;
nCurrentDriveLength = strlen(pCurrentDrive);
while (pCurrentDrive[0])
{
p3 = strtok(pCurrentDrive, "=");
p4 = strtok(NULL, "=");
pAlias = GTR_CALLOC(sizeof(struct AliasMap), 1);
if (pAlias)
{
pAlias->alias = GTR_MALLOC(strlen(p3) + 4);
if (pAlias->alias)
{
sprintf(pAlias->alias, "$(%s)", p3);
}
pAlias->path = GTR_strdup(p4);
pAlias->next = pIndexAliasList;
pIndexAliasList = pAlias;
}
else
{
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
}
pCurrentDrive = pCurrentDrive + nCurrentDriveLength + 1;
}
GTR_FREE(pCustomAlias);
}
/* Now read in the index file and process each line within the file */
if (ProcessIndexFile(p2, FALSE))
nCount++;
pCurrentIndex = pCurrentIndex + nCurrentIndexLength + 1; /* Skip past the null terminator */
}
GTR_FREE(pIndexSection);
return nCount;
}
#endif
GLOBALDEF PUBLIC HTProtocol HTDCache = {"dcache", NULL, HTLoadDCache_Async};
/**************************************************************************
Macintosh-Specific Code
**************************************************************************/
#ifdef MAC
#ifdef FEATURE_LOCAL_CACHE
#include "ResEqu.h"
enum
{
kSymbolStrLen = 16,
kReplacementStrLen = 256,
kFilePathLen = 256
};
#if GENERATINGPOWERPC
#pragma options align=mac68k
#endif
typedef struct
{
char szSymbol[kSymbolStrLen];
char szReplacement[kReplacementStrLen];
} SubstitutionStruct;
typedef struct
{
short iNumSubstitutions;
SubstitutionStruct s;
} SubstitutionRes;
typedef struct
{
char szIndexFilePath[kFilePathLen];
short substitutionID;
} IndexInfoRes;
#if GENERATINGPOWERPC
#pragma options align=reset
#endif
/**************************************************************************
ProcessSubstitutions
**************************************************************************/
static int
ProcessSubstitutions
(short resID,
struct AliasMap** pAliasMap)
{
Handle hResource;
int iNumProcessed = 0;
hResource = Get1Resource (kSubstitutionType, resID);
if (hResource)
{
SubstitutionRes* pS;
HLock (hResource);
pS = (SubstitutionRes*) *hResource;
if (pS)
{
SubstitutionStruct* pSubstitution = &pS->s;
char szAlias[kSymbolStrLen + 4];
int index;
short iNumSubstitutions = pS->iNumSubstitutions;
for (index = 0; index < iNumSubstitutions; index++)
{
struct AliasMap *pAlias = GTR_CALLOC (sizeof(struct AliasMap), 1);
if (!pAlias) break;
pAlias->path = GTR_strdup (pSubstitution->szReplacement);
sprintf(szAlias, "$(%s)", pSubstitution->szSymbol);
pAlias->alias = GTR_strdup (szAlias);
pAlias->next = *pAliasMap;
*pAliasMap = pAlias;
iNumProcessed++;
pSubstitution++;
}
}
HUnlock (hResource);
}
return iNumProcessed;
} /* ProcessSubstitutions */
/**************************************************************************
BuildAliasList
DESCRIPTION:
This function simply builds a table of "alias" substitution strings
for the local-disk-caching scheme.
RETURNS: the number of aliases recorded
**************************************************************************/
int
BuildAliasList
(short resRefNum)
{
short iNumProcessed = 0;
short curResFile = CurResFile ();
UseResFile (resRefNum);
iNumProcessed = ProcessSubstitutions (1000, &pGlobalAliasList);
UseResFile (curResFile);
return iNumProcessed;
} /* BuildAliasList */
/**************************************************************************
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
**************************************************************************/
int
ReadCacheIndexFiles
(short resRefNum)
{
short iCacheDescriptors;
short iNumProcessed = 0;
short curResFile = CurResFile ();
UseResFile (resRefNum);
iCacheDescriptors = Count1Resources (kCacheDescType);
if (iCacheDescriptors > 0)
{
int index;
for (index = 1; index <= iCacheDescriptors; index++)
{
IndexInfoRes* pInfo;
Handle hResource;
hResource = Get1IndResource (kCacheDescType, index);
if (!hResource) break;
HLock (hResource);
pInfo = (IndexInfoRes*) *hResource;
(void) ProcessSubstitutions (pInfo->substitutionID, &pIndexAliasList);
if (ProcessIndexFile (pInfo->szIndexFilePath, FALSE))
{
iNumProcessed++;
}
HUnlock (hResource);
}
}
UseResFile (curResFile);
return iNumProcessed;
} /* ReadCacheIndexFiles */
#endif
#endif
struct _HTStream
{
CONST HTStreamClass *isa;
struct CacheFileInformation *cfi;
char *url;
FILE *fp;
HTStream* sink;
};
/*****************************************************************************
HTCacheWriter_Async
This function simply calls the async function of its sink.
*****************************************************************************/
static int
HTCacheWriter_Async
(struct Mwin* tw,
int nState,
void** ppInfo)
{
struct Params_InitStream *pParams = (struct Params_InitStream *) *ppInfo;
int iNextState = STATE_DONE; /* default case */
switch (nState)
{
case STATE_INIT:
if (pParams->me->sink->isa->init_Async)
{
struct Params_InitStream *pis;
pis = GTR_CALLOC(sizeof(*pis), 1);
if (pis)
{
pis->me = pParams->me->sink;
pis->request = pParams->request;
pis->pResult = pParams->pResult;
Async_DoCall(pParams->me->sink->isa->init_Async, pis);
iNextState = STATE_OTHER;
}
else
{
iNextState = STATE_ABORT;
}
}
else
{
*pParams->pResult = 1; /* ok */
}
break;
case STATE_OTHER:
case STATE_ABORT:
break;
default:
XX_Assert((0), ("HTCacheWriter_Async called with invalid state: %d\n", nState));
break;
}
return iNextState;
}
/*
If writing to the cache file fails, we close the cache file and
set fp to NULL, indicating that an error occured. This is our
signal that the resulting cached object should be considered
invalid, and not actually added to the cache index when we're
done.
Even if writing to the cache file has failed, we allow the stream
to follow to completion, passing its data on to the sink stream.
The result will be that the object will be processed by Mosaic
correctly, but it won't be in the disk cache at all.
*/
static BOOL HTCacheWriter_put_character(HTStream * me, char c)
{
if (me->fp)
{
if (EOF == fputc(c, me->fp))
{
fclose(me->fp);
remove(me->cfi->pszPath);
me->fp = NULL;
}
}
return me->sink->isa->put_character(me->sink, c);
}
static BOOL HTCacheWriter_put_block(HTStream * me, const char *s, int len)
{
if (me->fp)
{
if (len != fwrite(s, 1, len, me->fp))
{
fclose(me->fp);
remove(me->cfi->pszPath);
me->fp = NULL;
}
}
return me->sink->isa->put_block(me->sink, s, len);
}
static BOOL HTCacheWriter_put_string(HTStream * me, const char *s)
{
return HTCacheWriter_put_block(me, s, strlen(s));
}
static void HTCacheWriter_free(HTStream * me)
{
if (me->fp)
{
fclose(me->fp);
me->fp = NULL;
DCACHE_FinishNewCacheEntry(me->cfi, me->url);
}
else
{
DCACHE_DestroyCacheFileInformation(me->cfi);
}
GTR_FREE(me->url);
me->sink->isa->free(me->sink); /* Close rest of pipe */
GTR_FREE(me);
}
static void HTCacheWriter_abort(HTStream * me, HTError e)
{
if (me->fp)
{
fclose(me->fp);
remove(me->cfi->pszPath);
me->fp = NULL;
}
GTR_FREE(me->url);
DCACHE_DestroyCacheFileInformation(me->cfi);
me->sink->isa->abort(me->sink, e); /* Abort rest of pipe */
GTR_FREE(me);
}
const HTStreamClass HTCacheWriter =
{
"CacheWriter",
-1, // No string...
-1, // No string...
HTCacheWriter_Async,
HTCacheWriter_free,
HTCacheWriter_abort,
HTCacheWriter_put_character, HTCacheWriter_put_string, HTCacheWriter_put_block,
};
/*****************************************************************************
The CacheWriter stream is usually the first stream in a chain,
because it is designed to simply take the raw bytes coming in
from an HTTP connection, write them into the main disk cache, and
pass those same bytes on to the next stream (its sink) unchanged.
*****************************************************************************/
HTStream *HTCacheWriter_create(HTStream * sink, struct CacheFileInformation *cfi, char *url)
{
HTStream *me = (HTStream *) GTR_CALLOC(sizeof(*me), 1);
if (me)
{
me->isa = &HTCacheWriter;
me->cfi = cfi;
me->url = GTR_strdup(url);
if (!me->url)
{
GTR_FREE(me);
return NULL;
}
me->fp = fopen(me->cfi->pszPath, "wb");
if (!me->fp)
{
GTR_FREE(me->url);
GTR_FREE(me);
return NULL;
}
#ifdef MAC /* need to set the creator and type */
SetGuitarFileType (me->cfi->pszPath, 'TEXT');
#endif
me->sink = sink;
}
return me;
}
unsigned long DCACHE_CurrentCacheSize()
{
return gNumBytesInMainCache;
}