home *** CD-ROM | disk | FTP | other *** search
- /* -*- 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.
- */
- /* Module to do memory caching; storage and retreival
- * very simple hash table based scheme.
- *
- * designed and originally implemented by Lou Montulli
- * modifications/additions by Gagan Saksena
- */
-
- /* Please leave outside of ifdef for windows precompiled headers */
- #include "mkutils.h"
-
- #ifdef MOZILLA_CLIENT
-
- #include "mkcache.h"
- #include "glhist.h"
- #include "xp_hash.h"
- #include "xp_mcom.h"
- #include "client.h"
- #include "mkgeturl.h"
- #include "mkstream.h"
- #include "extcache.h"
- #include "mkmemcac.h"
- #include "cert.h" /* for CERT_DupCertificate() */
- #include "libimg.h" /* Image Lib public API. */
- #include "prclist.h"
- #include "shist.h"
-
- /* exported error ints */
- extern int MK_OUT_OF_MEMORY;
-
- /* the size of the cache segment output size
- */
- #define NET_MEM_CACHE_OUTPUT_SIZE 2048
-
- /* MAX_MEMORY_ALLOC_SIZE is the size of the largest malloc
- * used in this module
- *
- * the NET_Socket_Buffer can never be larger than the
- * MAX_MEMORY_ALLOC_SIZE or this will fail
- */
- #ifdef XP_WIN16
- #define MAX_MEMORY_ALLOC_SIZE 32767 /* MAXINT */
- #elif defined(XP_MAC)
- #define MAX_MEMORY_ALLOC_SIZE 12*1024
- #else
- #define MAX_MEMORY_ALLOC_SIZE (((unsigned) (~0) << 1) >> 1) /* MAXINT */
-
- /* @@@@@@@@@ there is currently a limit on XP_ALLOC to less than 32 K */
- #undef MAX_MEMORY_ALLOC_SIZE
- #define MAX_MEMORY_ALLOC_SIZE 32767 /* MAXINT */
- #endif
-
- /* this is the minimum size of each of the memory segments used to hold
- * the cache object in memory
- */
- #ifdef XP_MAC
- #define MEMORY_CACHE_SEGMENT_SIZE 12*1024
- #else
- #define MEMORY_CACHE_SEGMENT_SIZE 2048
- #endif
-
- /* !!! structure is typedef'd to net_MemoryCacheObject
- * in net.h
- */
- struct _net_MemoryCacheObject {
- XP_List *list;
- net_CacheObject cache_obj;
- int external_locks; /* locks set by other modules calling in */
- int mem_read_lock; /* the number of current readers */
- Bool delete_me; /* set this to delete the object
- * once all readers are done
- */
- Bool current_page_only; /* this is set when the document
- * requested not to be cached.
- * When set the document should
- * only be served if it's a
- * resize or print
- */
- Bool completed; /* whether or not we have completed the
- * caching of this object. Initialized to
- * false, then set to true once the
- * complete function is called.
- */
- Bool aborted; /* whether or not we have aborted the
- * caching of this object.
- */
- };
-
- /* structure to hold memory
- * segments
- */
- typedef struct _net_MemorySegment {
- char *segment;
- uint32 seg_size;
- uint32 in_use;
- } net_MemorySegment;
-
- /* the hash table pointer that holds all the memory cache
- * objects for quick lookup
- */
- PRIVATE XP_HashList * net_MemoryCacheHashList = 0;
-
- /* semaphore counter set when calling any of the list add functions
- */
- PRIVATE int net_cache_adding_object=0;
-
- /* a list of all the documents currently in
- * memory
- *
- * The net_MemoryCacheList is used as a delete queue
- * and is ordered by last-accessed time.
- */
- PRIVATE XP_List *net_MemoryCacheList=0;
- PRIVATE uint32 net_MemoryCacheSize=0;
- PRIVATE uint32 net_MaxMemoryCacheSize=0;
-
- /* this object is used by the MemCacheConverter.
- * MemCacheConverter is a standard netlib stream and uses
- * this structure to hold data between invokations of
- * the write function and complete or abort.
- */
- typedef struct _CacheDataObject {
- PRCList links;
- NET_StreamClass *next_stream;
- net_MemoryCacheObject *memory_copy;
- URL_Struct *URL_s;
- uint32 computed_content_length;
- } CacheDataObject;
-
- /* PRIVATE XXX Mac CodeWarrior bug */ PRCList mem_active_cache_data_objects
- = PR_INIT_STATIC_CLIST(&mem_active_cache_data_objects);
-
- /* free a segmented memory copy of an object
- * and the included net_cacheObject struct
- */
- PRIVATE void
- net_FreeMemoryCopy(net_MemoryCacheObject * mem_copy)
- {
- net_MemorySegment * mem_seg;
-
- XP_ASSERT(mem_copy);
-
- if(!mem_copy)
- return;
-
- /* If the stream was aborted while being written, and nobody is
- * reading it, then it's ok to delete it now. That is,
- * we don't need to check if we want to set delete_me
- */
- if (!((mem_copy->mem_read_lock == 0) && mem_copy->aborted))
- {
- /* if this object is currently being read or written DO NOT delete it.
- * It will be deleted by another call to this function
- * after it is finished being read.
- */
- if (((mem_copy->mem_read_lock > 0) && mem_copy->completed) ||
- !mem_copy->completed)
- {
- TRACEMSG(("Seting delete_me flag on memory cache object"));
- mem_copy->delete_me = TRUE;
-
- /* remove it from the hash list so it won't be found
- * by NET_GetURLInMemCache
- */
- XP_HashListRemoveObject(net_MemoryCacheHashList, mem_copy);
- return;
- }
- }
-
- TRACEMSG(("Freeing memory cache copy"));
-
- /* free and delete the memory segment list
- */
- while((mem_seg = (net_MemorySegment *)XP_ListRemoveTopObject(mem_copy->list)) != NULL)
- {
- /* reduce the global memory cache size
- */
- net_MemoryCacheSize -= mem_seg->seg_size;
- FREE(mem_seg->segment);
- FREE(mem_seg);
- }
-
- XP_ListDestroy(mem_copy->list);
-
- /* Remove it from the hash and delete lists,
- * If the object isn't in these lists anymore
- * the call will be ignored.
- */
- XP_HashListRemoveObject(net_MemoryCacheHashList, mem_copy);
- XP_ListRemoveObject(net_MemoryCacheList, mem_copy);
-
- FREEIF(mem_copy->cache_obj.address);
- FREEIF(mem_copy->cache_obj.post_data);
- FREEIF(mem_copy->cache_obj.post_headers);
- FREEIF(mem_copy->cache_obj.content_type);
- FREEIF(mem_copy->cache_obj.charset);
- FREEIF(mem_copy->cache_obj.content_encoding);
-
- FREE(mem_copy);
-
- }
-
- /* removes the last mem object. Returns negative on error,
- * otherwise returns the current size of the cache
- */
- PRIVATE int32
- net_remove_last_memory_cache_object(void)
- {
- net_MemoryCacheObject * mem_cache_obj;
-
- /* safty valve in case there are no list items
- * or if we are in the process of adding an object
- */
- if(net_cache_adding_object)
- return -1;
-
- mem_cache_obj = (net_MemoryCacheObject *)
- XP_ListRemoveEndObject(net_MemoryCacheList);
-
- if(!mem_cache_obj)
- return -1;
-
- /* we can't remove it if we have external locks or if it's not completed -
- * try removing another one
- */
- if(mem_cache_obj->external_locks || !mem_cache_obj->completed)
- {
- int status = net_remove_last_memory_cache_object(); /* recurse */
- /* add the object back in */
- XP_ListAddObject(net_MemoryCacheList, mem_cache_obj);
- return status;
- }
-
- net_FreeMemoryCopy(mem_cache_obj);
-
- return((int32) net_MemoryCacheSize);
- }
-
- /* this function free's objects to bring the cache size
- * below the size passed in
- */
- PRIVATE void
- net_ReduceMemoryCacheTo(uint32 size)
- {
- /* safty valve in case we are in the process of adding an object
- */
- if(net_cache_adding_object)
- return;
-
- while(net_MemoryCacheSize > size)
- {
- if(0 > net_remove_last_memory_cache_object())
- break;
- }
- }
-
- /* set the size of the Memory cache.
- * Set it to 0 if you want cacheing turned off
- * completely
- */
- #define HTML_CACHE_MAX_BASE_SIZE 1024l*1024l
- #define HTML_CACHE_VARIABLE_GROWTH .05 /* the percentage to add */
- PUBLIC void
- NET_SetMemoryCacheSize(int32 new_size)
- {
- int32 image_cache_size;
- int32 html_cache_size;
-
- if(new_size <= 0)
- {
- IL_SetCacheSize(0);
- net_ReduceMemoryCacheTo(0);
- net_MaxMemoryCacheSize = 0;
- return;
- }
-
- /* the netlib mem cache is set to .3*new_size
- * or 200K + .05 * new_size, whichever is less.
- * and imagelb uses the rest
- */
- if(new_size * .3 < HTML_CACHE_MAX_BASE_SIZE)
- {
- /* use .3 of new_size */
- html_cache_size = (int32) (.3 * new_size);
- }
- else
- {
- /* use HTML_CACHE_MAX_BASE_SIZE + .05*new_size */
- html_cache_size = HTML_CACHE_MAX_BASE_SIZE +
- (int32)(HTML_CACHE_VARIABLE_GROWTH*
- (new_size - HTML_CACHE_MAX_BASE_SIZE));
- }
-
- image_cache_size = new_size - html_cache_size;
-
- net_MaxMemoryCacheSize = html_cache_size;
-
- net_ReduceMemoryCacheTo((uint32) html_cache_size);
-
- /* set the image cache to be the rest
- */
- IL_SetCacheSize(image_cache_size);
-
- return;
- }
-
- /* 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()
- {
- net_remove_last_memory_cache_object();
-
- return(net_MemoryCacheSize);
- }
-
- /* returns the number of bytes currently in use by the Memory cache
- */
- PUBLIC int32
- NET_GetMemoryCacheSize()
- {
- return(net_MemoryCacheSize);
- }
-
- PUBLIC int32
- NET_GetMaxMemoryCacheSize()
- {
- return net_MaxMemoryCacheSize;
- }
-
- /* compare entries for the hashing functions
- *
- * return 0 if match or positive or negative on
- * non match
- */
- PRIVATE int net_CacheHashComp(net_MemoryCacheObject * obj1,
- net_MemoryCacheObject * obj2)
- {
- int result;
- char *hash1;
- char *hash2;
- char *ques1=0;
- char *ques2=0;
- char *ques3=0;
- char *ques4=0;
-
- if(obj1->cache_obj.method != obj2->cache_obj.method)
- return(obj1->cache_obj.method - obj2->cache_obj.method);
-
- /* If this is a "news:" or "snews:" URL, then any search data in the URL
- * (the stuff after '?') should be ignored for cache-comparison purposes,
- * so that "news:MSGID" and "news:MSGID?headers=all" share the same cache
- * file.
- */
- if((XP_TO_UPPER(obj1->cache_obj.address[0]) == 'N'
- || XP_TO_UPPER(obj1->cache_obj.address[0]) == 'S')
- && NET_URL_Type(obj1->cache_obj.address) == NEWS_TYPE_URL)
- {
- ques1 = XP_STRCHR(obj1->cache_obj.address, '?');
- if(ques1)
- *ques1 = '\0';
- }
-
- if((XP_TO_UPPER(obj2->cache_obj.address[0]) == 'N'
- || XP_TO_UPPER(obj2->cache_obj.address[0]) == 'S')
- && NET_URL_Type(obj2->cache_obj.address) == NEWS_TYPE_URL)
- {
- ques2 = XP_STRCHR(obj2->cache_obj.address, '?');
- if(ques2)
- *ques2 = '\0';
- }
-
-
- /* do the same for IMAP */
- if(!strncasecomp(obj1->cache_obj.address,"mailbox://",10))
- {
- ques3 = XP_STRSTR(obj1->cache_obj.address, "&part=");
- if(ques3)
- *ques3 = '\0';
- }
-
- if(!strncasecomp(obj2->cache_obj.address,"mailbox://",10))
- {
- ques4 = XP_STRSTR(obj2->cache_obj.address, "&part=");
- if(ques4)
- *ques4 = '\0';
- }
-
-
- /* strip hash symbols because they
- * really represent the same
- * document
- */
- hash1 = XP_STRCHR(obj1->cache_obj.address, '#');
- hash2 = XP_STRCHR(obj2->cache_obj.address, '#');
-
- if(hash1)
- *hash1 = '\0';
- if(hash2)
- *hash2 = '\0';
-
- result = XP_STRCMP(obj1->cache_obj.address, obj2->cache_obj.address);
-
- /* set them back to previous values
- */
- if(hash1)
- *hash1 = '#';
- if(hash2)
- *hash2 = '#';
- if(ques1)
- *ques1 = '?';
- if(ques2)
- *ques2 = '?';
- if(ques3)
- *ques3 = '&';
- if(ques4)
- *ques4 = '&';
-
-
- if(result != 0)
- return(result);
-
- if(!obj1->cache_obj.post_data && !obj2->cache_obj.post_data)
- return(0); /* match; no post data on either */
-
- if(obj1->cache_obj.post_data && obj2->cache_obj.post_data)
- {
- result = XP_STRCMP(obj1->cache_obj.post_data,
- obj2->cache_obj.post_data);
- return(result);
- }
- else
- { /* one but not the other */
- if(obj1->cache_obj.post_data)
- return(1);
- else
- return(-1);
- }
-
- return(0); /* match with post data */
- }
-
- /* hashing function for url's
- *
- * This is some magic Jamie gave me...
- */
- PRIVATE uint32 net_CacheHashFunc(net_MemoryCacheObject * obj1)
- {
- unsigned const char *x;
- uint32 h = 0;
- uint32 g;
- XP_Bool news_type_url = FALSE;
- XP_Bool imap_type_url = FALSE;
-
- if(!obj1)
- return 0;
-
- x = (unsigned const char *) obj1->cache_obj.address;
-
- /* figure out if it's a news type URL */
- if((XP_TO_UPPER(obj1->cache_obj.address[0]) == 'N'
- || XP_TO_UPPER(obj1->cache_obj.address[0]) == 'S')
- && NET_URL_Type(obj1->cache_obj.address) == NEWS_TYPE_URL)
- news_type_url = TRUE;
- /* figure out if it's an IMAP type URL */
- else if (XP_TO_UPPER(obj1->cache_obj.address[0]) == 'M' &&
- !strncasecomp(obj1->cache_obj.address,"Mailbox://",10))
- imap_type_url = TRUE;
-
- /* modify the default String hash function
- * to work with URL's
- */
- assert(x);
-
- if (!x) return 0;
-
- /* ignore '#' data for all URL's.
- * ignore '?' data for news URL's
- * ignore '&' data for all IMAP URL's
- */
- while (*x != 0 && *x != '#' && (!news_type_url || *x != '?') &&
- (!imap_type_url || *x != '&'))
- {
- h = (h << 4) + *x++;
- if ((g = h & 0xf0000000) != 0)
- h = (h ^ (g >> 24)) ^ g;
- }
-
- return h;
- }
-
- /******************************************************************
- * Cache Converter Stream input routines
- */
-
- /* is the stream ready for writing?
- */
- PRIVATE unsigned int net_MemCacheWriteReady (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
- *
- * this function accepts a stream and writes the data
- * into memory segments on a segment list.
- */
- PRIVATE int net_MemCacheWrite (NET_StreamClass *stream, CONST char* buffer, int32 len)
- {
- CacheDataObject *obj=stream->data_object;
- TRACEMSG(("net_MemCacheWrite called with %ld buffer size", len));
-
- /* if delete_me is set, we don't want to keep writing to the memcache stream.
- */
- if(obj->memory_copy && !obj->memory_copy->delete_me)
- {
- net_MemorySegment * mem_seg;
- char * cur_mem_seg_ptr;
-
- /* compute a content_length as we read data
- * to use as a comparison and for URL's that
- * don't send a content type
- */
- obj->computed_content_length += len;
-
- /* we are always adding to the end one in the list so
- * lets get it
- */
- mem_seg = (net_MemorySegment *)
- XP_ListGetEndObject(obj->memory_copy->list);
- XP_ASSERT(mem_seg);
-
- if(mem_seg->in_use+len > mem_seg->seg_size)
- {
- /* we need a new segment
- */
- net_MemorySegment * new_mem_seg = XP_NEW(net_MemorySegment);
- uint32 size_left_in_old_buffer = mem_seg->seg_size - mem_seg->in_use;
- uint32 size_for_new_buffer = len - size_left_in_old_buffer;
- char * new_mem_seg_ptr;
-
- if(!new_mem_seg)
- {
- net_FreeMemoryCopy(obj->memory_copy);
- obj->memory_copy = 0;
- goto EndOfMemWrite;
- }
-
- /* @@@ the socket buffer can never be larger
- * than the MAX_MEMORY_ALLOC_SIZE
- */
- if(size_for_new_buffer > MEMORY_CACHE_SEGMENT_SIZE)
- {
- new_mem_seg->segment = (char*)XP_ALLOC(size_for_new_buffer);
- new_mem_seg->seg_size = size_for_new_buffer;
- }
- else
- {
- new_mem_seg->segment = (char*)XP_ALLOC(
- MEMORY_CACHE_SEGMENT_SIZE);
- new_mem_seg->seg_size = MEMORY_CACHE_SEGMENT_SIZE;
- }
-
- if(!new_mem_seg->segment)
- {
- FREE(new_mem_seg);
- net_FreeMemoryCopy(obj->memory_copy);
- obj->memory_copy = 0;
- goto EndOfMemWrite;
- }
-
- /* increase the global cache size counter
- */
- net_MemoryCacheSize += new_mem_seg->seg_size;
-
- TRACEMSG(("Cache size now: %d", net_MemoryCacheSize));
-
- cur_mem_seg_ptr = mem_seg->segment;
- new_mem_seg_ptr = new_mem_seg->segment;
-
- /* fill in what we can to the old buffer
- */
- if(size_left_in_old_buffer)
- {
- XP_MEMCPY(cur_mem_seg_ptr+mem_seg->in_use,
- buffer,
- (size_t) size_left_in_old_buffer);
- mem_seg->in_use = mem_seg->seg_size; /* seg new size */
- }
-
- /* fill in the new buffer
- */
- XP_MEMCPY(new_mem_seg_ptr,
- buffer+size_left_in_old_buffer,
- (size_t) size_for_new_buffer);
- new_mem_seg->in_use = size_for_new_buffer;
-
- net_cache_adding_object++; /* semaphore */
- XP_ListAddObjectToEnd(obj->memory_copy->list, new_mem_seg);
- net_cache_adding_object--; /* semaphore */
-
- TRACEMSG(("Adding %d to New memory segment %p", len, new_mem_seg));
- }
- else
- {
- assert (mem_seg->segment);
- cur_mem_seg_ptr = mem_seg->segment;
-
- /* fill in some more into the existing segment
- */
- XP_MEMCPY(cur_mem_seg_ptr + mem_seg->in_use, buffer, (size_t) len);
- mem_seg->in_use += len;
-
- TRACEMSG(("Adding %d to existing memory segment %p", len, mem_seg));
- }
-
- }
-
- EndOfMemWrite: /* target of a goto from an error above */
-
- if(obj->next_stream)
- {
- int status=0;
-
- XP_ASSERT (buffer);
- XP_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_MemCacheComplete (NET_StreamClass *stream)
- {
- CacheDataObject *obj=stream->data_object;
- /* refresh these entries in case meta changes them */
- if(obj->URL_s && obj->memory_copy)
- {
- obj->memory_copy->cache_obj.expires = obj->URL_s->expires;
- obj->memory_copy->cache_obj.last_modified = obj->URL_s->last_modified;
- StrAllocCopy(obj->memory_copy->cache_obj.charset, obj->URL_s->charset);
- }
-
- if (obj->memory_copy)
- {
- /* now it's completed */
- obj->memory_copy->completed = TRUE;
-
- /* if the object is zero size or
- * if the computed size is different that a given content type,
- * zero the last modified date so that it wont get reused
- * on a recheck or reload
- */
- if(!obj->computed_content_length
- || (obj->memory_copy->cache_obj.content_length
- && obj->memory_copy->cache_obj.content_length
- != obj->computed_content_length
- )
- )
- {
- obj->memory_copy->cache_obj.last_modified = 0;
- }
-
- /* override the given content_length with the correct
- * content length
- */
- obj->memory_copy->cache_obj.content_length =
- obj->computed_content_length;
-
- /* set current time */
- obj->memory_copy->cache_obj.last_accessed = time(NULL);
- }
-
-
- /* loser: */
- /* complete the next stream
- */
- if(obj->next_stream)
- {
- (*obj->next_stream->complete)(obj->next_stream);
- FREE(obj->next_stream);
- }
-
- /* finally, check to see if we want to delete this, after going
- through all that work just to put it in here. */
- if(obj->memory_copy &&
- obj->memory_copy->delete_me &&
- (obj->memory_copy->mem_read_lock == 0))
- {
- net_FreeMemoryCopy(obj->memory_copy);
- }
-
-
- /* don't need the data obj any more since this is the end
- * of the stream... */
- PR_REMOVE_LINK(&obj->links);
- FREE(obj);
-
- /* this is what keeps the cache from growing without end */
- net_ReduceMemoryCacheTo(net_MaxMemoryCacheSize);
-
- return;
- }
-
- /* add the object into the cache, even if it hasn't been finished
- getting cached. When it's finished, net_MemCacheComplete will
- set the obj->memory_copy->comlpleted flag.
- */
- PRIVATE void net_MemCacheAddObjectToCache (CacheDataObject * obj)
- {
- int status;
-
- /* if the hash table doesn't exist yet, initialize it now
- */
- if(!net_MemoryCacheHashList)
- {
-
- net_MemoryCacheHashList = XP_HashListNew(499,
- (XP_HashingFunction) net_CacheHashFunc,
- (XP_HashCompFunction) net_CacheHashComp);
- if(!net_MemoryCacheHashList)
- {
- net_FreeMemoryCopy(obj->memory_copy);
- goto loser;
- return;
- }
-
- }
-
- /* if the delete list doesn't exist yet, initialize it now
- */
- if(!net_MemoryCacheList)
- {
- net_MemoryCacheList = XP_ListNew();
- if(!net_MemoryCacheList)
- {
- net_FreeMemoryCopy(obj->memory_copy);
- goto loser;
- return;
- }
- }
-
- /* memory copy could have been free'd and clear if an
- * error occured in the write, so check to make sure
- * it's still around.
- */
- if(obj->memory_copy)
- {
- /* set the completed flag so that we know we're in
- * the process of caching this object.
- */
- obj->memory_copy->completed = FALSE;
- obj->memory_copy->aborted = FALSE;
-
- /* if the document requested not to be cached,
- * set the flag to only serve it for reloads and such
- */
- if(obj->URL_s && obj->URL_s->dont_cache)
- obj->memory_copy->current_page_only = TRUE;
-
- /* add the struct to the delete list */
- net_cache_adding_object++; /* semaphore */
- XP_ListAddObject(net_MemoryCacheList, obj->memory_copy);
- net_cache_adding_object--; /* semaphore */
-
- /* add the struct to the hash list */
- net_cache_adding_object++; /* semaphore */
- status = XP_HashListAddObject(net_MemoryCacheHashList,
- obj->memory_copy);
- net_cache_adding_object--; /* semaphore */
-
- /* check for hash collision */
- if(status == XP_HASH_DUPLICATE_OBJECT)
- {
- net_MemoryCacheObject * tmp_obj;
-
- tmp_obj = (net_MemoryCacheObject *)
- XP_HashListFindObject(net_MemoryCacheHashList,
- obj->memory_copy);
-
- if ((tmp_obj->mem_read_lock == 0) && tmp_obj->completed)
- {
- /* If there is nobody currently reading or writing the
- * duplicate hash entry, then free the old copy.
- * This will remove it from the hash list, also.
- */
- net_FreeMemoryCopy(tmp_obj);
-
- TRACEMSG(("Found duplicate object in cache. Removing old object"));
-
- /* re add the object
- */
- net_cache_adding_object++; /* semaphore */
- XP_HashListAddObject(net_MemoryCacheHashList,
- obj->memory_copy);
- net_cache_adding_object--; /* semaphore */
- }
- else
- {
- /* We can't cache this entry, the duplicate is
- * currently being read or written. So delete this entry.
- */
- net_FreeMemoryCopy(obj->memory_copy);
- }
- }
- }
-
- loser:
-
- return;
- }
-
- /* Abort the stream
- *
- */
- PRIVATE void net_MemCacheAbort (NET_StreamClass *stream, int status)
- {
- CacheDataObject *obj=stream->data_object;
- /* abort the next stream
- */
- if(obj->next_stream)
- {
- (*obj->next_stream->abort)(obj->next_stream, status);
- FREE(obj->next_stream);
- }
-
- /* set the aborted flag so that the object will really be deleted
- * if there are no read locks. */
- obj->memory_copy->aborted = TRUE;
- net_FreeMemoryCopy(obj->memory_copy);
-
- PR_REMOVE_LINK(&obj->links);
- FREE(obj);
-
- return;
- }
-
- /* setup the stream
- */
- MODULE_PRIVATE NET_StreamClass *
- NET_MemCacheConverter (FO_Present_Types format_out,
- void *converter_obj,
- URL_Struct *URL_s,
- MWContext *window_id)
- {
- CacheDataObject * data_object=0;
- NET_StreamClass * stream=0;
- net_MemorySegment * mem_seg=0;
- net_MemoryCacheObject *memory_copy=0;
- net_MemCacheConverterObject *mem_conv_obj = converter_obj;
-
- TRACEMSG(("Setting up cache stream. Have URL: %s\n", URL_s->address));
-
- TRACEMSG(("Setting up memory cache"));
-
- /* check to see if the object is larger than the
- * size of the max memory cache
- *
- * skip the size check if the must_cache flag is set.
- */
- if(!URL_s->must_cache && (uint32)URL_s->content_length >= net_MaxMemoryCacheSize)
- goto malloc_failure; /* dont cache this URL */
-
- /* set up the memory caching struct.
- *
- * malloc the segment first since it's the most likely malloc to fail
- */
- mem_seg = XP_NEW(net_MemorySegment);
-
- if(!mem_seg)
- return(NULL);
-
- mem_seg->in_use = 0;
-
- if(URL_s->content_length > 0)
- {
- /* use the content_length if we can since that would be
- * the most efficient
- */
- if(URL_s->content_length > MAX_MEMORY_ALLOC_SIZE)
- {
- mem_seg->segment = (char*)XP_ALLOC(MAX_MEMORY_ALLOC_SIZE);
- mem_seg->seg_size = MAX_MEMORY_ALLOC_SIZE;
- }
- else
- {
- /* add 10 just in case it's needed due to a bad content type :) */
- mem_seg->segment = (char*)XP_ALLOC(URL_s->content_length+10);
- mem_seg->seg_size = URL_s->content_length+10;
- }
- }
- else
- {
- /* this is a case of no content length, use standard size segments */
- mem_seg->segment = (char*)XP_ALLOC(MEMORY_CACHE_SEGMENT_SIZE);
- mem_seg->seg_size = MEMORY_CACHE_SEGMENT_SIZE;
- }
-
- if(!mem_seg->segment)
- {
- FREE(mem_seg);
- goto malloc_failure; /* skip memory cacheing */
- }
-
- /* malloc the main cache holding structure */
- memory_copy = XP_NEW(net_MemoryCacheObject);
- if(!memory_copy)
- {
- FREE(mem_seg);
- FREE(mem_seg->segment);
- goto malloc_failure; /* skip memory cacheing */
- }
- memset(memory_copy, 0, sizeof(net_MemoryCacheObject));
-
- memory_copy->list = XP_ListNew();
- if(!memory_copy->list)
- {
- FREE(mem_seg);
- FREE(mem_seg->segment);
- FREE(memory_copy);
- goto malloc_failure; /* skip memory cacheing */
- }
-
- /* if we get this far add the object to the size count
- * since if we fail anywhere below here it will get
- * subtracted by net_FreeMemoryCopy(memory_copy);
- */
- net_MemoryCacheSize += mem_seg->seg_size;
-
- /* add the segment malloced above to the segment list */
- net_cache_adding_object++; /* semaphore */
- XP_ListAddObject(memory_copy->list, mem_seg);
- net_cache_adding_object--; /* semaphore */
-
- StrAllocCopy(memory_copy->cache_obj.address, URL_s->address);
- memory_copy->cache_obj.method = URL_s->method;
- if(URL_s->post_data)
- {
- memory_copy->cache_obj.post_data_size = URL_s->post_data_size;
- StrAllocCopy(memory_copy->cache_obj.post_data, URL_s->post_data);
- StrAllocCopy(memory_copy->cache_obj.post_headers, URL_s->post_headers);
- }
-
- memory_copy->cache_obj.expires = URL_s->expires;
- memory_copy->cache_obj.last_modified = URL_s->last_modified;
-
- memory_copy->cache_obj.content_length = URL_s->content_length;
- memory_copy->cache_obj.real_content_length = URL_s->content_length;
- memory_copy->cache_obj.is_netsite = URL_s->is_netsite;
-
- StrAllocCopy(memory_copy->cache_obj.content_type, URL_s->content_type);
- StrAllocCopy(memory_copy->cache_obj.charset, URL_s->charset);
- StrAllocCopy(memory_copy->cache_obj.content_encoding,
- URL_s->content_encoding);
-
- /* check for malloc failure on critical fields above */
- if(!memory_copy->cache_obj.address || !memory_copy->cache_obj.content_type)
- {
- net_FreeMemoryCopy(memory_copy);
- goto malloc_failure; /* skip memory cacheing */
- }
-
- /* copy security info */
- memory_copy->cache_obj.security_on = URL_s->security_on;
- StrAllocCopy(memory_copy->cache_obj.key_cipher, URL_s->key_cipher);
- memory_copy->cache_obj.key_size = URL_s->key_size;
- memory_copy->cache_obj.key_secret_size = URL_s->key_secret_size;
- if (URL_s->certificate)
- memory_copy->cache_obj.certificate = CERT_DupCertificate(URL_s->certificate);
-
- /* build the stream object */
- stream = XP_NEW(NET_StreamClass);
- if(!stream)
- {
- net_FreeMemoryCopy(memory_copy);
- goto malloc_failure; /* skip memory cacheing */
- }
-
- /* this structure gets passed back into the write, complete
- * and abort stream functions
- */
- data_object = XP_NEW(CacheDataObject);
- if (!data_object)
- {
- FREE(stream);
- net_FreeMemoryCopy(memory_copy);
- goto malloc_failure; /* skip memory cacheing */
- }
-
- /* init the object */
- XP_MEMSET(data_object, 0, sizeof(CacheDataObject));
-
- /* assign the cache object to the stream data object
- */
- data_object->memory_copy = memory_copy;
-
- if (mem_conv_obj->dont_hold_URL_s == FALSE)
- data_object->URL_s = URL_s;
-
- TRACEMSG(("Returning stream from NET_CacheConverter\n"));
-
- stream->name = "Cache stream";
- stream->complete = (MKStreamCompleteFunc) net_MemCacheComplete;
- stream->abort = (MKStreamAbortFunc) net_MemCacheAbort;
- stream->put_block = (MKStreamWriteFunc) net_MemCacheWrite;
- stream->is_write_ready = (MKStreamWriteReadyFunc) net_MemCacheWriteReady;
- stream->data_object = data_object; /* document info object */
- stream->window_id = window_id;
-
- /* next stream is passed in by the caller but can be null */
- data_object->next_stream = mem_conv_obj->next_stream;
-
- PR_APPEND_LINK(&data_object->links, &mem_active_cache_data_objects);
-
- /* add the cache object to the hash list, but don't
- * complete it yet
- */
- net_MemCacheAddObjectToCache(data_object);
-
- return stream;
-
- malloc_failure: /* target of malloc failure */
-
- TRACEMSG(("NOT Caching this URL"));
-
- /* bypass the whole cache mechanism
- */
- if(format_out != FO_CACHE_ONLY)
- {
- format_out = CLEAR_CACHE_BIT(format_out);
- return(mem_conv_obj->next_stream);
- }
-
- return(NULL);
- }
-
- PRIVATE net_MemoryCacheObject *
- net_FindObjectInMemoryCache(URL_Struct *URL_s)
- {
- net_MemoryCacheObject tmp_cache_obj, *return_obj;
-
- /* fill in the temporary cache object so we can
- * use it for searching
- */
- XP_MEMSET(&tmp_cache_obj, 0, sizeof(tmp_cache_obj));
- tmp_cache_obj.cache_obj.method = URL_s->method;
- tmp_cache_obj.cache_obj.address = URL_s->address;
- tmp_cache_obj.cache_obj.post_data = URL_s->post_data;
- tmp_cache_obj.cache_obj.post_data_size = URL_s->post_data_size;
-
- return_obj = (net_MemoryCacheObject *)
- XP_HashListFindObject(net_MemoryCacheHashList, &tmp_cache_obj);
- if (return_obj)
- return (return_obj);
- else
- {
- /* try unescaping the URL address - MHTML parts are
- escaped, and weren't getting picked up in the
- cache */
- tmp_cache_obj.cache_obj.address = XP_STRDUP(URL_s->address);
- if (tmp_cache_obj.cache_obj.address)
- {
- /* unescape it */
- NET_UnEscape(tmp_cache_obj.cache_obj.address);
- if (tmp_cache_obj.cache_obj.address)
- {
- return_obj = (net_MemoryCacheObject *)
- XP_HashListFindObject(net_MemoryCacheHashList, &tmp_cache_obj);
- XP_FREE(tmp_cache_obj.cache_obj.address);
- }
- }
- return (return_obj);
- }
- }
-
- /* set or unset a lock on a memory cache object
- */
- MODULE_PRIVATE void
- NET_ChangeMemCacheLock(URL_Struct *URL_s, Bool set)
- {
- net_MemoryCacheObject * found_cache_obj;
-
- /* look up cache struct */
- found_cache_obj = net_FindObjectInMemoryCache(URL_s);
-
- if(found_cache_obj && found_cache_obj->completed)
- {
- XP_ASSERT(found_cache_obj->external_locks >= 0);
-
- if(set)
- {
- /* increment lock counter */
- found_cache_obj->external_locks++;
- }
- else
- {
- /* decrement lock counter */
- found_cache_obj->external_locks--;
-
- XP_ASSERT(found_cache_obj->external_locks >= 0);
-
- if(found_cache_obj->external_locks < 0)
- found_cache_obj->external_locks = 0;
- }
- }
- }
-
- /* remove a URL from the memory cache
- */
- MODULE_PRIVATE void
- NET_RemoveURLFromMemCache(URL_Struct *URL_s)
- {
- net_MemoryCacheObject * found_cache_obj;
-
- found_cache_obj = net_FindObjectInMemoryCache(URL_s);
-
- if(found_cache_obj &&
- (found_cache_obj->mem_read_lock == 0) &&
- found_cache_obj->completed)
- net_FreeMemoryCopy(found_cache_obj);
- }
-
- /* Returns TRUE if the URL is currently in the
- * memory cache and false otherwise.
- */
- PUBLIC Bool
- NET_IsURLInMemCache(URL_Struct *URL_s)
- {
- if(net_FindObjectInMemoryCache(URL_s))
- return(TRUE);
- else
- return(FALSE);
- }
-
- /* this function looks up a URL in the hash table and
- * returns MEMORY_CACHE_TYPE_URL if it's found and
- * should be loaded via the memory cache.
- *
- * It also sets several entries in the passed in URL
- * struct including "memory_copy" which is a pointer
- * to the found net_MemoryCacheObject struct.
- */
- MODULE_PRIVATE int
- NET_FindURLInMemCache(URL_Struct * URL_s, MWContext *ctxt)
- {
- net_MemoryCacheObject *found_cache_obj;
-
- TRACEMSG(("Checking for URL in cache"));
-
- if(!net_MemoryCacheHashList)
- return(0);
-
- found_cache_obj = net_FindObjectInMemoryCache(URL_s);
-
- if(found_cache_obj)
- {
- TRACEMSG(("mkcache: found URL in memory cache!"));
-
- if(found_cache_obj->current_page_only)
- {
- History_entry * he = SHIST_GetCurrent(&ctxt->hist);
- int hist_num;
-
- /* if the current_page_only flag is set
- * then the page should only be
- * served if it's the current document loaded
- * (i.e. reloads)
- *
- * @@@ if !he then let it through, this allows the
- * doc info window to work
- */
- if(he)
- {
- hist_num = SHIST_GetIndex(&ctxt->hist, he);
-
- /* we can tell if the document being loaded is the
- * same as the current document by looking up it's
- * index in the history. If they don't match
- * then it isn't the current document
- */
- if(URL_s->history_num != hist_num)
- return 0;
- }
- }
-
- /* set a pointer to the structure in the URL struct
- */
- URL_s->memory_copy = found_cache_obj;
-
- /* copy the contents of the URL struct so that the content type
- * and other stuff gets recorded
- */
- StrAllocCopy(URL_s->content_type,
- found_cache_obj->cache_obj.content_type);
- StrAllocCopy(URL_s->charset,
- found_cache_obj->cache_obj.charset);
- StrAllocCopy(URL_s->content_encoding,
- found_cache_obj->cache_obj.content_encoding);
- URL_s->content_length = found_cache_obj->cache_obj.content_length;
- URL_s->real_content_length = found_cache_obj->cache_obj.content_length;
- URL_s->expires = found_cache_obj->cache_obj.expires;
- URL_s->last_modified = found_cache_obj->cache_obj.last_modified;
- URL_s->is_netsite = found_cache_obj->cache_obj.is_netsite;
-
- /* copy security info */
- URL_s->security_on = found_cache_obj->cache_obj.security_on;
- URL_s->key_size = found_cache_obj->cache_obj.key_size;
- URL_s->key_secret_size = found_cache_obj->cache_obj.key_secret_size;
- StrAllocCopy(URL_s->key_cipher, found_cache_obj->cache_obj.key_cipher);
-
- /* remove any existing certificate */
- CERT_DestroyCertificate(URL_s->certificate);
- URL_s->certificate = NULL;
-
- if (found_cache_obj->cache_obj.certificate)
- URL_s->certificate =
- CERT_DupCertificate(found_cache_obj->cache_obj.certificate);
-
- net_cache_adding_object++; /* semaphore */
- /* reorder objects so that the list is in last accessed order */
- XP_ListRemoveObject(net_MemoryCacheList, found_cache_obj);
- XP_ListAddObject(net_MemoryCacheList, found_cache_obj);
- net_cache_adding_object--; /* semaphore */
-
- TRACEMSG(("Cached copy is valid. returning method"));
-
- return(MEMORY_CACHE_TYPE_URL);
- }
-
- TRACEMSG(("URL not found in cache"));
-
- return(0);
- }
-
- /*****************************************************************
- * Memory cache output module routine
- *
- * This set of routines pushes the document in memory up a stream
- * created by NET_StreamBuilder
- */
-
- /* used to hold data between invokations of ProcessNet
- */
- typedef struct _MemCacheConData {
- XP_List *cur_list_ptr;
- uint32 bytes_written_in_segment;
- NET_StreamClass *stream;
- } MemCacheConData;
-
- #define CD_CUR_LIST_PTR connection_data->cur_list_ptr
- #define CD_BYTES_WRITTEN_IN_SEGMENT connection_data->bytes_written_in_segment
- #define CD_STREAM connection_data->stream
-
- #define CE_URL_S cur_entry->URL_s
- #define CE_WINDOW_ID cur_entry->window_id
- #define CE_FORMAT_OUT cur_entry->format_out
- #define CE_STATUS cur_entry->status
- #define CE_BYTES_RECEIVED cur_entry->bytes_received
-
- /* begin the load, This is called from NET_GetURL
- */
- #ifdef MOZ_MAIL_NEWS
- extern int
- net_InitializeNewsFeData (ActiveEntry * cur_entry);
- extern int
- IMAP_InitializeImapFeData (ActiveEntry * cur_entry);
- extern void
- IMAP_URLFinished(URL_Struct *URL_s);
- #endif /* MOZ_MAIL_NEWS */
-
- PRIVATE int32
- net_MemoryCacheLoad (ActiveEntry * cur_entry)
- {
- /* get memory for Connection Data */
- MemCacheConData * connection_data = XP_NEW(MemCacheConData);
- net_MemorySegment * mem_seg;
- uint32 chunk_size;
- char *mem_seg_ptr;
- char *first_buffer;
-
- TRACEMSG(("Entering NET_MemoryCacheLoad!\n"));
-
- if(!connection_data)
- {
- CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
- CE_STATUS = MK_OUT_OF_MEMORY;
- return (CE_STATUS);
- }
-
- if (!CE_URL_S->memory_copy)
- {
- /* the memory_copy has been freed before fully
- * making it into the cache. In other words, it's
- * been aborted. Abort this load then, also.
- * We should probably never hit this case, however.
- * If we got here, it means we've just found this entry
- * in the hash table, which means we have not deleted it
- * and we have not set the delete_me flag.
- */
- XP_ASSERT(FALSE);
- CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
- return (CE_STATUS);
- }
-
- cur_entry->protocol = MEMORY_CACHE_TYPE_URL;
- cur_entry->memory_file = TRUE;
-
- /* point to the first list struct that contains data
- */
- CD_CUR_LIST_PTR = CE_URL_S->memory_copy->list->next;
- CD_BYTES_WRITTEN_IN_SEGMENT = 0;
-
- /* put a read lock on the data
- */
- CE_URL_S->memory_copy->mem_read_lock++;
-
- cur_entry->con_data = connection_data;
-
- cur_entry->local_file = TRUE;
-
- cur_entry->socket = NULL;
-
- NET_SetCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
-
- cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);
-
- FE_EnableClicking(CE_WINDOW_ID);
-
- #ifdef MOZ_MAIL_NEWS
- if (cur_entry->format_out == FO_PRESENT)
- {
- if (NET_URL_Type(cur_entry->URL_s->address) == NEWS_TYPE_URL)
- {
- /* #### DISGUSTING KLUDGE to make cacheing work for news articles. */
- cur_entry->status = net_InitializeNewsFeData (cur_entry);
- if (cur_entry->status < 0)
- {
- /* #### what error message? */
- return cur_entry->status;
- }
- }
- else if (!XP_STRNCMP(CE_URL_S->address, "Mailbox://", 10))
- {
- /* #### DISGUSTING KLUDGE to make cacheing work for imap articles. */
- cur_entry->status = IMAP_InitializeImapFeData (cur_entry);
- if (cur_entry->status < 0)
- {
- /* #### what error message? */
- return cur_entry->status;
- }
- }
- }
- #endif /* MOZ_MAIL_NEWS */
-
- /* open the outgoing stream
- */
- CD_STREAM = NET_StreamBuilder(CE_FORMAT_OUT, CE_URL_S, CE_WINDOW_ID);
- if(!CD_STREAM)
- {
- NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
-
- FREE(connection_data);
-
- CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
- CE_STATUS = MK_UNABLE_TO_CONVERT;
- return (CE_STATUS);
- }
-
- if (!CE_URL_S->load_background)
- FE_GraphProgressInit(CE_WINDOW_ID, CE_URL_S, CE_URL_S->content_length);
-
- /* process one chunk of the
- * cache file so that
- * layout can continue
- * when images are in the cache
- */
- #define FIRST_BUFF_SIZE 1024
- if (CE_URL_S->memory_copy->completed)
- {
- mem_seg = (net_MemorySegment *) CD_CUR_LIST_PTR->object;
-
- mem_seg_ptr = mem_seg->segment;
-
- chunk_size = MIN(FIRST_BUFF_SIZE,
- mem_seg->in_use-CD_BYTES_WRITTEN_IN_SEGMENT);
-
- /* malloc this first buffer because we can't use
- * the NET_SocketBuffer in calls from NET_GetURL
- * because of reentrancy
- */
- first_buffer = (char *) XP_ALLOC(chunk_size);
-
- if(!first_buffer)
- {
- FREE(connection_data);
- CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
- return(MK_OUT_OF_MEMORY);
- }
-
- /* copy the segment because the parser will muck with it
- */
- XP_MEMCPY(first_buffer,
- mem_seg_ptr+CD_BYTES_WRITTEN_IN_SEGMENT,
- (size_t) chunk_size);
-
- CD_BYTES_WRITTEN_IN_SEGMENT += chunk_size;
-
- CE_STATUS = (*CD_STREAM->put_block)(CD_STREAM,
- first_buffer,
- chunk_size);
- if(CE_STATUS < 0)
- {
- NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
-
- if (!CE_URL_S->load_background)
- FE_GraphProgressDestroy(CE_WINDOW_ID,
- CE_URL_S,
- CE_URL_S->content_length,
- CE_BYTES_RECEIVED);
-
- FREE(connection_data);
-
- return (CE_STATUS);
- }
-
- CE_BYTES_RECEIVED += chunk_size;
-
- /* check to see if we need to advance the pointer yet.
- * should hardly ever happen here since the first buffer
- * is so small
- */
- if(CD_BYTES_WRITTEN_IN_SEGMENT >= mem_seg->in_use)
- {
- CD_CUR_LIST_PTR = CD_CUR_LIST_PTR->next;
- CD_BYTES_WRITTEN_IN_SEGMENT = 0;
- }
-
- FREE(first_buffer);
- }
- else
- {
- CE_STATUS = 0;
- }
-
- return(CE_STATUS);
-
- }
-
- /* called repeatedly from NET_ProcessNet to push all the
- * data up the stream
- */
- PRIVATE int32
- net_ProcessMemoryCache (ActiveEntry * cur_entry)
- {
- MemCacheConData * connection_data = (MemCacheConData *)cur_entry->con_data;
- net_MemorySegment * mem_seg;
- uint32 chunk_size;
- uint32 buffer_size;
- char *mem_seg_ptr;
-
-
- if (!CE_URL_S->memory_copy)
- {
- /* the memory_copy has been freed before fully
- * making it into the cache. In other words, it's
- * been aborted. Abort this load then, also,
- * since it's a concurrent load.
- * We know that CE_URL_S->memory_copy will be NULL
- * if it's been removed, because we have a read lock
- * on the entry, and that will cause the delete_me
- * flag to be set, rather than just deleting the
- * object initially. We catch the delete_me flag
- * here, and set CE_URL_S->memory_copy to NULL.
- */
- CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
- return (CE_STATUS);
- }
-
-
- /* wait until the object has been fully inserted into
- * the cache.
- */
- if (!CE_URL_S->memory_copy->completed &&
- !(CE_URL_S->memory_copy->delete_me &&
- CE_URL_S->memory_copy->aborted))
- {
- /* normal case - we are still waiting for it to complete */
- return 0;
- }
-
- if(!CD_CUR_LIST_PTR ||
- (CE_URL_S->memory_copy->aborted &&
- CE_URL_S->memory_copy->delete_me))
- {
- /* when CD_CUR_LIST_PTR turns NULL we are at the end
- * of the document. Finish up the stream and exit.
- *
- * If the entry is aborted and the delete_me flag is set,
- * the stream was aborted in the middle of being written.
- * If we are here, then it's simultaneously being read also.
- * So, finish up.
- */
-
- /* complete the stream
- */
- (*CD_STREAM->complete)(CD_STREAM);
-
- /* free the stream
- */
- FREE(CD_STREAM);
-
- /* remove the read lock
- */
- CE_URL_S->memory_copy->mem_read_lock--;
-
- /* check to see if we should delete this memory cached object
- *
- * delete_me is set by FreeMemoryCacheObject when it tried
- * to free it but couldn't because of read locks on the
- * object
- */
- if(CE_URL_S->memory_copy->delete_me &&
- CE_URL_S->memory_copy->mem_read_lock == 0)
- {
- net_FreeMemoryCopy(CE_URL_S->memory_copy);
- CE_URL_S->memory_copy = 0;
- }
-
- /* FREE the structs used by this protocol module
- */
- FREE(connection_data);
-
- /* set the status to success */
- if (CE_URL_S->memory_copy && CE_URL_S->memory_copy->completed)
- CE_STATUS = MK_DATA_LOADED;
- else
- CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
-
- /* clear the CallNetlibAllTheTime if there are no
- * other readers
- */
- NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
-
- if (!CE_URL_S->load_background)
- FE_GraphProgressDestroy(CE_WINDOW_ID,
- CE_URL_S,
- CE_URL_S->content_length,
- CE_BYTES_RECEIVED);
-
- #ifdef MOZ_MAIL_NEWS
- if (!XP_STRNCMP(CE_URL_S->address, "Mailbox://", 10))
- /* #### DISGUSTING KLUDGE to make cacheing work for imap articles. */
- IMAP_URLFinished(CE_URL_S);
- #endif
- /* Tell ProcessNet that we are all done */
- return(-1);
- }
-
- /* CD_CUR_LIST_PTR is pointing to the most current
- * memory segment list object
- */
- mem_seg = (net_MemorySegment *) CD_CUR_LIST_PTR->object;
-
- TRACEMSG(("ProcessMemoryCache: printing segment %p",mem_seg));
-
- mem_seg_ptr = mem_seg->segment;
-
- /* write out at least part of the buffer
- */
- buffer_size = (*CD_STREAM->is_write_ready)(CD_STREAM);
- buffer_size = MIN(buffer_size, (unsigned int) NET_Socket_Buffer_Size);
-
- /* make it ??? at the most
- * when coming out of the cache
- */
- buffer_size = MIN(buffer_size, NET_MEM_CACHE_OUTPUT_SIZE);
-
- chunk_size = MIN(buffer_size, mem_seg->in_use-CD_BYTES_WRITTEN_IN_SEGMENT);
-
- /* copy the segment because the parser will muck with it
- */
- XP_MEMCPY(NET_Socket_Buffer,
- mem_seg_ptr+CD_BYTES_WRITTEN_IN_SEGMENT,
- (size_t) chunk_size);
-
- /* remember how much of this segment we have written */
- CD_BYTES_WRITTEN_IN_SEGMENT += chunk_size;
-
- CE_STATUS = (*CD_STREAM->put_block)(CD_STREAM,
- NET_Socket_Buffer,
- chunk_size);
- CE_BYTES_RECEIVED += chunk_size;
-
- /* check to see if we need to advance the pointer yet
- */
- if(CD_BYTES_WRITTEN_IN_SEGMENT >= mem_seg->in_use)
- {
- CD_CUR_LIST_PTR = CD_CUR_LIST_PTR->next;
- CD_BYTES_WRITTEN_IN_SEGMENT = 0;
- }
-
- if (!CE_URL_S->load_background)
- FE_GraphProgress(CE_WINDOW_ID, CE_URL_S, CE_BYTES_RECEIVED,
- chunk_size, CE_URL_S->content_length);
-
- if(CE_STATUS < 0)
- {
- NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
-
- (*CD_STREAM->abort)(CD_STREAM, CE_STATUS);
- }
-
- return(CE_STATUS);
-
- }
-
- /* called by functions in mkgeturl to interrupt the loading of
- * an object. (Usually a user interrupt)
- */
- PRIVATE int32
- net_InterruptMemoryCache (ActiveEntry * cur_entry)
- {
- MemCacheConData * connection_data = (MemCacheConData *)cur_entry->con_data;
-
- /* abort and free the outgoing stream
- */
- (*CD_STREAM->abort)(CD_STREAM, MK_INTERRUPTED);
- FREE(CD_STREAM);
-
- if (!CE_URL_S->memory_copy)
- {
- /* the memory_copy has been freed before fully
- * making it into the cache. In other words, it's
- * been aborted. Abort this load then, also,
- * since it's a concurrent load.
- */
- CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
- return (CE_STATUS);
- }
-
- /* remove the read lock
- */
- CE_URL_S->memory_copy->mem_read_lock--;
-
- /* check to see if we should delete this memory cached object
- */
- if(CE_URL_S->memory_copy->delete_me &&
- CE_URL_S->memory_copy->mem_read_lock == 0)
- {
- net_FreeMemoryCopy(CE_URL_S->memory_copy);
- CE_URL_S->memory_copy = 0;
- }
-
- /* FREE the structs
- */
- FREE(connection_data);
- CE_STATUS = MK_INTERRUPTED;
-
- NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
-
- return(CE_STATUS);
- }
-
- /* create an HTML stream and push a bunch of HTML about
- * the memory cache
- */
- MODULE_PRIVATE void
- NET_DisplayMemCacheInfoAsHTML(ActiveEntry * cur_entry)
- {
- char *buffer = (char*)XP_ALLOC(2048);
- char *address;
- char *escaped;
- NET_StreamClass * stream;
- net_CacheObject * cache_obj;
- net_MemoryCacheObject * mem_cache_obj;
- Bool long_form = FALSE;
- int32 number_in_memory_cache;
- XP_List *list_ptr;
- int i;
-
- if(!buffer)
- {
- cur_entry->status = MK_UNABLE_TO_CONVERT;
- return;
- }
-
- if(strcasestr(cur_entry->URL_s->address, "?long"))
- long_form = TRUE;
- else if(strcasestr(cur_entry->URL_s->address, "?traceon"))
- NET_CacheTraceOn = TRUE;
- else if(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;
- 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 ? XP_STRLEN(part) : 7); \
- if(cur_entry->status < 0) \
- goto END;
-
- if(!net_MemoryCacheList)
- {
- XP_STRCPY(buffer, "There are no objects in the memory cache");
- PUT_PART(buffer);
- goto END;
- }
-
- number_in_memory_cache = XP_ListCount(net_MemoryCacheList);
-
- /* add the header info */
- XP_SPRINTF(buffer,
- "<TITLE>Information about the Netscape memory cache</TITLE>\n"
- "<h2>Memory 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_MaxMemoryCacheSize,
- net_MemoryCacheSize,
- number_in_memory_cache,
- number_in_memory_cache ? net_MemoryCacheSize/number_in_memory_cache : 0);
-
- PUT_PART(buffer);
-
- /* define some macros to help us output HTML tables
- */
- #if 0
-
- #define TABLE_TOP(arg1) \
- XP_SPRINTF(buffer, \
- "<TR><TD ALIGN=RIGHT><b>%s</TD>\n" \
- "<TD>", arg1); \
- PUT_PART(buffer);
-
- #define TABLE_BOTTOM \
- XP_SPRINTF(buffer, \
- "</TD></TR>"); \
- PUT_PART(buffer);
-
- #else
-
- #define TABLE_TOP(arg1) \
- XP_STRCPY(buffer, "<tt>"); \
- for(i=XP_STRLEN(arg1); i < 16; i++) \
- XP_STRCAT(buffer, " "); \
- XP_STRCAT(buffer, arg1); \
- XP_STRCAT(buffer, " </tt>"); \
- PUT_PART(buffer);
-
- #define TABLE_BOTTOM \
- XP_STRCPY(buffer, "<BR>\n"); \
- PUT_PART(buffer);
-
- #endif
-
- list_ptr = net_MemoryCacheList;
-
- while((mem_cache_obj = (net_MemoryCacheObject *) XP_ListNextObject(list_ptr)) != NULL)
- {
-
- cache_obj = &mem_cache_obj->cache_obj;
- address = XP_STRDUP(mem_cache_obj->cache_obj.address);
-
- /* put the URL out there */
- TABLE_TOP("URL:");
- XP_STRCPY(buffer, "<A TARGET=Internal_URL_Info HREF=about:");
- PUT_PART(buffer);
- PUT_PART(address);
- XP_STRCPY(buffer, ">");
- PUT_PART(buffer);
- escaped = NET_EscapeHTML(address);
- PUT_PART(escaped);
- FREE(address);
- FREE(escaped);
- XP_STRCPY(buffer, "</A>");
- PUT_PART(buffer);
- TABLE_BOTTOM;
-
- TABLE_TOP("Content Length:");
- XP_SPRINTF(buffer, "%lu", cache_obj->content_length);
- PUT_PART(buffer);
- TABLE_BOTTOM;
-
- TABLE_TOP("Content type:");
- PUT_PART(cache_obj->content_type);
- TABLE_BOTTOM;
-
- TABLE_TOP("Last Modified:");
- if(cache_obj->last_modified)
- {
- PUT_PART(ctime(&cache_obj->last_modified));
- }
- else
- {
- XP_STRCPY(buffer, "No date sent");
- PUT_PART(buffer);
- }
- TABLE_BOTTOM;
-
- TABLE_TOP("Expires:");
- if(cache_obj->expires)
- {
- PUT_PART(ctime(&cache_obj->expires));
- }
- else
- {
- XP_STRCPY(buffer, "No expiration date sent");
- PUT_PART(buffer);
- }
- TABLE_BOTTOM;
-
- TABLE_TOP("Last accessed:");
- PUT_PART(ctime(&cache_obj->last_accessed));
- TABLE_BOTTOM;
-
- TABLE_TOP("Character set:");
- if(cache_obj->charset)
- {
- PUT_PART(cache_obj->charset);
- }
- else
- {
- XP_STRCPY(buffer, "iso-8859-1 (default)");
- PUT_PART(buffer);
- }
- TABLE_BOTTOM;
-
- TABLE_TOP("Secure:");
- XP_SPRINTF(buffer, "%s", cache_obj->security_on ? "TRUE" : "FALSE");
- PUT_PART(buffer);
- TABLE_BOTTOM;
-
- XP_STRCPY(buffer, "\n<P>\n");
- PUT_PART(buffer);
-
-
- }
-
- END:
- FREE(buffer);
- if(cur_entry->status < 0)
- (*stream->abort)(stream, cur_entry->status);
- else
- (*stream->complete)(stream);
-
- return;
- }
-
- /*Accessor for use by Cache Browser */
- PUBLIC net_CacheObject*
- NET_FirstMemCacheObject(XP_List* list_ptr)
- {
- if (net_MemoryCacheList)
- {
- list_ptr = net_MemoryCacheList;
- return NET_NextMemCacheObject(list_ptr);
- }
- else
- return 0;
- }
-
- /*Accessor for use by Cache Browser */
- PUBLIC net_CacheObject*
- NET_NextMemCacheObject(XP_List* list_ptr)
- {
- if (list_ptr)
- {
- net_MemoryCacheObject * tmp = (net_MemoryCacheObject *) XP_ListNextObject(list_ptr);
- if (tmp)
- {
- return &tmp->cache_obj;
- }
- }
- return 0;
- }
-
- #include "libmocha.h"
-
- NET_StreamClass *
- net_CloneWysiwygMemCacheEntry(MWContext *window_id, URL_Struct *URL_s,
- uint32 nbytes, const char * wysiwyg_url,
- const char * base_href)
- {
- net_MemoryCacheObject *memory_copy;
- PRCList *link;
- CacheDataObject *data_object;
- NET_StreamClass *stream;
- XP_List *list;
- net_MemorySegment *seg;
- uint32 len;
-
- if (!(memory_copy = URL_s->memory_copy))
- {
- /* not hitting the cache -- check whether we're filling it */
- for (link = mem_active_cache_data_objects.next;
- link != &mem_active_cache_data_objects;
- link = link->next)
- {
- data_object = (CacheDataObject *)link;
- if (data_object->URL_s == URL_s &&
- (memory_copy = data_object->memory_copy) != NULL)
- {
- goto found;
- }
- }
- return NULL;
- }
-
- found:
- stream = LM_WysiwygCacheConverter(window_id, URL_s,
- wysiwyg_url, base_href);
- if (!stream)
- return 0;
- list = memory_copy->list;
- while (nbytes != 0 &&
- (seg = (net_MemorySegment *) XP_ListNextObject(list)) != NULL)
- {
- len = seg->seg_size;
- if (len > nbytes)
- len = nbytes;
- if (stream->put_block(stream, seg->segment,
- (int32)len) < 0)
- break;
- nbytes -= len;
- }
- if (nbytes != 0)
- {
- /* NB: Our caller must clear top_state->mocha_write_stream. */
- stream->abort(stream, MK_UNABLE_TO_CONVERT);
- XP_DELETE(stream);
- return 0;
- }
- return stream;
- }
-
- PRIVATE void
- net_CleanupMemoryCacheProtocol(void)
- {
- }
-
- void
- NET_InitMemCacProtocol(void)
- {
- static NET_ProtoImpl mem_cac_proto_impl;
-
- mem_cac_proto_impl.init = net_MemoryCacheLoad;
- mem_cac_proto_impl.process = net_ProcessMemoryCache;
- mem_cac_proto_impl.interrupt = net_InterruptMemoryCache;
- mem_cac_proto_impl.cleanup = net_CleanupMemoryCacheProtocol;
-
- NET_RegisterProtocolImplementation(&mem_cac_proto_impl, MEMORY_CACHE_TYPE_URL);
- }
-
- #endif /* MOZILLA_CLIENT */
-