gecko-dev/network/cache/mkcache.c
1998-05-28 03:15:39 +00:00

3447 lines
84 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/* implements disk caching: See mkmemcac.c for memory cache
*
* Designed and originally implemented by Lou Montulli '94
* Modifications/additions by Gagan Saksena '97
*/
#ifdef MOZILLA_CLIENT
/* dbm code */
#include "mkcache.h"
#include "extcache.h"
#include "mkgeturl.h"
#include "netutils.h"
#include "netcache.h"
#include "glhist.h"
#include "xp_hash.h"
#include "xp_mcom.h"
#include "client.h"
#include "mkstream.h"
#include "secnav.h"
#include "mcom_db.h"
#include "prclist.h"
#include "prmem.h"
#include "plstr.h"
#include "mkextcac.h"
#include "fileurl.h"
#include "mkmemcac.h"
#include "merrors.h"
#ifdef PROFILE
#pragma profile on
#endif
#ifdef XP_OS2_NOT_YET
/*serious hack to put cache cleanup on another thread...*/
extern int XP_LazyFileRemove(const char * name, XP_FileType type);
#else
#define XP_LazyFileRemove XP_FileRemove
#endif
/* For Mac */
#include <errno.h>
/* for XP_GetString() */
#include "xpgetstr.h"
extern int XP_CACHE_CLEANUP;
extern int MK_CACHE_CONTENT_LENGTH;
extern int MK_CACHE_REAL_CONTENT_LENGTH;
extern int MK_CACHE_CONTENT_TYPE;
extern int MK_CACHE_LOCAL_FILENAME;
extern int MK_CACHE_LAST_MODIFIED;
extern int MK_CACHE_EXPIRES;
extern int MK_CACHE_LAST_ACCESSED;
extern int MK_CACHE_CHARSET;
extern int MK_CACHE_SECURE;
extern int MK_CACHE_USES_RELATIVE_PATH;
extern int MK_CACHE_FROM_NETSITE_SERVER;
#define INTERNAL_NAME_PREFIX "INT_"
#define DISK_CACHE_SIZE_KEY_NAME "INT_DiskCacheSize"
#define DISK_CACHE_NUMBER_KEY_NAME "INT_DiskCacheNumber"
#define DISK_CACHE_NAME "Netscape Internal Disk Cache"
#define CACHE_SYNC_RATE 30
typedef enum {
NEW_CACHE_STORE,
REFRESH_CACHE_STORE
} store_type_enum;
PRIVATE DBT *net_DiskCacheSizeKey=0;
PRIVATE DBT *net_DiskCacheNumberKey=0;
PRIVATE DBT *net_DiskCacheNameKey=0;
PRIVATE uint32 net_DiskCacheSize=0;
PRIVATE uint32 net_NumberInDiskCache=0;
PRIVATE uint32 net_MaxDiskCacheSize=0;
PRIVATE uint32 net_cache_file_block_size=0;
/* Saves the cache struct to the DB */
extern void CACHE_SaveCacheInfoToDB(ExtCacheDBInfo *db_info);
#ifdef NO_MEMORY_CACHE
PRIVATE uint32 net_MaxMemoryCacheSize=0;
#endif /* NO_MEMORY_CACHE */
/* trace variable for cache testing */
MODULE_PRIVATE PRBool NET_CacheTraceOn = PR_FALSE;
PRIVATE PRBool net_dont_disk_cache_ssl = PR_FALSE;
PRIVATE DB * cache_database = 0;
/* PRIVATE XXX Mac CodeWarrior bug */ PRCList active_cache_data_objects
= PR_INIT_STATIC_CLIST(&active_cache_data_objects);
typedef struct _CacheDataObject {
PRCList links;
XP_File fp;
NET_StreamClass *next_stream;
URL_Struct *URL_s;
net_CacheObject *cache_object;
} CacheDataObject;
PRIVATE void net_RemoveAllDiskCacheObjects(void);
/* pass in TRUE to disable disk caching
* of SSL documents.
* pass in FALSE to enable disk cacheing
* of SSL documents
*/
PUBLIC void
NET_DontDiskCacheSSL(XP_Bool set)
{
net_dont_disk_cache_ssl = set;
}
PUBLIC PRBool
NET_IsCacheTraceOn(void)
{
return NET_CacheTraceOn;
}
/* return the size of the file on
* disk based on the block size
*/
PRIVATE
uint32
net_calc_real_file_size(uint32 size)
{
if(net_cache_file_block_size == 0)
return(size);
else /* round sizes up to the next block size */
return(PR_ROUNDUP(size, net_cache_file_block_size));
}
PRIVATE void
net_GetDiskCacheSize(void)
{
DBT data;
if(!cache_database)
return;
if(!net_DiskCacheSizeKey)
{
net_DiskCacheSizeKey = PR_NEW(DBT);
if(!net_DiskCacheSizeKey)
return;
net_DiskCacheSizeKey->data = 0;
BlockAllocCopy(net_DiskCacheSizeKey->data,
DISK_CACHE_SIZE_KEY_NAME,
PL_strlen(DISK_CACHE_SIZE_KEY_NAME));
net_DiskCacheSizeKey->size = PL_strlen(DISK_CACHE_SIZE_KEY_NAME);
}
if(0 == (*cache_database->get)(cache_database,
net_DiskCacheSizeKey,
&data,
0))
{
if(data.size == sizeof(uint32))
{
COPY_INT32(&net_DiskCacheSize, data.data);
}
else
{
net_DiskCacheSize = 0;
}
}
else
{
net_DiskCacheSize = 0;
}
if(!net_DiskCacheNumberKey)
{
net_DiskCacheNumberKey = PR_NEW(DBT);
if(!net_DiskCacheNumberKey)
return;
net_DiskCacheNumberKey->data = 0;
BlockAllocCopy(net_DiskCacheNumberKey->data,
DISK_CACHE_NUMBER_KEY_NAME,
PL_strlen(DISK_CACHE_NUMBER_KEY_NAME));
net_DiskCacheNumberKey->size = PL_strlen(DISK_CACHE_NUMBER_KEY_NAME);
}
if(0 == (*cache_database->get)(cache_database,
net_DiskCacheNumberKey,
&data,
0))
{
if(data.size == sizeof(uint32))
{
COPY_INT32(&net_NumberInDiskCache, data.data);
}
else
{
net_NumberInDiskCache = 0;
}
}
else
{
net_NumberInDiskCache = 0;
}
}
PRIVATE int
net_OpenCacheFatDB(void)
{
char* filename;
static XP_Bool have_tried_open=FALSE;
if(!cache_database)
{
HASHINFO hash_info = {
4 * 1024,
0,
0,
#ifdef WIN16
60 * 1024,
#else
96 * 1024,
#endif
0,
0};
filename = WH_FileName("", xpCacheFAT);
if (!filename) return 0;
/* @@@ add .db to the end of the files
*/
cache_database = dbopen(filename,
O_RDWR | O_CREAT,
0600,
DB_HASH,
&hash_info);
PR_Free(filename);
if(!have_tried_open && !cache_database)
{
XP_StatStruct stat_entry;
have_tried_open = TRUE; /* only do this once */
TRACEMSG(("Could not open cache database -- errno: %d", errno));
/* if the file is zero length remove it
*/
if(XP_Stat("", &stat_entry, xpCacheFAT) != -1)
{
if(stat_entry.st_size <= 0)
{
XP_FileRemove("", xpCacheFAT);
}
else
{
XP_File fp;
#define BUFF_SIZE 1024
char buffer[BUFF_SIZE];
/* open the file and look for
* the old magic cookie. If it's
* there delete the file
*/
fp = XP_FileOpen("", xpCacheFAT, XP_FILE_READ);
if(fp)
{
XP_FileReadLine(buffer, BUFF_SIZE, fp);
XP_FileClose(fp);
if(PL_strstr(buffer,
"Cache-file-allocation-table-format"))
XP_FileRemove("", xpCacheFAT);
}
}
}
/* try it again */
filename = WH_FileName("", xpCacheFAT);
if (filename) {
cache_database = dbopen(filename,
O_RDWR | O_CREAT,
0600,
DB_HASH,
0);
PR_Free(filename);
}
else
cache_database = NULL;
}
if(cache_database)
{
if(-1 == (*cache_database->sync)(cache_database, 0))
{
TRACEMSG(("Error syncing cache database"));
/* close the database */
(*cache_database->close)(cache_database);
cache_database = 0;
}
net_GetDiskCacheSize();
}
#ifndef XP_PC
/* if we don't know the standard block size yet
* get it now
*/
if(!net_cache_file_block_size)
{
XP_StatStruct stat_entry;
if(XP_Stat("", &stat_entry, xpCacheFAT) != -1)
net_cache_file_block_size = stat_entry.st_blksize;
}
#endif
}
/* return non-zero if the cache_database pointer is
* non-zero
*/
return((int) cache_database);
}
#if defined(DEBUG) && defined(UNIX)
int
cache_test_me()
{
net_CacheObject test;
net_CacheObject *rv;
int32 total_size;
DBT *db_obj;
memset(&test, 0, sizeof(net_CacheObject));
StrAllocCopy(test.address, "test1");
db_obj = net_CacheStructToDBData(&test);
rv = net_DBDataToCacheStruct(db_obj);
printf("test1: %s\n", rv->address);
memset(&test, 0, sizeof(net_CacheObject));
StrAllocCopy(test.address, "test2");
StrAllocCopy(test.charset, "test2");
db_obj = net_CacheStructToDBData(&test);
rv = net_DBDataToCacheStruct(db_obj);
printf("test2: %s %s\n", rv->address, rv->charset);
memset(&test, 0, sizeof(net_CacheObject));
StrAllocCopy(test.address, "test3");
StrAllocCopy(test.charset, "test3");
test.content_length = 3 ;
test.method = 3 ;
test.is_netsite = 3 ;
db_obj = net_CacheStructToDBData(&test);
rv = net_DBDataToCacheStruct(db_obj);
printf("test3: %s %s %d %d %s\n",
rv->address, rv->charset,
rv->content_length, rv->method,
(rv->is_netsite == 3 ? "TRUE" : "FALSE"));
}
#endif
PRIVATE void
net_StoreDiskCacheSize(void)
{
DBT data;
uint32 tmp_num;
if(!cache_database)
return;
if(!net_DiskCacheSizeKey)
{
net_DiskCacheSizeKey = PR_NEW(DBT);
if(!net_DiskCacheSizeKey)
return;
net_DiskCacheSizeKey->data = 0;
BlockAllocCopy(net_DiskCacheSizeKey->data,
DISK_CACHE_SIZE_KEY_NAME,
PL_strlen((char *)DISK_CACHE_SIZE_KEY_NAME));
net_DiskCacheSizeKey->size = PL_strlen(DISK_CACHE_SIZE_KEY_NAME);
}
if(!net_DiskCacheNumberKey)
{
net_DiskCacheNumberKey = PR_NEW(DBT);
if(!net_DiskCacheNumberKey)
return;
net_DiskCacheNumberKey->data = 0;
BlockAllocCopy(net_DiskCacheNumberKey->data,
DISK_CACHE_NUMBER_KEY_NAME,
PL_strlen((char *)DISK_CACHE_NUMBER_KEY_NAME));
net_DiskCacheNumberKey->size = PL_strlen(DISK_CACHE_NUMBER_KEY_NAME);
}
if(!net_DiskCacheNameKey)
{
/* set the name of the cache so that
* if it is read as an external cache
* we will know what created it
*/
net_DiskCacheNameKey = PR_NEW(DBT);
if(!net_DiskCacheNameKey)
return;
net_DiskCacheNameKey->data = 0;
BlockAllocCopy(net_DiskCacheNameKey->data,
EXT_CACHE_NAME_STRING,
PL_strlen((char *)EXT_CACHE_NAME_STRING));
net_DiskCacheNameKey->size = PL_strlen(EXT_CACHE_NAME_STRING);
}
data.size = sizeof(uint32);
COPY_INT32(&tmp_num, &net_DiskCacheSize);
data.data = &tmp_num;
(*cache_database->put)(cache_database, net_DiskCacheSizeKey, &data, 0);
data.size = sizeof(uint32);
COPY_INT32(&tmp_num, &net_NumberInDiskCache);
data.data = &tmp_num;
(*cache_database->put)(cache_database, net_DiskCacheNumberKey, &data, 0);
data.size = PL_strlen(DISK_CACHE_NAME)+1;
data.data = DISK_CACHE_NAME;
(*cache_database->put)(cache_database, net_DiskCacheNameKey, &data, 0);
}
/* returns TRUE if the object gets stored
* FALSE if not
*/
PRIVATE XP_Bool
net_CacheStore(net_CacheObject * obj,
URL_Struct * URL_s,
XP_Bool accept_partial_files,
store_type_enum store_type)
{
DBT *data, *key;
int status;
static cache_sync_count = 0;
XP_StatStruct stat_entry;
/* larubbio */
XP_Bool SARCache = FALSE;
XP_FileType fileType;
DB *local_cache_database = NULL;
/* larubbio fix to allow 197 SAR cache adds without modifying
* the navigator cache (too much)
*/
if ( URL_s != NULL && URL_s->SARCache != NULL )
{
SARCache = TRUE;
fileType = xpSARCache;
local_cache_database = URL_s->SARCache->database;
}
else
{
fileType = xpCache;
local_cache_database = cache_database;
}
if(!local_cache_database)
{
/* delete the file since we wont remember it
*/
#ifdef XP_OS2
XP_LazyFileRemove(obj->filename, fileType);
#else
XP_FileRemove(obj->filename, fileType);
#endif
return FALSE;
}
if(URL_s
&& URL_s->server_date
&& URL_s->server_date < obj->last_modified)
{
/* the last modified date is after the current date
* as given by the server. We shoudn't cache this
* object
*/
TRACEMSG(("Found file dated into the future. "
"Zeroing the last modified date"));
obj->last_modified = 0;
URL_s->last_modified = 0;
}
if(store_type == NEW_CACHE_STORE)
{
if(URL_s && URL_s->dont_cache)
{
/* delete the file since we wont remember it
*/
#ifdef XP_OS2
XP_LazyFileRemove(obj->filename, fileType);
#else
XP_FileRemove(obj->filename, fileType);
#endif
return FALSE;
}
else if(XP_Stat(obj->filename, &stat_entry, fileType) != -1)
{
if(stat_entry.st_size == 0)
{
/* don't cache any zero length files
*/
#ifdef XP_OS2
XP_LazyFileRemove(obj->filename, fileType);
#else
XP_FileRemove(obj->filename, fileType);
#endif
return FALSE;
}
if(obj->content_length <= 0)
{
/* fill this in for consistancy */
obj->real_content_length = stat_entry.st_size;
obj->content_length = stat_entry.st_size;
}
else if(accept_partial_files
&& obj->content_length > (uint32) stat_entry.st_size)
{
obj->real_content_length = obj->content_length;
obj->content_length = (uint32) stat_entry.st_size;
obj->incomplete_file = TRUE;
}
else
{
/* stat the file to verify the content_length
* if the content_length is positive and does not
* match the size of the cache file, handle it somehow
*/
if(obj->content_length != (uint32) stat_entry.st_size)
{
/* this is a case of a server or network
* error. Assume that the file is corrupted,
* Display it as it was for history navigation
* but do a complete reload any time the server
* is contacted
* kill the last modified date to disable IMS
*/
obj->last_modified = 0;
if(URL_s)
URL_s->last_modified = 0;
TRACEMSG(("\n\n!!!!!!!! Content length of cache file not equal"
" To the content transfer length: %d - %d !!!!!!!\n",
obj->content_length, stat_entry.st_size));
}
/* fill this in for consistancy */
obj->real_content_length
= obj->content_length
= stat_entry.st_size;
}
/* must be set now */
PR_ASSERT(obj->content_length);
}
else
{
/* delete the file since we wont remember it
*/
#ifdef XP_OS2
XP_LazyFileRemove(obj->filename, fileType);
#else
XP_FileRemove(obj->filename, fileType);
#endif
return FALSE;
}
/* refresh these entries in case meta changes them */
if(URL_s)
{
obj->expires = URL_s->expires;
obj->last_modified = URL_s->last_modified;
StrAllocCopy(obj->charset, URL_s->charset);
}
}
else if(store_type == REFRESH_CACHE_STORE)
{
/* OK */
}
else
{
PR_ASSERT(0);
}
/* update the last accessed time
*/
obj->last_accessed = time(NULL); /* current time */
/* these must be true or else the partial cache logic is screwed */
PR_ASSERT(obj->content_length == obj->real_content_length
|| obj->incomplete_file);
/* create key */
key = net_GenCacheDBKey(obj->address, obj->post_data, obj->post_data_size);
/* create new dbt data object */
data = net_CacheStructToDBData(obj);
if(!key || !data)
{
TRACEMSG(("Failed to get key or data: malloc error probably"));
/* delete the file since we wont remember it
*/
#ifdef XP_OS2
XP_LazyFileRemove(obj->filename, fileType);
#else
XP_FileRemove(obj->filename, fileType);
#endif
return FALSE;
}
if(store_type == NEW_CACHE_STORE)
status = (*local_cache_database->put)(local_cache_database,
key,
data,
R_NOOVERWRITE);
else /* overwrite by default */
status = (*local_cache_database->put)(local_cache_database, key, data, 0);
if(status > 0)
{
DBT old_data;
TRACEMSG(("Duplicate cache object found"));
/* get the duplicate out of the database */
status = (*local_cache_database->get)(local_cache_database, key, &old_data, 0);
/* we should always get a success here
* or at least a DB error.
*/
assert(status < 1);
if(status == 0)
{
/* found it */
/* delete the previous object from the database
*/
/* remove the old file */
char * filename = net_GetFilenameInCacheDBT((DBT *)&old_data);
if(filename)
{
#ifdef XP_OS2
XP_LazyFileRemove(filename, fileType);
#else
XP_FileRemove(filename, fileType);
#endif
PR_Free(filename);
}
/* larubbio file size checks for SARCache */
if ( SARCache )
{
/* subtract the size of the file being removed */
URL_s->SARCache->DiskCacheSize -= net_calc_real_file_size(
net_GetInt32InCacheDBT(&old_data,
CONTENT_LENGTH_BYTE_POSITION));
URL_s->SARCache->NumberInDiskCache--;
}
else
{
/* subtract the size of the file being removed */
net_DiskCacheSize -= net_calc_real_file_size(
net_GetInt32InCacheDBT(&old_data,
CONTENT_LENGTH_BYTE_POSITION));
net_NumberInDiskCache--;
}
/* put the new data in over the old data */
status = (*local_cache_database->put)(local_cache_database, key, data, 0);
}
}
if(status < 0)
{
char * filename = net_GetFilenameInCacheDBT((DBT *)&data);
TRACEMSG(("cache update failed due to database error"));
/* remove the old file */
if(filename)
{
#ifdef XP_OS2
XP_LazyFileRemove(filename, fileType);
#else
XP_FileRemove(filename, fileType);
#endif
PR_Free(filename);
}
if ( !SARCache ) {
/* close the database */
(*local_cache_database->close)(local_cache_database);
cache_database = 0;
}
else
CACHE_CloseCache(URL_s->SARCache);
local_cache_database = 0;
}
if ( !SARCache )
{
/* add the size of the new file
* size_on_disk can be in error, use content_length
* when missized
*/
net_DiskCacheSize += net_calc_real_file_size(obj->content_length);
net_NumberInDiskCache++;
if (++cache_sync_count >= CACHE_SYNC_RATE)
{
/* sync the database everytime to guarentee
* consistancy
*/
if(local_cache_database && -1 == (*local_cache_database->sync)(local_cache_database, 0))
{
TRACEMSG(("Error syncing cache database"));
/* close the database */
(*local_cache_database->close)(local_cache_database);
cache_database = 0;
local_cache_database = 0;
}
cache_sync_count = 0;
}
}
else
{
/* larubbio */
/* add the size of the new file
* size_on_disk can be in error, use content_length
* when missized
*/
URL_s->SARCache->DiskCacheSize += net_calc_real_file_size(obj->content_length);
URL_s->SARCache->NumberInDiskCache++;
if(URL_s->SARCache->DiskCacheSize > URL_s->SARCache->MaxSize && -1 != URL_s->SARCache->MaxSize )
{
TRACEMSG(("MaxSize exceeded!!!"));
/* delete the file since we wont remember it
*/
XP_FileRemove(obj->filename, fileType);
URL_s->SARCache->DiskCacheSize -= net_calc_real_file_size(obj->content_length);
URL_s->SARCache->NumberInDiskCache--;
return FALSE;
}
/* sync the database every time to guarentee
* consistancy
*/
if(local_cache_database && -1 == (*local_cache_database->sync)(local_cache_database, 0))
{
TRACEMSG(("Error syncing cache database"));
/* close the database */
(*local_cache_database->close)(local_cache_database);
CACHE_CloseCache(URL_s->SARCache);
local_cache_database = 0;
}
else
{
/* This is inefficent to do each time, but it keeps us in synch, and this is for
offline browsing, which is not a performance intensive task */
CACHE_SaveCacheInfoToDB(URL_s->SARCache);
}
}
net_FreeCacheDBTdata(key);
net_FreeCacheDBTdata(data);
return TRUE;
}
/* Public accesor function for Netcaster */
PUBLIC PRBool
NET_CacheStore(net_CacheObject * obj,
URL_Struct * URL_s,
PRBool accept_partial_files)
{
return net_CacheStore(obj, URL_s, accept_partial_files, NEW_CACHE_STORE);
}
/* Accessor functions for cache database used by cache browser */
PUBLIC int
NET_FirstCacheObject(DBT *key, DBT *data)
{
return (*cache_database->seq)(cache_database, key, data, R_FIRST);
}
/* Accessor functions for cache database used by cache browser */
PUBLIC int
NET_NextCacheObject(DBT *key, DBT *data)
{
return (*cache_database->seq)(cache_database, key, data, R_NEXT);
}
/* Accessor functions for cache database used by cache browser */
PUBLIC int32
NET_GetMaxDiskCacheSize()
{
return net_MaxDiskCacheSize;
}
/****************************************************************
* General cache routines needed by lots of stuff
*/
typedef struct _DatePlusDBT {
DBT * dbt;
time_t date;
} DatePlusDBT;
/* removes the specified number of objects from the
* cache taking care to remove only the oldest objects.
*/
PUBLIC int32
NET_RemoveDiskCacheObjects(uint32 remove_num)
{
#define CORRUPT_ENTRY_ARRAY_SIZE 50
uint32 corrupt_entry_count = 0;
DatePlusDBT *old_array;
DBT *corrupt_entry_array[CORRUPT_ENTRY_ARRAY_SIZE];
uint32 total_cl=0;
uint32 total_number=0;
time_t date, lock_date, last_modified_date;
uint32 i,j;
char *filename;
char *url_address;
DBT data;
DBT key;
/*
* This optimization is not valid for the case of URLs that normally
* would not be cached because their size exceeds the cache size, but
* will be cached anyway because the must_cache flag of the URL is set.
* For these URLs, the condition below would be true (since we're
* trying to remove as many files as possible to make room for the
* too-big file), which would cause all files to be removed but also
* the cache database to be closed and deleted. With the database
* gone, net_CacheStore will fail, so even though the large file was
* saved correctly it won't be used. (See bug #11327.)
*/
#if 0
if(remove_num >= net_NumberInDiskCache)
{
net_RemoveAllDiskCacheObjects();
return(net_DiskCacheSize);
}
#endif
if(!cache_database)
return(net_DiskCacheSize);
old_array = (DatePlusDBT *)PR_Malloc(sizeof(DatePlusDBT) * remove_num);
if(!old_array)
return(net_DiskCacheSize);
memset(old_array, 0, sizeof(DatePlusDBT) * remove_num);
/* We need to delete the oldest items in the database.
* Sequentailly go throught the DB and remove
* the oldest items
*/
if(0 != (*cache_database->seq)(cache_database, &key, &data, R_FIRST))
return(net_DiskCacheSize);
total_cl += net_calc_real_file_size(net_GetInt32InCacheDBT(&data,
CONTENT_LENGTH_BYTE_POSITION));
total_number++;
/* Only add the first item to the old_array if the number to remove
is greater than zero, otherwise we're writing where we shouldn't be */
if(remove_num > 0)
{
old_array[0].dbt = net_CacheDBTDup(&key);
old_array[0].date = net_GetTimeInCacheDBT(&data,
LAST_ACCESSED_BYTE_POSITION);
}
while(0 == (*cache_database->seq)(cache_database, &key, &data, R_NEXT))
{
date = net_GetTimeInCacheDBT(&data, LAST_ACCESSED_BYTE_POSITION);
if(date)
last_modified_date = net_GetTimeInCacheDBT(&data,
LAST_MODIFIED_BYTE_POSITION);
/* there are other items beside FAT entries in here
* but they don't have a date
*/
if(!date)
{
/* this could be a corrupt entry */
if(!net_IsValidCacheDBT(&data))
{
if((key.size < 4 || PL_strncmp((char*)key.data,
INTERNAL_NAME_PREFIX,
4))
&& corrupt_entry_count < CORRUPT_ENTRY_ARRAY_SIZE)
{
/* store invalid cache entries in an array
* for later deletion
*/
corrupt_entry_array[corrupt_entry_count++] =
net_CacheDBTDup(&key);
TRACEMSG(("Found %d corrupted cache entries",
corrupt_entry_count));
}
}
}
else
{
url_address = net_GetAddressFromCacheKey(&key);
if(!last_modified_date
|| (url_address && PL_strncasecmp(url_address, "http",4)))
{
/* this is not a http URL or it has no last modified date.
* Delete it preferentially
* if it's older than the current session
*/
if(NET_StartupTime > date)
{
/* set the date really low so it automatically
* gets deleted
*/
date = 1;
}
}
total_cl += net_calc_real_file_size(net_GetInt32InCacheDBT(&data,
CONTENT_LENGTH_BYTE_POSITION));
total_number++;
lock_date = net_GetTimeInCacheDBT(&data, LOCK_DATE_BYTE_POSITION);
/* honor locks issued during this session */
TRACEMSG(("cache: comparing lock date %ld to startup time %ld\n",
lock_date, NET_StartupTime));
if(lock_date > NET_StartupTime)
continue;
else if(lock_date)
net_SetTimeInCacheDBT(&data,
LOCK_DATE_BYTE_POSITION,
0); /* current time */
for(i=0; i < remove_num; i++)
{
if(date < old_array[i].date || !old_array[i].date)
{
/* free the oldest one so it
* doesn't get lost as it is pushed off
* the stack
*/
if(old_array[remove_num-1].dbt)
net_FreeCacheDBTdata(old_array[remove_num-1].dbt);
/* shift all the entries down */
for(j=remove_num-1; j > i; j--)
{
old_array[j].dbt = old_array[j-1].dbt;
old_array[j].date = old_array[j-1].date;
}
old_array[i].date = date;
old_array[i].dbt = net_CacheDBTDup(&key);
break;
}
}
}
}
#ifdef XP_UNIX
/* assert(total_cl == net_DiskCacheSize); */
#endif
net_DiskCacheSize = total_cl;
#ifdef XP_UNIX
/* assert(total_number == net_NumberInDiskCache); */
#endif
net_NumberInDiskCache = total_number;
/* now old1 old2 and old3 should contain the oldest
* keys in the database. Delete them all.
*/
for(i=0; i < remove_num; i++)
{
DBT *db_obj = old_array[i].dbt;
if(db_obj)
{
if(0 == (*cache_database->get)(cache_database, db_obj, &data, 0))
{
lock_date = net_GetTimeInCacheDBT(&data, LOCK_DATE_BYTE_POSITION);
/* honor locks issued during this session */
TRACEMSG(("cache: comparing (2) lock date %ld to startup time %ld\n",
lock_date, NET_StartupTime));
if(lock_date > NET_StartupTime)
{
TRACEMSG(("cache: saved your ass again, bud!"));
continue;
}
filename = net_GetFilenameInCacheDBT(&data);
if(filename)
{
TRACEMSG(("Removing file: %s due to disk"
" cache size overflow",filename));
#ifdef XP_OS2
XP_LazyFileRemove(filename, xpCache);
#else
XP_FileRemove(filename, xpCache);
#endif
PR_Free(filename);
}
net_DiskCacheSize -= net_calc_real_file_size(
net_GetInt32InCacheDBT(&data,
CONTENT_LENGTH_BYTE_POSITION));
net_NumberInDiskCache--;
(*cache_database->del)(cache_database, db_obj, 0);
}
net_FreeCacheDBTdata(db_obj);
}
}
/* remove any invalid cache intries
*/
for(i=0; i < corrupt_entry_count; i++)
{
DBT *newkey = corrupt_entry_array[i];
if(newkey && -1 == (*cache_database->del)(cache_database, newkey, 0))
{
TRACEMSG(("Ack, error trying to delete corrupt entry, aborting",i));
break;
}
TRACEMSG(("Deleting %d corrupted cache entries",i));
net_FreeCacheDBTdata(newkey);
}
/* don't need the stale array any more */
PR_Free(old_array);
/* sync the database
*/
net_StoreDiskCacheSize();
if(-1 == (*cache_database->sync)(cache_database, 0))
{
TRACEMSG(("Error syncing cache database"));
/* close the database */
(*cache_database->close)(cache_database);
cache_database = 0;
}
return(net_DiskCacheSize);
}
PRIVATE void
net_ReduceDiskCacheTo(MWContext *window_id, uint32 size, int recurse_count)
{
uint32 avg, reduce, remove_num;
if(net_DiskCacheSize < size
|| !net_NumberInDiskCache
|| recurse_count >= 5)
return;
TRACEMSG(("net_ReduceDiskCacheTo: Current size: %d "
"Desired Size: %d Number: %d",
net_DiskCacheSize,
size,
net_NumberInDiskCache));
/* compute the avarage size of the
* contents of the disk cache
*/
if(net_NumberInDiskCache < 1)
avg = 0;
else
avg = net_DiskCacheSize/net_NumberInDiskCache;
/* compute the amount that we need to shrink
* the disk cache
*/
reduce = net_DiskCacheSize - size;
/* add 10% of the total disk cache size
* to the reduce number to reduce
* the number of times that we need to
* do this operation
*/
reduce += net_DiskCacheSize/10;
/* compute the number of objects to remove
*/
if(avg < 1)
remove_num = 1;
else
remove_num = reduce/avg;
if(remove_num < 1)
remove_num = 1;
if(window_id)
{
char buffer[256];
sprintf(buffer, XP_GetString( XP_CACHE_CLEANUP ), remove_num);
NET_Progress(window_id, buffer);
}
NET_RemoveDiskCacheObjects(remove_num);
/* make sure we removed enough
*/
if(net_DiskCacheSize > size)
net_ReduceDiskCacheTo(window_id, size, ++recurse_count);
return;
}
#ifdef NO_MEMORY_CACHE
/* set the size of the Memory cache.
* Set it to 0 if you want cacheing turned off
* completely
*/
PUBLIC void
NET_SetMemoryCacheSize(int32 new_size)
{
net_MaxMemoryCacheSize=new_size;
IL_ReduceImageCacheSizeTo(new_size);
return;
}
PUBLIC int32
NET_GetMaxMemoryCacheSize()
{
return net_MaxMemoryCacheSize;
}
/* remove the last memory cache object if one exists
* returns the total size of the memory cache in bytes
* after performing the operation
*/
PUBLIC int32
NET_RemoveLastMemoryCacheObject()
{
/* and remove something from the image cache */
return IL_ShrinkCache();
/* XXX now the mac arithmetic is wrong */
return(0);
}
/* returns the number of bytes currently in use by the Memory cache
*/
PUBLIC int32
NET_GetMemoryCacheSize()
{
return(0);
}
/* stub functions for memory cache that are no
* longer needed
*/
MODULE_PRIVATE int
NET_MemoryCacheLoad (ActiveEntry * cur_entry)
{
return(-1);
}
MODULE_PRIVATE int
NET_ProcessMemoryCache (ActiveEntry * cur_entry)
{
return(-1);
}
MODULE_PRIVATE int
NET_InterruptMemoryCache (ActiveEntry * cur_entry)
{
return(-1);
}
#endif /* MEMORY_CACHE */
/* set the size of the Disk cache.
* Set it to 0 if you want cacheing turned off
* completely
*/
PUBLIC void
NET_SetDiskCacheSize(int32 new_size)
{
if(new_size < 0)
new_size = 0;
net_MaxDiskCacheSize = new_size;
/* open it if it's not open already */
if(net_MaxDiskCacheSize > 0)
{
net_OpenCacheFatDB();
net_ReduceDiskCacheTo(NULL, (uint32) new_size, 0);
}
else
{
net_RemoveAllDiskCacheObjects();
}
return;
}
/* remove the last disk cache object if one exists
* returns the total size of the disk cache in bytes
* after performing the operation
*/
PUBLIC int32
NET_RemoveLastDiskCacheObject(void)
{
return(net_DiskCacheSize);
}
PRIVATE void
net_RemoveAllDiskCacheObjects(void)
{
char * filename;
DBT data;
DBT key;
if(!cache_database)
return;
if(0 != (*cache_database->seq)(cache_database, &key, &data, R_FIRST))
return;
do
{
filename = net_GetFilenameInCacheDBT(&data);
if(filename)
{
TRACEMSG(("Removing file: %s due to disk"
" cache size remove",filename));
#ifdef XP_OS2
XP_LazyFileRemove(filename, xpCache);
#else
XP_FileRemove(filename, xpCache);
#endif
PR_Free(filename);
}
/* We don't acctually need to do the
* del since we will del the database
*/
/* (*cache_database->del)(cache_database, &key, 0); */
}
while(0 == (*cache_database->seq)(cache_database, &key, &data, R_NEXT));
net_DiskCacheSize = 0;
net_NumberInDiskCache = 0;
if(-1 == (*cache_database->close)(cache_database))
{
TRACEMSG(("Error closing cache database"));
}
cache_database = 0;
/* remove the database */
XP_FileRemove("", xpCacheFAT);
}
/* returns the number of bytes currently in use by the Disk cache
*/
PUBLIC int32
NET_GetDiskCacheSize()
{
return(net_DiskCacheSize);
}
/******************************************************************
* Cache Stream input routines
*/
/* is the stream ready for writing?
*/
PRIVATE unsigned int net_CacheWriteReady (NET_StreamClass *stream)
{
CacheDataObject *obj=stream->data_object;
if(obj->next_stream)
return((*obj->next_stream->is_write_ready)(obj->next_stream));
else
return(MAX_WRITE_READY);
}
/* stream write function
*/
PRIVATE int net_CacheWrite (NET_StreamClass *stream, CONST char* buffer, int32 len)
{
CacheDataObject *obj=stream->data_object;
TRACEMSG(("net_CacheWrite called with %ld buffer size", len));
if(obj->fp)
{
/* increase the global cache size counter
*/
if(XP_FileWrite((char *) buffer, len, obj->fp) < (uint32)len)
{
TRACEMSG(("Error writing to cache file"));
XP_FileClose(obj->fp);
#ifdef XP_OS2
XP_LazyFileRemove(obj->cache_object->filename, xpCache);
#else
XP_FileRemove(obj->cache_object->filename, xpCache);
#endif
if(obj->URL_s)
obj->URL_s->dont_cache = TRUE;
obj->fp = 0;
}
}
if(obj->next_stream)
{
int status=0;
assert (buffer);
assert (len > -1);
status = (*obj->next_stream->put_block)
(obj->next_stream, buffer, len);
/* abort */
if(status < 0)
return(status);
}
return(1);
}
/* complete the stream
*/
PRIVATE void net_CacheComplete (NET_StreamClass *stream)
{
CacheDataObject *obj=stream->data_object;
/* close the cache file
*/
if(obj->fp)
XP_FileClose(obj->fp);
assert(obj->cache_object);
/* add it to the database */
if(net_CacheStore(obj->cache_object,
obj->URL_s,
FALSE,
NEW_CACHE_STORE))
{
/* set the cache_file in the url struct so that Plugins can
* use it...
*/
if(obj->URL_s)
StrAllocCopy(obj->URL_s->cache_file, obj->cache_object->filename);
}
/* complete the next stream
*/
if(obj->next_stream)
{
(*obj->next_stream->complete)(obj->next_stream);
PR_Free(obj->next_stream);
}
net_freeCacheObj(obj->cache_object);
PR_REMOVE_LINK(&obj->links);
PR_Free(obj);
return;
}
/* Abort the stream
*
*/
PRIVATE void net_CacheAbort (NET_StreamClass *stream, int status)
{
CacheDataObject *obj=stream->data_object;
PR_ASSERT(obj);
PR_ASSERT(obj->cache_object);
/* abort the next stream
*/
if(obj->next_stream)
{
(*obj->next_stream->abort)(obj->next_stream, status);
PR_Free(obj->next_stream);
}
/* fp might be null if we had a file write error */
if(obj->fp)
{
XP_FileClose(obj->fp);
/* do this test in here since the file must be
* alright for us to use this
*
* test to see if we were user interrupted and if
* the server support byteranges. If so, store
* the partial object since we will request it
* with a partial byterange later
*
* Also, we can't do partial caching unless we know
* the content_length
*/
if(obj->URL_s
&& obj->URL_s->content_length
&& (obj->URL_s->server_can_do_byteranges || obj->URL_s->server_can_do_restart)
&& !obj->URL_s->dont_cache)
{
/* store the partial file */
if(obj->URL_s->real_content_length > 0)
obj->cache_object->content_length = obj->URL_s->real_content_length;
net_CacheStore(obj->cache_object,
obj->URL_s,
TRUE,
NEW_CACHE_STORE);
}
else if(status == MK_INTERRUPTED
&& obj->URL_s->content_length
&& !PL_strcasecmp(obj->URL_s->content_type, TEXT_HTML))
{
/* temporarly cache interrupted HTML pages for history navigation */
obj->URL_s->last_modified = 0;
net_CacheStore(obj->cache_object,
obj->URL_s,
FALSE,
NEW_CACHE_STORE);
}
else
{
#ifdef XP_OS2
XP_LazyFileRemove(obj->cache_object->filename, xpCache);
#else
XP_FileRemove(obj->cache_object->filename, xpCache);
#endif
}
}
net_freeCacheObj(obj->cache_object);
PR_REMOVE_LINK(&obj->links);
PR_Free(obj);
return;
}
#ifdef XP_UNIX
extern char **fe_encoding_extensions; /* gag! */
#endif
/* setup the stream
*/
MODULE_PRIVATE NET_StreamClass *
NET_CacheConverter (FO_Present_Types format_out,
void *converter_obj,
URL_Struct *URL_s,
MWContext *window_id)
{
CacheDataObject * data_object=0;
net_CacheObject * cache_object=0;
NET_StreamClass * stream=0;
char *filename=0, *new_filename=0;
char *org_content_type = 0;
XP_Bool do_disk_cache=FALSE;
XP_Bool want_to_cache=FALSE;
XP_File fp=0;
NET_StreamClass * next_stream=0;
/* XXX brendan will #define this hack after 3.0 ships! */
XP_Bool dont_hold_URL_s = (converter_obj != NULL);
TRACEMSG(("Setting up cache stream. Have URL: %s\n", URL_s->address));
FE_EnableClicking(window_id);
#ifdef MOZILLA_CLIENT
/* add this new url to the Global history since we must be getting it now
*/
if (NET_URL_Type(URL_s->address) != WYSIWYG_TYPE_URL && URL_s->SARCache == NULL)
GH_UpdateGlobalHistory(URL_s);
#endif /* MOZILLA_CLIENT */
StrAllocCopy(org_content_type, URL_s->content_type);
if(!org_content_type)
return(NULL);
if(format_out != FO_CACHE_ONLY)
{
format_out = CLEAR_CACHE_BIT(format_out);
next_stream = NET_StreamBuilder(format_out, URL_s, window_id);
if(!next_stream)
{
PR_FREEIF(org_content_type);
return(NULL);
}
}
/* set the content type back the way it should be
* this is because some streams muck with it and
* dont set it back and we need it intact
*/
PR_Free(URL_s->content_type);
URL_s->content_type = org_content_type;
org_content_type = 0;
/* do we want to cache this object?
*/
if(URL_s->server_can_do_restart) URL_s->must_cache = TRUE;
TRACEMSG(("cache: URL_s->must_cache is %s", URL_s->must_cache ? "TRUE" : "FALSE"));
if(URL_s->must_cache /* overrides all the rest of these tests */
|| (CLEAR_CACHE_BIT(format_out) == FO_INTERNAL_IMAGE
|| format_out == FO_CACHE_ONLY
|| !PL_strncasecmp(URL_s->content_type, "IMAGE", 5)
|| !PL_strncasecmp(URL_s->content_type, "TEXT", 4)
|| !PL_strncasecmp(URL_s->content_type, "MESSAGE", 7)
|| !PL_strcasecmp(URL_s->content_type, APPLICATION_HTTP_INDEX)
|| !PL_strcasecmp(URL_s->content_type, APPLICATION_JAVAARCHIVE)
|| ( (URL_s->content_length>0) && ((uint32) URL_s->content_length < (net_MaxDiskCacheSize/4)) )
)
)
{
/* set a flag to say that we want to cache */
want_to_cache = TRUE;
}
/* set a flag if we should disk cache it based
* on whether the cache setting is non-zero and
* the cache database is open
*/
TRACEMSG(("cache: want_to_cache is %s", want_to_cache ? "TRUE" : "FALSE"));
if(want_to_cache
&& (!net_dont_disk_cache_ssl
|| PL_strncasecmp(URL_s->address, "https:", 6))
&& !URL_s->dont_cache
&& (net_MaxDiskCacheSize > 0 || URL_s->must_cache)
&& net_OpenCacheFatDB() /* make sure database is open */
&& PL_strncasecmp(URL_s->address, "news:", 5) /* not news: */
&& PL_strncasecmp(URL_s->address, "snews:", 6) /* not snews: */
&& PL_strncasecmp(URL_s->address, "mailbox://", 10)) /* not IMAP */
do_disk_cache = TRUE;
/* Special case for StreamAsFile plugins: */
if (URL_s->must_cache /* this only set by plugin code ? */
&& ((PL_strncasecmp(URL_s->address, "news:", 5)==0) /* is news */
|| (PL_strncasecmp(URL_s->address, "https:", 6)==0) /* is secure */
|| !PL_strncasecmp(URL_s->address, "mailbox://", 10))) /* is imap */
do_disk_cache = TRUE;
/* malloc and init all the necessary structs
* do_disk_cache will be set false on error
*/
if(do_disk_cache)
{
TRACEMSG(("Object has not been cached before"));
cache_object = PR_NEW(net_CacheObject);
if (!cache_object)
{
return(NULL);
}
/* assign and init the cache object */
memset(cache_object, 0, sizeof(net_CacheObject));
/* copy the contents of the URL structure
*/
StrAllocCopy(cache_object->address, URL_s->address);
cache_object->method = URL_s->method;
if(URL_s->post_data)
{
cache_object->post_data_size = URL_s->post_data_size;
StrAllocCopy(cache_object->post_data, URL_s->post_data);
StrAllocCopy(cache_object->post_headers, URL_s->post_headers);
}
/* add expiration stuff from URL structure
*/
cache_object->expires = URL_s->expires;
cache_object->last_modified = URL_s->last_modified;
cache_object->content_length = URL_s->content_length;
cache_object->real_content_length = URL_s->content_length;
cache_object->is_netsite = URL_s->is_netsite;
StrAllocCopy(cache_object->content_type, URL_s->content_type);
StrAllocCopy(cache_object->charset, URL_s->charset);
StrAllocCopy(cache_object->content_encoding, URL_s->content_encoding);
/* copy security info */
cache_object->security_on = URL_s->security_on;
cache_object->sec_info = SECNAV_CopySSLSocketStatus(URL_s->sec_info);
StrAllocCopy(cache_object->page_services_url, URL_s->page_services_url);
/* we always use relative paths in the main disk cache
*/
cache_object->is_relative_path = TRUE;
/* open a file for the cache
larubbio added SARCache check it's late in 4.0 cycle, and I don't want to have
any impact on the other platforms
*/
filename = WH_TempName(
#ifdef XP_UNIX
(NULL != URL_s->SARCache) ? xpSARCache :
#endif
xpCache, "cache");
if (filename)
{
/* Put an appropriate suffix on the tmp cache file */
if (cache_object->address)
{
char *tail;
char *suffix;
char *allocSuffix; /* in case we have to allocate a suffix */
char *end;
char *junk;
char old_char;
suffix = allocSuffix = NULL;
if (URL_s->content_name){
suffix = (URL_s->content_name ? PL_strrchr (URL_s->content_name, '.') : 0);
}
else if ((URL_s->address) &&
(!PL_strncasecmp(URL_s->address, "mailbox:", 8)
|| !PL_strncasecmp(URL_s->address, "news:", 5)
|| !PL_strncasecmp(URL_s->address, "snews:", 6))
&& (URL_s->content_type) && (*(URL_s->content_type)))
{
/*
Gag. If we're a mailbox URL and can't figure out the content name,
but have a content type, ask the type registry for a suitable suffix.
(This allows us to set up Java and audio cache files so that those
subsystems will actually work with the cache files. Ick.)
*/
char *regSuffix = NET_cinfo_find_ext(URL_s->content_type);
if (regSuffix)
{
suffix = PR_smprintf(".%s", regSuffix);
allocSuffix = suffix; /* we allocated it here, we delete it below */
}
}
if (!suffix)
{
tail = PL_strrchr (cache_object->address, '/');
suffix = (tail ? PL_strrchr (tail, '.') : 0);
}
end = suffix + (suffix ? PL_strlen (suffix) : 0);
junk=0;
#ifdef XP_UNIX
/* Gag. foo.html.gz --> cacheXXXXX.html, not cacheXXXXX.gz. */
if (suffix && fe_encoding_extensions)
{
int i = 0;
while (fe_encoding_extensions [i])
{
if (!PL_strcmp (suffix, fe_encoding_extensions [i]))
{
end = suffix;
suffix--;
while (suffix > tail && *suffix != '.')
suffix--;
if (*suffix != '.')
suffix = 0;
break;
}
i++;
}
}
#endif
/* Avoid "cache2F1F6AD102169F0.sources&maxhits=10" */
if (suffix)
{
junk = suffix + 1;
while (isalnum (*junk))
junk++;
old_char = *junk;
*junk = 0;
}
#ifdef XP_PC
/* Remove any suffix that the temp filename currently has
*/
strtok(filename, ".");
#endif
if (suffix && (end - suffix) < 20)
{
/* make sure it is terminated... */
if(!junk)
{
junk = end;
old_char = *junk;
*junk = 0;
}
StrAllocCopy(new_filename, filename);
#ifdef XP_PC
/* the comment */
/* say 16 bit */
/* 32 bit Win */
/* goes thru */
/* here, so do */
/* the same for*/
/* OS2 */
/* make all suffixes be UPPERCASE for win16
* since the operating system will make
* them so and we need to know the exact
* name for hashing and comparison purposes
*/
if(1)
{
char *new_suffix = PL_strdup(suffix);
if(new_suffix)
{
char *cp = new_suffix;
int i;
/* limit it to 4 chars.
* a dot and three letters
*/
for(i=0; *cp && i < 4; i++)
{
*cp = NET_TO_UPPER(*cp);
cp++;
}
*cp = '\0'; /* make sure it's terminated */
StrAllocCat(new_filename, new_suffix);
PR_Free(new_suffix);
}
}
#else
StrAllocCat(new_filename, suffix);
#endif
}
else
{
StrAllocCopy(new_filename, filename);
}
if(junk)
*junk = old_char;
if (allocSuffix)
PR_Free(allocSuffix);
}
PR_Free(filename);
}
if(new_filename)
{
/* larubbio added 197 SAR cache checks */
if ( URL_s->SARCache == NULL )
fp = XP_FileOpen(new_filename, xpCache, XP_FILE_WRITE_BIN);
else
fp = XP_FileOpen(new_filename, xpSARCache, XP_FILE_WRITE_BIN);
}
if(!fp)
{
net_freeCacheObj(cache_object);
cache_object = 0;
do_disk_cache = FALSE;
PR_FREEIF(new_filename);
}
else
{
cache_object->filename = new_filename;
/* cap the total size of the cache
*/
/* larubbio disk cache size checks for SARCache
* Also, don't reduce the cache size yet for "must_cache"
* objects, since in the download-restart case the object
* will be deleted most of the time before it ever gets
* counted in the cache. Objects that do get put into
* the cache will self correct at the next call to
* ReduceDiskCacheTo()
*/
if(net_DiskCacheSize+URL_s->content_length > net_MaxDiskCacheSize
&& URL_s->SARCache == NULL
&& !URL_s->must_cache)
{
int32 new_size = net_MaxDiskCacheSize - URL_s->content_length;
if(new_size < 0)
new_size = 0;
net_ReduceDiskCacheTo(window_id, (uint32)new_size, 0);
}
}
}
/* if we get this far and do_disk_cache is true
* then we want cache and all the setup succeeded.
*/
if(do_disk_cache)
{
TRACEMSG(("Caching this URL!!!!"));
data_object = PR_NEW(CacheDataObject);
if (!data_object)
{
XP_FileClose(fp);
return(NULL);
}
stream = PR_NEW(NET_StreamClass);
if(!stream)
{
XP_FileClose(fp);
PR_Free(data_object);
return(NULL);
}
/* init the object */
memset(data_object, 0, sizeof(CacheDataObject));
/* assign the cache object to the stream data object
*/
data_object->cache_object = cache_object;
/* save the URL struct since we will
* need to use some data from it later
* XXX if converter_obj is non-null it is a flag telling us not
* XXX to hold a pointer to the URL struct.
*/
if (dont_hold_URL_s == FALSE)
data_object->URL_s = URL_s;
data_object->fp = fp;
TRACEMSG(("Returning stream from NET_CacheConverter\n"));
stream->name = "Cache stream";
stream->complete = (MKStreamCompleteFunc) net_CacheComplete;
stream->abort = (MKStreamAbortFunc) net_CacheAbort;
stream->put_block = (MKStreamWriteFunc) net_CacheWrite;
stream->is_write_ready = (MKStreamWriteReadyFunc) net_CacheWriteReady;
stream->data_object = data_object; /* document info object */
stream->window_id = window_id;
if(format_out != FO_CACHE_ONLY)
data_object->next_stream = next_stream;
PR_APPEND_LINK(&data_object->links, &active_cache_data_objects);
return stream;
}
else
{
TRACEMSG(("NOT Disk Caching this URL"));
if(want_to_cache)
{
net_MemCacheConverterObject mem_conv_obj;
mem_conv_obj.next_stream = next_stream;
mem_conv_obj.dont_hold_URL_s = dont_hold_URL_s;
return(NET_MemCacheConverter(format_out,
&mem_conv_obj,
URL_s,
window_id));
}
/* bypass the whole cache mechanism
*/
if(format_out != FO_CACHE_ONLY)
{
format_out = CLEAR_CACHE_BIT(format_out);
return(next_stream);
}
}
return(NULL);
}
/***************************************************************************
* Cache object manipulation stuff
*/
/* locks or unlocks a cache file. The
* cache lock is only good for a single
* session
*/
PUBLIC XP_Bool
NET_ChangeCacheFileLock(URL_Struct *URL_s, XP_Bool set)
{
int status;
DBT data;
DBT * key;
DBT * new_data=0;
net_CacheObject *found_cache_obj;
/* lock or unlock the memory cache too */
NET_ChangeMemCacheLock(URL_s, set);
if(!cache_database)
return(FALSE);
key = net_GenCacheDBKey(URL_s->address,
URL_s->post_data,
URL_s->post_data_size);
status = (*cache_database->get)(cache_database, key, &data, 0);
if(status != 0)
{
TRACEMSG(("Key not found in database"));
net_FreeCacheDBTdata(key);
return(FALSE);
}
found_cache_obj = net_Fast_DBDataToCacheStruct(&data);
if(!found_cache_obj)
{
TRACEMSG(("mkcache: Corrupted db entry"));
return(FALSE);
}
if(set)
found_cache_obj->lock_date = time(NULL);
else
found_cache_obj->lock_date = 0;
/* create new dbt data object */
new_data = net_CacheStructToDBData(found_cache_obj);
if(new_data)
status = (*cache_database->put)(cache_database, key, new_data, 0);
return(status == 0);
}
MODULE_PRIVATE void NET_RefreshCacheFileExpiration(URL_Struct * URL_s)
{
net_CacheObject *found_cache_obj;
int status;
DBT *key;
DBT data;
if(!cache_database)
{
TRACEMSG(("Cache database not open"));
return;
}
/* only update if the server status is 304
* The other cases involve a new cache object
* and all the info will automatically get updated
*/
if(URL_s->server_status != 304)
return;
/* gen a key */
key = net_GenCacheDBKey(URL_s->address,
URL_s->post_data,
URL_s->post_data_size);
/* look up the key */
status = (*cache_database->get)(cache_database, key, &data, 0);
/* don't need the key anymore */
net_FreeCacheDBTdata(key);
if(status != 0)
return;
/* turn the cache DB object into a cache object */
found_cache_obj = net_Fast_DBDataToCacheStruct(&data);
if(!found_cache_obj)
return;
/* stick the address and post data in there
* since this stuff is ususally in the Key
*/
StrAllocCopy(found_cache_obj->address, URL_s->address);
if(URL_s->post_data_size)
{
found_cache_obj->post_data_size = URL_s->post_data_size;
BlockAllocCopy(found_cache_obj->post_data,
URL_s->post_data,
URL_s->post_data_size);
}
/* this will update the last modified time */
net_CacheStore(found_cache_obj,
NULL,
FALSE,
REFRESH_CACHE_STORE);
return;
#if 0
/* this is the really old version of RefreshURL */
/* this isn't really needed because we can do it all
* in the Complete step by keeping a copy if the URL struct.
*/
int status;
DBT data;
DBT * key;
DBT * new_data=0;
if(!cache_database)
return;
key = net_GenCacheDBKey(URL_s->address,
URL_s->post_data,
URL_s->post_data_size);
status = (*cache_database->get)(cache_database, key, &data, 0);
if(status != 0)
{
TRACEMSG(("Key not found in database"));
net_FreeCacheDBTdata(key);
return;
}
new_data = net_CacheDBTDup(&data);
if(!new_data)
{
net_FreeCacheDBTdata(key);
return;
}
net_SetTimeInCacheDBT(new_data,
EXPIRES_BYTE_POSITION,
URL_s->expires);
net_SetTimeInCacheDBT(new_data,
LAST_MODIFIED_BYTE_POSITION,
URL_s->last_modified);
net_SetTimeInCacheDBT(new_data,
LAST_ACCESSED_BYTE_POSITION,
time(NULL)); /* current time */
TRACEMSG(("Refreshing the cache expires date: %d last_mod: %d\n",
URL_s->expires, URL_s->last_modified));
status = (*cache_database->put)(cache_database, key, new_data, 0);
#ifdef DEBUG
if(status != 0)
TRACEMSG(("Error updateing cache info in database"));
#endif /* DEBUG */
net_FreeCacheDBTdata(key);
net_FreeCacheDBTdata(new_data);
return;
#endif
}
/* returns TRUE if the url is in the disk cache
*/
PUBLIC XP_Bool
NET_IsURLInDiskCache(URL_Struct *URL_s)
{
DBT *key;
DBT data;
int status;
if(!cache_database)
{
TRACEMSG(("Cache database not open"));
return(0);
}
key = net_GenCacheDBKey(URL_s->address,
URL_s->post_data,
URL_s->post_data_size);
status = (*cache_database->get)(cache_database, key, &data, 0);
net_FreeCacheDBTdata(key);
if(status == 0)
return(TRUE);
else
return(FALSE);
}
/* remove a URL from the cache
*/
MODULE_PRIVATE void
NET_RemoveURLFromCache(URL_Struct *URL_s)
{
int status;
DBT data;
DBT *key;
/* larubbio */
XP_Bool SARCache = FALSE;
XP_FileType fileType;
DB *local_cache_database = NULL;
/* larubbio fix to allow 197 SAR cache adds without modifying
* the navigator cache (too much)
*/
if ( URL_s != NULL && URL_s->SARCache != NULL )
{
SARCache = TRUE;
fileType = xpSARCache;
local_cache_database = URL_s->SARCache->database;
}
else
{
fileType = xpCache;
local_cache_database = cache_database;
}
NET_RemoveURLFromMemCache(URL_s);
if(!local_cache_database)
return;
key = net_GenCacheDBKey(URL_s->address,
URL_s->post_data,
URL_s->post_data_size);
if(!key)
return;
status = (*local_cache_database->get)(local_cache_database, key, &data, 0);
if(status == 0)
{
char * filename = net_GetFilenameInCacheDBT(&data);
if(filename)
{
#ifdef XP_OS2
XP_LazyFileRemove(filename, fileType);
#else
XP_FileRemove(filename, fileType);
#endif
PR_Free(filename);
}
(*local_cache_database->del)(local_cache_database, key, 0);
if ( SARCache )
{
/* subtract the size of the file being removed */
URL_s->SARCache->DiskCacheSize -= net_calc_real_file_size(
net_GetInt32InCacheDBT(&data,
CONTENT_LENGTH_BYTE_POSITION));
URL_s->SARCache->NumberInDiskCache--;
}
else
{
net_DiskCacheSize -= net_calc_real_file_size(
net_GetInt32InCacheDBT(&data,
CONTENT_LENGTH_BYTE_POSITION));
net_NumberInDiskCache--;
}
}
net_FreeCacheDBTdata(key);
}
/* return TRUE if the URL is in the cache and
* is a partial cache file
*/
MODULE_PRIVATE PRBool
NET_IsPartialCacheFile(URL_Struct *URL_s)
{
net_CacheObject *found_cache_obj;
int status;
DBT *key;
DBT data;
if(!cache_database)
{
TRACEMSG(("Cache database not open"));
return(0);
}
key = net_GenCacheDBKey(URL_s->address,
URL_s->post_data,
URL_s->post_data_size);
status = (*cache_database->get)(cache_database, key, &data, 0);
net_FreeCacheDBTdata(key);
if(status != 0)
return(0);
found_cache_obj = net_Fast_DBDataToCacheStruct(&data);
if(found_cache_obj)
return(found_cache_obj->incomplete_file);
else
return(0);
}
PUBLIC int
NET_FindURLInCache(URL_Struct * URL_s, MWContext *ctxt)
{
net_CacheObject *found_cache_obj;
XP_StatStruct stat_entry;
int status;
char *byterange;
char byterange_char;
DBT *key;
DBT data;
TRACEMSG(("Checking for URL in cache"));
/* zero the last modified date so that we don't
* screw up the If-modified-since header by
* having it in even when the document isn't
* cached.
*/
URL_s->last_modified = 0;
if(!cache_database)
{
TRACEMSG(("Cache database not open"));
/* can't find it here. Let's look
* in the memory and external caches
*/
status = NET_FindURLInMemCache(URL_s, ctxt);
if(status)
return(status);
else
return(NET_FindURLInExtCache(URL_s, ctxt));
}
byterange = PL_strcasestr(URL_s->address, ";bytes=");
/* this might be a cached imap url. Ignore the mime
part when going back for attachments, otherwise we'll
reload the attachment from the server */
if (!byterange)
byterange = PL_strcasestr(URL_s->address, "&part=");
if(byterange)
{
byterange_char = *byterange;
*byterange = '\0';
}
key = net_GenCacheDBKey(URL_s->address,
URL_s->post_data,
URL_s->post_data_size);
if(byterange)
*byterange = byterange_char;
status = (*cache_database->get)(cache_database, key, &data, 0);
/* try it again with the byterange stuff
*/
if(status != 0 && byterange)
{
net_FreeCacheDBTdata(key);
key = net_GenCacheDBKey(URL_s->address,
URL_s->post_data,
URL_s->post_data_size);
status = (*cache_database->get)(cache_database, key, &data, 0);
}
/* If the file doesn't have an extension try adding
* a slash to the end and look for it again.
* this will catch the most common redirect cases
*/
if(status != 0)
{
char *slash;
slash = PL_strrchr(URL_s->address, '/');
if(slash && !PL_strchr(slash, '.') && *(slash+1) != '\0')
{
/* no extension. Add the slash and
* look for it in the database
*/
StrAllocCat(URL_s->address, "/");
net_FreeCacheDBTdata(key);
key = net_GenCacheDBKey(URL_s->address,
URL_s->post_data,
URL_s->post_data_size);
status = (*cache_database->get)(cache_database, key, &data, 0);
if(status != 0)
{
/* adding the slash didn't work, get rid of it
*/
URL_s->address[PL_strlen(URL_s->address)-1] = '\0';
}
}
}
/* If the java Cache api is requesting this file, and it was found in the
* navigator cache, remove it from that cache so it can be recached in the proper archive cache
*/
if( (0 == status) && (NULL != URL_s->SARCache) )
{
found_cache_obj = net_Fast_DBDataToCacheStruct(&data);
if(!found_cache_obj)
{
TRACEMSG(("mkcache: Corrupted db entry"));
return(0);
}
/* remove the object from the cache
*/
XP_FileRemove(found_cache_obj->filename, xpCache);
if(cache_database)
status = (*cache_database->del)(cache_database, key, 0);
/* remove the size of that file from the running total */
net_DiskCacheSize -= net_calc_real_file_size(
found_cache_obj->content_length);
status = -1;
}
if(status != 0)
{
TRACEMSG(("Key not found in database"));
net_FreeCacheDBTdata(key);
/* can't find it here. Let's look
* in the memory and external caches
*/
status = NET_FindURLInMemCache(URL_s, ctxt);
if(status)
return(status);
else
return(NET_FindURLInExtCache(URL_s, ctxt));
}
found_cache_obj = net_Fast_DBDataToCacheStruct(&data);
if(!found_cache_obj)
{
TRACEMSG(("mkcache: Corrupted db entry"));
return(0);
}
TRACEMSG(("mkcache: found URL in cache!"));
/* pretend we don't have the object cached if we are looking
* for a byterange URL and the object is only partially
* cached
*/
if(byterange && found_cache_obj->incomplete_file)
{
return(0);
}
/* make sure the file still exists
*/
if(XP_Stat(found_cache_obj->filename, &stat_entry, xpCache) == -1)
{
/* file does not exist!!
* remove the entry
*/
TRACEMSG(("Error! Cache file missing"));
if(cache_database)
status = (*cache_database->del)(cache_database, key, 0);
/* remove the size of that file from the running total */
net_DiskCacheSize -= net_calc_real_file_size(
found_cache_obj->content_length);
#ifdef DEBUG
if(status != 0)
TRACEMSG(("Error deleting bad file from cache"));
#endif /* DEBUG */
net_FreeCacheDBTdata(key);
return(0);
}
/*
* if the last accessed date is before the startup date set the
* expiration date really low so that the URL is forced to be rechecked
* again. We don't just not return the URL as being in the
* cache because we want to use "If-modified-since"
*
* This works correctly because mkhttp will zero the
* expires field.
*
* if it's not an http url then just delete the entry
* since we can't do an If-modified-since
*
* NOTE: use the use_local_copy flag as a signal not to
* do the "once per session" check since it is possible
* that the cache is read only, also use history_num
* since we shouldn't reload on history navigation or resize
*/
if(!URL_s->use_local_copy)
{
if(found_cache_obj->last_accessed < NET_StartupTime
&& NET_CacheUseMethod != CU_NEVER_CHECK
#ifdef MOZ_OFFLINE
&& !NET_IsOffline()
#endif /* MOZ_OFFLINE */
) /* *X* check for is offline */
{
if(!PL_strncasecmp(URL_s->address, "http", 4))
{
URL_s->expires = 1;
#if 0
/* this is ifdeffed because of the following bug:
* If you update now, there is still the possibility
* of putting this URL on the wait queue. If that
* happens, when the URL gets pulled off the wait queue
* it will be called with NET_GetURL again and come
* here again. Since we already updated it wont
* get set expired again. So instead I will update
* the URL when I get a 304 in NET_RefreshCacheFileExpiration
*/
/* stick the address and post data in there
* since this stuff is in the Key
*/
StrAllocCopy(found_cache_obj->address, URL_s->address);
if(URL_s->post_data_size)
{
found_cache_obj->post_data_size = URL_s->post_data_size;
BlockAllocCopy(found_cache_obj->post_data,
URL_s->post_data,
URL_s->post_data_size);
}
/* this will update the last accessed time
*/
net_CacheStore(found_cache_obj,
NULL,
FALSE,
REFRESH_CACHE_STORE);
#endif
}
else
{
/* remove the object from the cache
*/
#ifdef XP_OS2
XP_LazyFileRemove(found_cache_obj->filename, xpCache);
#else
XP_FileRemove(found_cache_obj->filename, xpCache);
#endif
/* remove the size of that file from the running total */
net_DiskCacheSize -= net_calc_real_file_size(
found_cache_obj->content_length);
status = (*cache_database->del)(cache_database, key, 0);
#ifdef DEBUG
if(status != 0)
TRACEMSG(("Error deleting expired file from cache"));
#endif /* DEBUG */
net_FreeCacheDBTdata(key);
return(0);
}
}
else
{
/* otherwise use the normal expires date */
URL_s->expires = found_cache_obj->expires;
}
}
else
{
URL_s->expires = 0; /* prevent looping */
}
/* we don't need the key anymore */
net_FreeCacheDBTdata(key);
/* copy in the cache file name
*/
StrAllocCopy(URL_s->cache_file, found_cache_obj->filename);
/* copy the contents of the URL struct so that the content type
* and other stuff gets recorded
*/
if(!URL_s->preset_content_type)
StrAllocCopy(URL_s->content_type, found_cache_obj->content_type);
StrAllocCopy(URL_s->charset, found_cache_obj->charset);
StrAllocCopy(URL_s->content_encoding, found_cache_obj->content_encoding);
StrAllocCopy(URL_s->page_services_url, found_cache_obj->page_services_url);
/* only set these for HTTP url's
* otherwise the content-length will mess up files that don't
* return a content-length and are different
*/
if(!PL_strncasecmp(URL_s->address, "http", 4) || !PL_strncasecmp(URL_s->address, "ftp", 3))
{
URL_s->content_length = found_cache_obj->content_length;
URL_s->real_content_length = found_cache_obj->real_content_length;
if(URL_s->content_length != URL_s->real_content_length
&& !found_cache_obj->incomplete_file)
{
PR_ASSERT(0);
URL_s->real_content_length = 0;
URL_s->content_length = 0;
/* content type indicates a partial cache file but
* the incomplete file flag is not set. Something
* went wrong.
*/
return(0); /* return not found */
}
}
URL_s->last_modified = found_cache_obj->last_modified;
URL_s->is_netsite = found_cache_obj->is_netsite;
/* copy security info */
URL_s->security_on = found_cache_obj->security_on;
URL_s->sec_info = SECNAV_CopySSLSocketStatus(found_cache_obj->sec_info);
TRACEMSG(("Cached copy is valid. returning method"));
TRACEMSG(("Using Disk Copy"));
return(FILE_CACHE_TYPE_URL);
}
/* read the Cache File allocation table.
*/
PUBLIC void
NET_ReadCacheFAT(char * cachefatfile, XP_Bool stat_files)
{
if(net_MaxDiskCacheSize > 0)
net_OpenCacheFatDB();
return;
}
PUBLIC void
NET_CacheInit(void)
{
NET_ReadCacheFAT("", TRUE);
}
/* unload the disk cache FAT list to disk
*
* set final_call to true if this is the last call to
* this function before cleaning up the cache and shutting down.
*
* Front ends should never set final_call to TRUE, this will
* be done by the cache cleanup code when the netlib is being
* shutdown.
*/
PUBLIC void
NET_WriteCacheFAT(char *filename, XP_Bool final_call)
{
net_StoreDiskCacheSize();
if(!cache_database)
return;
if(-1 == (*cache_database->sync)(cache_database, 0))
{
TRACEMSG(("Error syncing cache database"));
/* close the database */
(*cache_database->close)(cache_database);
cache_database = 0;
}
return;
}
MODULE_PRIVATE void
NET_CleanupCache (char * filename)
{
net_StoreDiskCacheSize();
if(!cache_database)
return;
if(-1 == (*cache_database->close)(cache_database))
{
TRACEMSG(("Error closing cache database"));
}
cache_database = 0;
CACHE_CloseAllOpenSARCache();
return;
}
/* returns the number of files currently in the
* disk cache
*/
PUBLIC uint32
NET_NumberOfFilesInDiskCache()
{
return(net_NumberInDiskCache);
}
static int
net_cache_strcmp (const void *a, const void *b)
{
return PL_strcmp ((const char *) a, (const char *) b);
}
/* this function recurses through all directories in
* the cache directory and stuffs all the files into
* the hash table. Any directory names below the
* root cache directory should have their relative path
* names prepended to them
*
* returns 0 on success, -1 on failure
*/
PRIVATE int
net_cache_recursive_file_finder(XP_HashList *hash_table,
const char *prefix,
char *cur_dir,
char *base_dir,
char *buffer)
{
XP_Dir dir_ptr;
XP_DirEntryStruct * dir_entry;
int base_len;
int prefix_len, d_len, status;
char *dir_prefix=0;
char *d_name;
XP_Bool add_dir_prefix=TRUE;
/* compute the difference between base_dir and
* cur_dir. The difference should be prepended
* to the front of filenames
*/
base_len = PL_strlen(base_dir);
StrAllocCopy(dir_prefix, cur_dir+base_len);
if(!dir_prefix)
return(-1);
if(*dir_prefix == '\0')
add_dir_prefix = FALSE;
else if(dir_prefix[PL_strlen(dir_prefix)-1] == '/')
dir_prefix[PL_strlen(dir_prefix)-1] = '\0';
if(!(dir_ptr = XP_OpenDir (cur_dir, xpCache)))
{
PR_Free(dir_prefix);
return(-1);
}
/* add all the files on disk to a hash table
*/
prefix_len = PL_strlen(prefix);
while((dir_entry = XP_ReadDir(dir_ptr)) != 0)
{
/* only do comparison and delete if the prefix
* is the same
*/
d_name = dir_entry->d_name;
d_len = PL_strlen(d_name);
status = 0;
if(PL_strncmp(d_name, prefix, prefix_len))
{
#if 0
XP_StatStruct stat_entry;
#endif
/* This directory didn't begin with the cache prefix, so it
*should* be a directory. But if it's not, then it's some
random junk that somehow got into the cache directory.
Rather than stat'ing it first and then recursing, we will
simply assume it's a directory, recurse, and call XP_OpenDir
on it. If it's not a directory this will fail, and we will
ignore it.
*/
#ifdef XP_UNIX
/* Unix-specific speed hack: only recursively descend directories
if they have names which consist of exactly two hex digits.
Because of the behavior of xp_file.c on Unix, anything else is
either not a directory, or not a directory which we use.
*/
if (! (d_len == 2 &&
((d_name[0] >= '0' && d_name[0] <= '9') ||
(d_name[0] >= 'A' && d_name[0] <= 'F')) &&
((d_name[1] >= '0' && d_name[1] <= '9') ||
(d_name[1] >= 'A' && d_name[1] <= 'F'))))
continue;
#else /* !XP_UNIX */
/* This is redundant on Unix because of the above check. */
if(!PL_strcmp(d_name, ".") || !PL_strcmp(d_name, ".."))
{
/* ignore . and .. */
status = -1;
}
#endif /* !XP_UNIX */
else if(add_dir_prefix)
{
sprintf(buffer, "%.250s/%.250s", dir_prefix, d_name);
}
if(status != -1)
{
char *new_dir=0;
StrAllocCopy(new_dir, cur_dir);
if(cur_dir[PL_strlen(cur_dir)-1] != '/')
StrAllocCat(new_dir, "/");
StrAllocCat(new_dir, d_name);
net_cache_recursive_file_finder(hash_table,
prefix,
new_dir,
base_dir,
buffer);
PR_Free(new_dir);
}
continue;
}
if(add_dir_prefix)
{
sprintf(buffer, "%.250s/%.250s", dir_prefix, d_name);
XP_HashListAddObject(hash_table, PL_strdup(buffer));
}
else
{
XP_HashListAddObject(hash_table, PL_strdup(d_name));
}
}
PR_Free(dir_prefix);
XP_CloseDir(dir_ptr);
return 0;
}
/* cleans up the cache directory by listing every
* file and deleting the ones it doesn't know about
* that begin with the cacheing prefix
*/
PUBLIC int
NET_CleanupCacheDirectory(char * dir_name, const char * prefix)
{
#define REMOVE_LIST_SIZE 1000
DBT *remove_list[REMOVE_LIST_SIZE];
char *filename, *d_name;
XP_HashList *hash_table;
DBT key, data;
int status;
char buffer[512];
uint32 num_files;
int i;
int number_in_remove_list=0;
#ifdef XP_MAC
/* make sure the size of the cache is as we want it
*/
if(net_MaxDiskCacheSize == 0)
net_RemoveAllDiskCacheObjects();
else
net_ReduceDiskCacheTo(NULL, net_MaxDiskCacheSize, 0);
if(!dir_name)
dir_name = "";
/* optimization for cleanup.
*
* If the number of files on disk is within a couple
* of the number of files that we know about then
* skip the cache directory cleanup
*/
num_files = XP_FileNumberOfFilesInDirectory(dir_name, xpCache);
if(num_files-3 <= net_NumberInDiskCache)
return(0);
#endif
if(!cache_database)
return(-1);
TRACEMSG(("cleaning up cache directory of unknown cache files"));
hash_table = XP_HashListNew(1597, XP_StringHash, net_cache_strcmp);
if(!hash_table)
return(-1);
/* find all the files in the cache directory
*/
if(net_cache_recursive_file_finder(hash_table,
prefix,
dir_name,
dir_name,
buffer))
return(-1);
/* init the count of all the files in the database */
num_files = 0;
/* now run through the disk cache FAT and remove
* any matches from the hash table
*/
status = (*cache_database->seq)(cache_database, &key, &data, R_FIRST);
while(status == 0)
{
filename = net_GetFilenameInCacheDBT(&data);
if(filename)
{
time_t last_modified_date = net_GetTimeInCacheDBT(&data,
LAST_MODIFIED_BYTE_POSITION);
if(!last_modified_date)
{
/* if there is no last_modified date then
* it doesn't do us much good to keep the
* data since it can't be compared and used
* in a future session. Put the file in a remove list
*
* Don't do this if the check mode is NEVER
*
*/
if(NET_CacheUseMethod != CU_NEVER_CHECK
&& number_in_remove_list < REMOVE_LIST_SIZE)
remove_list[number_in_remove_list++] =
net_CacheDBTDup(&key);
}
else
{
num_files++; /* keep a count of all the files in the database */
d_name = (char*) XP_HashListRemoveObject(hash_table, filename);
if(d_name)
{
PR_Free(d_name);
}
}
PR_Free(filename);
}
else
{
/* could not grab the filename from
* the DBT. See if this is a valid
* entry
*/
if(!net_IsValidCacheDBT(&data)
&& (key.size < 4 || PL_strncmp((char*)key.data,
INTERNAL_NAME_PREFIX,
4))
&& number_in_remove_list < REMOVE_LIST_SIZE)
{
remove_list[number_in_remove_list++] = net_CacheDBTDup(&key);
}
}
status = (*cache_database->seq)(cache_database, &key, &data, R_NEXT);
}
/* any files left in the hash table are not in the
* cache FAT, so delete them
*
* @@@@ Manipulate the hash table directly
*/
{
int list_count;
XP_List *list_ptr;
for(list_count=0; list_count < hash_table->size; list_count++)
{
list_ptr = hash_table->list[list_count];
if(list_ptr)
{
while((filename =
(char *) XP_ListRemoveTopObject(list_ptr)) != 0)
{
#ifdef XP_OS2
XP_LazyFileRemove(filename, xpCache);
#else
XP_FileRemove(filename, xpCache);
#endif
TRACEMSG(("Unknown cache file %s found! -- deleteing...",
filename));
PR_Free(filename);
}
XP_ListDestroy(list_ptr); /* free the list */
}
}
XP_HashListDestroy(hash_table);
}
/* remove the entries that are no longer needed or
* are corrupt
*
* Note that we don't need to remove the files since
* they will already have been removed from the code
* above
*/
TRACEMSG(("Deleting %d unneeded cache entries",number_in_remove_list));
for(i=0; i < number_in_remove_list; i++)
{
DBT *newkey = remove_list[i];
if(-1 == (*cache_database->del)(cache_database, newkey, 0))
{
TRACEMSG(("Ack, error trying to delete corrupt entry, aborting",i)); break;
}
net_FreeCacheDBTdata(newkey);
}
/* if the number of files that we counted in the database
* does not agree with the number we think it should be
* reset the number we think it should be with the actual
* number
*/
if(net_NumberInDiskCache != num_files)
{
TRACEMSG(("Reseting number of files in cache to: %d", num_files));
net_NumberInDiskCache = num_files;
net_StoreDiskCacheSize(); /* this will store the number of files */
}
return(0);
}
/* removes all cache files from the cache directory
* and deletes the cache FAT
*/
PUBLIC int
NET_DestroyCacheDirectory(char * dir_name, char * prefix)
{
net_RemoveAllDiskCacheObjects();
NET_CleanupCacheDirectory(dir_name, prefix);
(*cache_database->close)(cache_database);
cache_database = 0;
net_DiskCacheSize=0;
net_NumberInDiskCache=0;
return XP_FileRemove("", xpCacheFAT);
}
/* create an HTML stream and push a bunch of HTML about
* the cache
*/
MODULE_PRIVATE void
NET_DisplayCacheInfoAsHTML(ActiveEntry * cur_entry)
{
char *buffer = (char*)PR_Malloc(2048);
char *address;
char *escaped;
NET_StreamClass * stream;
net_CacheObject * cache_obj;
DBT key, data;
XP_Bool long_form = FALSE;
int i;
if(!buffer)
{
cur_entry->status = MK_UNABLE_TO_CONVERT;
return;
}
if(PL_strcasestr(cur_entry->URL_s->address, "?long"))
long_form = TRUE;
else if(PL_strcasestr(cur_entry->URL_s->address, "?traceon"))
NET_CacheTraceOn = TRUE;
else if(PL_strcasestr(cur_entry->URL_s->address, "?traceoff"))
NET_CacheTraceOn = FALSE;
StrAllocCopy(cur_entry->URL_s->content_type, TEXT_HTML);
cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);
stream = NET_StreamBuilder(cur_entry->format_out,
cur_entry->URL_s,
cur_entry->window_id);
if(!stream)
{
cur_entry->status = MK_UNABLE_TO_CONVERT;
PR_Free(buffer);
return;
}
/* define a macro to push a string up the stream
* and handle errors
*/
#define PUT_PART(part) \
cur_entry->status = (*stream->put_block)(stream, \
part ? part : "Unknown", \
part ? PL_strlen(part) : 7); \
if(cur_entry->status < 0) \
goto END;
sprintf(buffer,
"<TITLE>Information about the Netscape disk cache</TITLE>\n"
"<h2>Disk Cache statistics</h2>\n"
"<TABLE>\n"
"<TR>\n"
"<TD ALIGN=RIGHT><b>Maximum size:</TD>\n"
"<TD>%ld</TD>\n"
"</TR>\n"
"<TR>\n"
"<TD ALIGN=RIGHT><b>Current size:</TD>\n"
"<TD>%ld</TD>\n"
"</TR>\n"
"<TR>\n"
"<TD ALIGN=RIGHT><b>Number of files in cache:</TD>\n"
"<TD>%ld</TD>\n"
"</TR>\n"
"<TR>\n"
"<TD ALIGN=RIGHT><b>Average cache file size:</TD>\n"
"<TD>%ld</TD>\n"
"</TR>\n"
"</TABLE>\n"
"<HR>",
net_MaxDiskCacheSize,
net_DiskCacheSize,
net_NumberInDiskCache,
net_NumberInDiskCache ? net_DiskCacheSize/net_NumberInDiskCache : 0);
PUT_PART(buffer);
if(!cache_database)
{
PL_strcpy(buffer, "The cache database is currently closed");
PUT_PART(buffer);
goto END;
}
if(0 != (*cache_database->seq)(cache_database, &key, &data, R_FIRST))
return;
/* define some macros to help us output HTML tables
*/
#if 0
#define TABLE_TOP(arg1) \
sprintf(buffer, \
"<TR><TD ALIGN=RIGHT><b>%s</TD>\n" \
"<TD>", arg1); \
PUT_PART(buffer);
#define TABLE_BOTTOM \
sprintf(buffer, \
"</TD></TR>"); \
PUT_PART(buffer);
#else
#define TABLE_TOP(arg1) \
PL_strcpy(buffer, "<tt>"); \
for(i=PL_strlen(arg1); i < 16; i++) \
PL_strcat(buffer, "&nbsp;"); \
PL_strcat(buffer, arg1); \
PL_strcat(buffer, " </tt>"); \
PUT_PART(buffer);
#define TABLE_BOTTOM \
PL_strcpy(buffer, "<BR>\n"); \
PUT_PART(buffer);
#endif
do
{
cache_obj = net_Fast_DBDataToCacheStruct(&data);
if(!cache_obj)
{
if(!net_IsValidCacheDBT(&data)
&& (key.size < 4 || PL_strncmp((char*)key.data,
INTERNAL_NAME_PREFIX,
4)))
{
PL_strcpy(buffer, "<H3>Corrupted or misversioned cache entry</H3>"
"This entry will be deleted automatically "
"in the future."
"<HR ALIGN=LEFT WIDTH=50%>");
PUT_PART(buffer);
}
}
else
{
#if 0
/* begin a table for this entry */
PL_strcpy(buffer, "<TABLE>");
PUT_PART(buffer);
#endif
/* put the URL out there */
/* the URL is 8 bytes into the key struct
*/
address = (char *)key.data+8;
TABLE_TOP("URL:");
PL_strcpy(buffer, "<A TARGET=Internal_URL_Info HREF=about:");
PUT_PART(buffer);
PUT_PART(address);
PL_strcpy(buffer, ">");
PUT_PART(buffer);
escaped = NET_EscapeHTML(address);
PUT_PART(escaped);
PR_Free(escaped);
PL_strcpy(buffer, "</A>");
PUT_PART(buffer);
TABLE_BOTTOM;
TABLE_TOP( XP_GetString(MK_CACHE_CONTENT_LENGTH) );
sprintf(buffer, "%lu", cache_obj->content_length);
PUT_PART(buffer);
TABLE_BOTTOM;
if(cache_obj->content_length != cache_obj->real_content_length)
{
TABLE_TOP( XP_GetString(MK_CACHE_REAL_CONTENT_LENGTH) );
sprintf(buffer, "%lu", cache_obj->real_content_length);
PUT_PART(buffer);
TABLE_BOTTOM;
}
TABLE_TOP( XP_GetString(MK_CACHE_CONTENT_TYPE) );
PUT_PART(cache_obj->content_type);
TABLE_BOTTOM;
TABLE_TOP( XP_GetString(MK_CACHE_LOCAL_FILENAME) );
PUT_PART(cache_obj->filename);
TABLE_BOTTOM;
TABLE_TOP( XP_GetString(MK_CACHE_LAST_MODIFIED) );
if(cache_obj->last_modified)
{
PUT_PART(ctime(&cache_obj->last_modified));
}
else
{
PL_strcpy(buffer, "No date sent");
PUT_PART(buffer);
}
TABLE_BOTTOM;
TABLE_TOP( XP_GetString(MK_CACHE_EXPIRES) );
if(cache_obj->expires)
{
PUT_PART(ctime(&cache_obj->expires));
}
else
{
PL_strcpy(buffer, "No expiration date sent");
PUT_PART(buffer);
}
TABLE_BOTTOM;
if(long_form)
{
TABLE_TOP( XP_GetString(MK_CACHE_LAST_ACCESSED) );
PUT_PART(ctime(&cache_obj->last_accessed));
TABLE_BOTTOM;
TABLE_TOP( XP_GetString(MK_CACHE_CHARSET) );
if(cache_obj->charset)
{
PUT_PART(cache_obj->charset);
}
else
{
PL_strcpy(buffer, "iso-8859-1 (default)");
PUT_PART(buffer);
}
TABLE_BOTTOM;
TABLE_TOP( XP_GetString(MK_CACHE_SECURE) );
sprintf(buffer, "%s", cache_obj->security_on ?
"TRUE" : "FALSE");
PUT_PART(buffer);
TABLE_BOTTOM;
TABLE_TOP( XP_GetString(MK_CACHE_USES_RELATIVE_PATH) );
sprintf(buffer, "%s", cache_obj->is_relative_path ?
"TRUE" : "FALSE");
PUT_PART(buffer);
TABLE_BOTTOM;
TABLE_TOP( XP_GetString(MK_CACHE_FROM_NETSITE_SERVER) );
sprintf(buffer, "%s", cache_obj->is_netsite ?
"TRUE" : "FALSE");
PUT_PART(buffer);
TABLE_BOTTOM;
TABLE_TOP( "Lock date:" );
if(cache_obj->lock_date)
{
PUT_PART(ctime(&cache_obj->lock_date));
}
else
{
PL_strcpy(buffer, "Not locked");
PUT_PART(buffer);
}
TABLE_BOTTOM;
}
#if 0
/* end the table for this entry */
PL_strcpy(buffer, "</TABLE><HR ALIGN=LEFT WIDTH=50%>");
PUT_PART(buffer);
#else
PL_strcpy(buffer, "\n<P>\n");
PUT_PART(buffer);
#endif
}
}
while(0 == (*cache_database->seq)(cache_database, &key, &data, R_NEXT));
END:
PR_Free(buffer);
if(cur_entry->status < 0)
(*stream->abort)(stream, cur_entry->status);
else
(*stream->complete)(stream);
return;
}
#define SANE_BUFLEN 1024
#include "libmocha.h"
NET_StreamClass *
NET_CloneWysiwygCacheFile(MWContext *window_id, URL_Struct *URL_s,
uint32 nbytes, const char * wysiwyg_url,
const char * base_href)
{
char *filename;
PRCList *link;
CacheDataObject *data_object;
NET_StreamClass *stream;
XP_File fromfp;
int32 buflen, len;
char *buf;
int url_type = NET_URL_Type(URL_s->address);
XP_FileType mode = xpCache;
if (url_type == MAILBOX_TYPE_URL ||
url_type == IMAP_TYPE_URL ||
url_type == POP3_TYPE_URL)
return NULL;
filename = URL_s->cache_file;
if (!filename)
{
/* not hitting the cache -- check whether we're filling it */
for (link = active_cache_data_objects.next;
link != &active_cache_data_objects;
link = link->next)
{
data_object = (CacheDataObject *)link;
if (data_object->URL_s == URL_s)
{
XP_FileFlush(data_object->fp);
filename = data_object->cache_object->filename;
goto found;
}
}
/* try memory cache and local filesystem */
stream = net_CloneWysiwygMemCacheEntry(window_id, URL_s, nbytes,
wysiwyg_url, base_href);
if (stream)
return stream;
return net_CloneWysiwygLocalFile(window_id, URL_s, nbytes,
wysiwyg_url, base_href);
}
else
{
/*
* If this is a local file URL_s->cache_file will have been
* set by the code in mkfile.c so that the stream is treated
* like a disk-cache stream. But, in this case, we care that
* they are different and need to preserve the fact that its
* a local file URL
*/
if(NET_IsLocalFileURL(URL_s->address))
mode = xpURL;
}
found:
fromfp = XP_FileOpen(filename, mode, XP_FILE_READ_BIN);
if (!fromfp)
return NULL;
stream = LM_WysiwygCacheConverter(window_id, URL_s, wysiwyg_url,
base_href);
if (!stream)
{
XP_FileClose(fromfp);
return 0;
}
buflen = stream->is_write_ready(stream);
if (buflen > SANE_BUFLEN)
buflen = SANE_BUFLEN;
buf = (char *)PR_Malloc(buflen * sizeof(char));
if (!buf)
{
XP_FileClose(fromfp);
return 0;
}
while (nbytes != 0)
{
len = buflen;
if ((uint32)len > nbytes)
len = (int32)nbytes;
len = XP_FileRead(buf, len, fromfp);
if (len <= 0)
break;
if (stream->put_block(stream, buf, len) < 0)
break;
nbytes -= len;
}
PR_Free(buf);
XP_FileClose(fromfp);
if (nbytes != 0)
{
/* NB: Our caller must clear top_state->mocha_write_stream. */
stream->abort(stream, MK_UNABLE_TO_CONVERT);
PR_Free(stream);
return 0;
}
return stream;
}
#endif /* MOZILLA_CLIENT */