home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libnet / mkmemcac.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  54.5 KB  |  2,016 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /* Module to do memory caching; storage and retreival
  19.  * very simple hash table based scheme.
  20.  *
  21.  * designed and originally implemented by Lou Montulli 
  22.  * modifications/additions by Gagan Saksena
  23.  */
  24.  
  25. /* Please leave outside of ifdef for windows precompiled headers */
  26. #include "mkutils.h"
  27.  
  28. #ifdef MOZILLA_CLIENT
  29.  
  30. #include "mkcache.h"
  31. #include "glhist.h"
  32. #include "xp_hash.h"
  33. #include "xp_mcom.h"
  34. #include "client.h"
  35. #include "mkgeturl.h"
  36. #include "mkstream.h"
  37. #include "extcache.h"
  38. #include "mkmemcac.h"
  39. #include "cert.h" /* for CERT_DupCertificate() */
  40. #include "libimg.h"             /* Image Lib public API. */
  41. #include "prclist.h"
  42. #include "shist.h"
  43.  
  44. /* exported error ints */
  45. extern int MK_OUT_OF_MEMORY;
  46.  
  47. /* the size of the cache segment output size
  48.  */
  49. #define NET_MEM_CACHE_OUTPUT_SIZE 2048
  50.  
  51. /* MAX_MEMORY_ALLOC_SIZE is the size of the largest malloc
  52.  * used in this module
  53.  * 
  54.  * the NET_Socket_Buffer can never be larger than the
  55.  * MAX_MEMORY_ALLOC_SIZE or this will fail
  56.  */
  57. #ifdef XP_WIN16
  58. #define MAX_MEMORY_ALLOC_SIZE 32767 /* MAXINT */
  59. #elif defined(XP_MAC)
  60. #define MAX_MEMORY_ALLOC_SIZE 12*1024
  61. #else
  62. #define MAX_MEMORY_ALLOC_SIZE (((unsigned) (~0) << 1) >> 1)  /* MAXINT */
  63.  
  64. /* @@@@@@@@@ there is currently a limit on XP_ALLOC to less than 32 K */
  65. #undef  MAX_MEMORY_ALLOC_SIZE
  66. #define MAX_MEMORY_ALLOC_SIZE 32767 /* MAXINT */
  67. #endif
  68.  
  69. /* this is the minimum size of each of the memory segments used to hold
  70.  * the cache object in memory
  71.  */
  72. #ifdef XP_MAC
  73. #define MEMORY_CACHE_SEGMENT_SIZE 12*1024
  74. #else
  75. #define MEMORY_CACHE_SEGMENT_SIZE 2048
  76. #endif
  77.  
  78. /* !!! structure is typedef'd to net_MemoryCacheObject
  79.  * in net.h
  80.  */
  81. struct _net_MemoryCacheObject {
  82.     XP_List         *list;
  83.     net_CacheObject  cache_obj;
  84.     int              external_locks;       /* locks set by other modules calling in */
  85.     int              mem_read_lock;        /* the number of current readers */
  86.     Bool             delete_me;            /* set this to delete the object 
  87.                                             * once all readers are done 
  88.                                             */
  89.     Bool             current_page_only;    /* this is set when the document
  90.                                             * requested not to be cached.
  91.                                             * When set the document should
  92.                                             * only be served if it's a
  93.                                             * resize or print
  94.                                             */
  95.     Bool             completed;            /* whether or not we have completed the
  96.                                             * caching of this object.  Initialized to
  97.                                             * false, then set to true once the
  98.                                             * complete function is called.
  99.                                             */
  100.     Bool            aborted;               /* whether or not we have aborted the
  101.                                             * caching of this object.
  102.                                             */
  103. };
  104.  
  105. /* structure to hold memory
  106.  * segments
  107.  */
  108. typedef struct _net_MemorySegment {
  109.     char      *segment;
  110.     uint32     seg_size;
  111.     uint32     in_use;
  112. } net_MemorySegment;
  113.  
  114. /* the hash table pointer that holds all the memory cache
  115.  * objects for quick lookup
  116.  */
  117. PRIVATE XP_HashList * net_MemoryCacheHashList = 0;
  118.  
  119. /* semaphore counter set when calling any of the list add functions
  120.  */
  121. PRIVATE int net_cache_adding_object=0;
  122.  
  123. /* a list of all the documents currently in
  124.  * memory
  125.  *
  126.  * The net_MemoryCacheList is used as a delete queue
  127.  * and is ordered by last-accessed time.
  128.  */
  129. PRIVATE XP_List   *net_MemoryCacheList=0;
  130. PRIVATE uint32     net_MemoryCacheSize=0;
  131. PRIVATE uint32     net_MaxMemoryCacheSize=0;
  132.  
  133. /* this object is used by the MemCacheConverter.
  134.  * MemCacheConverter is a standard netlib stream and uses
  135.  * this structure to hold data between invokations of
  136.  * the write function and complete or abort.
  137.  */
  138. typedef struct _CacheDataObject {
  139.     PRCList               links;
  140.     NET_StreamClass       *next_stream;
  141.     net_MemoryCacheObject *memory_copy;
  142.     URL_Struct            *URL_s;
  143.     uint32                   computed_content_length;
  144. } CacheDataObject;
  145.  
  146. /* PRIVATE XXX Mac CodeWarrior bug */ PRCList mem_active_cache_data_objects
  147.     = PR_INIT_STATIC_CLIST(&mem_active_cache_data_objects);
  148.  
  149. /* free a segmented memory copy of an object
  150.  * and the included net_cacheObject struct
  151.  */
  152. PRIVATE void
  153. net_FreeMemoryCopy(net_MemoryCacheObject * mem_copy)
  154. {
  155.     net_MemorySegment * mem_seg;
  156.  
  157.     XP_ASSERT(mem_copy);
  158.  
  159.     if(!mem_copy)
  160.         return;
  161.  
  162.      /* If the stream was aborted while being written, and nobody is
  163.       * reading it, then it's ok to delete it now.  That is,
  164.       * we don't need to check if we want to set delete_me
  165.       */
  166.     if (!((mem_copy->mem_read_lock == 0) && mem_copy->aborted))
  167.     {
  168.         /* if this object is currently being read or written DO NOT delete it.
  169.          * It will be deleted by another call to this function
  170.          * after it is finished being read.
  171.          */
  172.          if (((mem_copy->mem_read_lock > 0) && mem_copy->completed) ||
  173.             !mem_copy->completed)
  174.           {
  175.             TRACEMSG(("Seting delete_me flag on memory cache object"));
  176.             mem_copy->delete_me = TRUE;
  177.  
  178.             /* remove it from the hash list so it won't be found
  179.              * by NET_GetURLInMemCache
  180.              */
  181.             XP_HashListRemoveObject(net_MemoryCacheHashList, mem_copy);
  182.             return;
  183.           }
  184.     }
  185.  
  186.     TRACEMSG(("Freeing memory cache copy"));
  187.  
  188.     /* free and delete the memory segment list
  189.      */
  190.     while((mem_seg = (net_MemorySegment *)XP_ListRemoveTopObject(mem_copy->list)) != NULL)
  191.       {
  192.         /* reduce the global memory cache size
  193.          */
  194.         net_MemoryCacheSize -= mem_seg->seg_size;
  195.         FREE(mem_seg->segment);
  196.         FREE(mem_seg);
  197.       }
  198.  
  199.     XP_ListDestroy(mem_copy->list);
  200.  
  201.     /* Remove it from the hash and delete lists,
  202.      * If the object isn't in these lists anymore
  203.      * the call will be ignored.
  204.      */
  205.     XP_HashListRemoveObject(net_MemoryCacheHashList, mem_copy);
  206.     XP_ListRemoveObject(net_MemoryCacheList, mem_copy);
  207.  
  208.     FREEIF(mem_copy->cache_obj.address);
  209.     FREEIF(mem_copy->cache_obj.post_data);
  210.     FREEIF(mem_copy->cache_obj.post_headers);
  211.     FREEIF(mem_copy->cache_obj.content_type);
  212.     FREEIF(mem_copy->cache_obj.charset);
  213.     FREEIF(mem_copy->cache_obj.content_encoding);
  214.  
  215.     FREE(mem_copy);
  216.  
  217. }
  218.  
  219. /* removes the last mem object.  Returns negative on error,
  220.  * otherwise returns the current size of the cache
  221.  */
  222. PRIVATE int32
  223. net_remove_last_memory_cache_object(void)
  224. {
  225.     net_MemoryCacheObject * mem_cache_obj;
  226.     
  227.     /* safty valve in case there are no list items 
  228.      * or if we are in the process of adding an object
  229.      */
  230.     if(net_cache_adding_object)
  231.         return -1;
  232.  
  233.     mem_cache_obj = (net_MemoryCacheObject *) 
  234.                                 XP_ListRemoveEndObject(net_MemoryCacheList);
  235.  
  236.     if(!mem_cache_obj)
  237.         return -1;
  238.  
  239.     /* we can't remove it if we have external locks or if it's not completed -
  240.      * try removing another one 
  241.      */
  242.     if(mem_cache_obj->external_locks || !mem_cache_obj->completed)
  243.       {
  244.         int status = net_remove_last_memory_cache_object(); /* recurse */
  245.         /* add the object back in */
  246.         XP_ListAddObject(net_MemoryCacheList, mem_cache_obj);
  247.         return status;
  248.       }
  249.  
  250.     net_FreeMemoryCopy(mem_cache_obj);
  251.  
  252.     return((int32) net_MemoryCacheSize);
  253. }
  254.  
  255. /* this function free's objects to bring the cache size
  256.  * below the size passed in
  257.  */
  258. PRIVATE void
  259. net_ReduceMemoryCacheTo(uint32 size)
  260. {
  261.     /* safty valve in case we are in the process of adding an object
  262.      */
  263.     if(net_cache_adding_object)
  264.         return;
  265.  
  266.     while(net_MemoryCacheSize > size)
  267.       {
  268.         if(0 > net_remove_last_memory_cache_object())
  269.             break;
  270.       }
  271. }
  272.  
  273. /* set the size of the Memory cache.
  274.  * Set it to 0 if you want cacheing turned off
  275.  * completely
  276.  */
  277. #define HTML_CACHE_MAX_BASE_SIZE 1024l*1024l
  278. #define HTML_CACHE_VARIABLE_GROWTH .05   /* the percentage to add */
  279. PUBLIC void
  280. NET_SetMemoryCacheSize(int32 new_size)
  281. {
  282.     int32 image_cache_size;
  283.     int32 html_cache_size;
  284.  
  285.     if(new_size <= 0)
  286.       {
  287.         IL_SetCacheSize(0);
  288.         net_ReduceMemoryCacheTo(0);
  289.         net_MaxMemoryCacheSize = 0;
  290.         return;
  291.       }
  292.  
  293.     /* the netlib mem cache is set to .3*new_size 
  294.      * or 200K + .05 * new_size, whichever is less.
  295.      * and imagelb uses the rest 
  296.      */
  297.     if(new_size * .3 < HTML_CACHE_MAX_BASE_SIZE)
  298.       {
  299.         /* use .3 of new_size */
  300.         html_cache_size = (int32) (.3 * new_size);
  301.       }
  302.     else
  303.       {
  304.         /* use HTML_CACHE_MAX_BASE_SIZE + .05*new_size */
  305.         html_cache_size = HTML_CACHE_MAX_BASE_SIZE + 
  306.                           (int32)(HTML_CACHE_VARIABLE_GROWTH*
  307.                            (new_size - HTML_CACHE_MAX_BASE_SIZE));
  308.       }
  309.     
  310.     image_cache_size = new_size - html_cache_size;
  311.  
  312.     net_MaxMemoryCacheSize = html_cache_size;
  313.     
  314.     net_ReduceMemoryCacheTo((uint32) html_cache_size);
  315.  
  316.     /* set the image cache to be the rest
  317.      */
  318.     IL_SetCacheSize(image_cache_size);
  319.  
  320.     return;
  321. }
  322.  
  323. /* Remove the last memory cache object if one exists
  324.  * Returns the total size of the memory cache in bytes
  325.  * after performing the operation
  326.  */
  327. PUBLIC int32
  328. NET_RemoveLastMemoryCacheObject()
  329. {
  330.     net_remove_last_memory_cache_object();
  331.  
  332.     return(net_MemoryCacheSize);
  333. }
  334.  
  335. /* returns the number of bytes currently in use by the Memory cache
  336.  */
  337. PUBLIC int32
  338. NET_GetMemoryCacheSize()
  339. {
  340.     return(net_MemoryCacheSize);
  341. }
  342.  
  343. PUBLIC int32
  344. NET_GetMaxMemoryCacheSize()
  345. {
  346.     return net_MaxMemoryCacheSize;
  347. }
  348.  
  349. /* compare entries for the hashing functions
  350.  *
  351.  * return 0 if match or positive or negative on
  352.  * non match
  353.  */
  354. PRIVATE int net_CacheHashComp(net_MemoryCacheObject * obj1,
  355.                                  net_MemoryCacheObject * obj2)
  356. {
  357.     int result;
  358.     char *hash1;
  359.     char *hash2;
  360.     char *ques1=0;
  361.     char *ques2=0;
  362.     char *ques3=0;
  363.     char *ques4=0;
  364.  
  365.     if(obj1->cache_obj.method != obj2->cache_obj.method)
  366.         return(obj1->cache_obj.method - obj2->cache_obj.method);
  367.  
  368.     /* If this is a "news:" or "snews:" URL, then any search data in the URL
  369.      * (the stuff after '?') should be ignored for cache-comparison purposes,
  370.      * so that "news:MSGID" and "news:MSGID?headers=all" share the same cache
  371.      * file.
  372.      */
  373.     if((XP_TO_UPPER(obj1->cache_obj.address[0]) == 'N'
  374.         || XP_TO_UPPER(obj1->cache_obj.address[0]) == 'S')
  375.         &&  NET_URL_Type(obj1->cache_obj.address) == NEWS_TYPE_URL)
  376.       {
  377.         ques1 = XP_STRCHR(obj1->cache_obj.address, '?');
  378.         if(ques1) 
  379.             *ques1 = '\0';
  380.       }
  381.  
  382.     if((XP_TO_UPPER(obj2->cache_obj.address[0]) == 'N'
  383.         || XP_TO_UPPER(obj2->cache_obj.address[0]) == 'S')
  384.         &&  NET_URL_Type(obj2->cache_obj.address) == NEWS_TYPE_URL)
  385.       {
  386.         ques2 = XP_STRCHR(obj2->cache_obj.address, '?');
  387.         if(ques2)
  388.             *ques2 = '\0';
  389.       }
  390.  
  391.  
  392.     /* do the same for IMAP */
  393.     if(!strncasecomp(obj1->cache_obj.address,"mailbox://",10))
  394.       {
  395.         ques3 = XP_STRSTR(obj1->cache_obj.address, "&part=");
  396.         if(ques3) 
  397.             *ques3 = '\0';
  398.       }
  399.  
  400.     if(!strncasecomp(obj2->cache_obj.address,"mailbox://",10))
  401.       {
  402.         ques4 = XP_STRSTR(obj2->cache_obj.address, "&part=");
  403.         if(ques4)
  404.             *ques4 = '\0';
  405.       }
  406.  
  407.  
  408.     /* strip hash symbols because they
  409.      * really represent the same
  410.      * document
  411.      */
  412.     hash1 = XP_STRCHR(obj1->cache_obj.address, '#');
  413.     hash2 = XP_STRCHR(obj2->cache_obj.address, '#');
  414.  
  415.     if(hash1)
  416.         *hash1 = '\0';
  417.     if(hash2)
  418.         *hash2 = '\0';
  419.  
  420.     result = XP_STRCMP(obj1->cache_obj.address, obj2->cache_obj.address);
  421.  
  422.     /* set them back to previous values
  423.      */
  424.     if(hash1)
  425.         *hash1 = '#';
  426.     if(hash2)
  427.         *hash2 = '#';
  428.     if(ques1)
  429.         *ques1 = '?';
  430.     if(ques2)
  431.         *ques2 = '?';
  432.     if(ques3)
  433.         *ques3 = '&';
  434.     if(ques4)
  435.         *ques4 = '&';
  436.  
  437.  
  438.     if(result != 0)
  439.        return(result);
  440.  
  441.     if(!obj1->cache_obj.post_data && !obj2->cache_obj.post_data)
  442.        return(0);  /* match; no post data on either */
  443.  
  444.     if(obj1->cache_obj.post_data && obj2->cache_obj.post_data)
  445.       {
  446.         result = XP_STRCMP(obj1->cache_obj.post_data, 
  447.                            obj2->cache_obj.post_data);
  448.         return(result);
  449.       }
  450.     else
  451.       {  /* one but not the other */
  452.         if(obj1->cache_obj.post_data)
  453.            return(1);
  454.         else
  455.            return(-1);
  456.       }
  457.  
  458.     return(0);  /* match with post data */
  459. }
  460.  
  461. /* hashing function for url's
  462.  *
  463.  * This is some magic Jamie gave me...
  464.  */
  465. PRIVATE uint32 net_CacheHashFunc(net_MemoryCacheObject * obj1)
  466. {
  467.     unsigned const char *x;
  468.     uint32 h = 0;
  469.     uint32 g;
  470.     XP_Bool news_type_url = FALSE;
  471.     XP_Bool imap_type_url = FALSE;
  472.  
  473.     if(!obj1)
  474.         return 0;
  475.  
  476.     x = (unsigned const char *) obj1->cache_obj.address;
  477.  
  478.     /* figure out if it's a news type URL */
  479.     if((XP_TO_UPPER(obj1->cache_obj.address[0]) == 'N'
  480.         || XP_TO_UPPER(obj1->cache_obj.address[0]) == 'S')
  481.         &&  NET_URL_Type(obj1->cache_obj.address) == NEWS_TYPE_URL)
  482.         news_type_url = TRUE;
  483.     /* figure out if it's an IMAP type URL */
  484.     else if (XP_TO_UPPER(obj1->cache_obj.address[0]) == 'M' &&
  485.         !strncasecomp(obj1->cache_obj.address,"Mailbox://",10))
  486.         imap_type_url = TRUE;
  487.  
  488.     /* modify the default String hash function
  489.      * to work with URL's
  490.      */
  491.     assert(x);
  492.  
  493.     if (!x) return 0;
  494.  
  495.     /* ignore '#' data for all URL's.
  496.       * ignore '?' data for news URL's
  497.      * ignore '&' data for all IMAP URL's
  498.      */
  499.     while (*x != 0 && *x != '#' && (!news_type_url || *x != '?') &&
  500.         (!imap_type_url || *x != '&'))
  501.       {
  502.         h = (h << 4) + *x++;
  503.         if ((g = h & 0xf0000000) != 0)
  504.           h = (h ^ (g >> 24)) ^ g;
  505.       }
  506.  
  507.     return h;
  508. }
  509.  
  510. /******************************************************************
  511.  * Cache Converter Stream input routines
  512.  */
  513.  
  514. /* is the stream ready for writing?
  515.  */
  516. PRIVATE unsigned int net_MemCacheWriteReady (NET_StreamClass *stream)
  517. {
  518.    CacheDataObject *obj=stream->data_object;   
  519.    if(obj->next_stream)
  520.        return((*obj->next_stream->is_write_ready)(obj->next_stream));
  521.    else
  522.        return(MAX_WRITE_READY);
  523. }
  524.  
  525. /*  stream write function
  526.  *
  527.  * this function accepts a stream and writes the data
  528.  * into memory segments on a segment list.
  529.  */
  530. PRIVATE int net_MemCacheWrite (NET_StreamClass *stream, CONST char* buffer, int32 len)
  531. {
  532.     CacheDataObject *obj=stream->data_object;    
  533.     TRACEMSG(("net_MemCacheWrite called with %ld buffer size", len));
  534.  
  535.     /* if delete_me is set, we don't want to keep writing to the memcache stream.
  536.      */
  537.     if(obj->memory_copy && !obj->memory_copy->delete_me)
  538.       {
  539.         net_MemorySegment * mem_seg;
  540.         char * cur_mem_seg_ptr;
  541.  
  542.         /* compute a content_length as we read data 
  543.          * to use as a comparison and for URL's that
  544.          * don't send a content type
  545.          */
  546.         obj->computed_content_length += len;
  547.  
  548.         /* we are always adding to the end one in the list so 
  549.          * lets get it
  550.          */
  551.         mem_seg = (net_MemorySegment *)
  552.                             XP_ListGetEndObject(obj->memory_copy->list);
  553.         XP_ASSERT(mem_seg);
  554.     
  555.         if(mem_seg->in_use+len > mem_seg->seg_size)
  556.           {
  557.             /* we need a new segment
  558.              */
  559.             net_MemorySegment * new_mem_seg = XP_NEW(net_MemorySegment);
  560.             uint32 size_left_in_old_buffer = mem_seg->seg_size - mem_seg->in_use;
  561.             uint32 size_for_new_buffer = len - size_left_in_old_buffer;
  562.             char * new_mem_seg_ptr;
  563.         
  564.             if(!new_mem_seg)
  565.               {
  566.                 net_FreeMemoryCopy(obj->memory_copy);
  567.                 obj->memory_copy = 0;
  568.                 goto EndOfMemWrite;
  569.               }
  570.  
  571.             /* @@@ the socket buffer can never be larger
  572.              * than the MAX_MEMORY_ALLOC_SIZE
  573.              */
  574.             if(size_for_new_buffer > MEMORY_CACHE_SEGMENT_SIZE)
  575.               {
  576.                 new_mem_seg->segment = (char*)XP_ALLOC(size_for_new_buffer);
  577.                 new_mem_seg->seg_size = size_for_new_buffer;
  578.               }
  579.             else
  580.               {
  581.                 new_mem_seg->segment = (char*)XP_ALLOC(
  582.                                                     MEMORY_CACHE_SEGMENT_SIZE);
  583.                 new_mem_seg->seg_size = MEMORY_CACHE_SEGMENT_SIZE;
  584.               }
  585.  
  586.             if(!new_mem_seg->segment)
  587.               {
  588.                 FREE(new_mem_seg);
  589.                 net_FreeMemoryCopy(obj->memory_copy);
  590.                 obj->memory_copy = 0;
  591.                 goto EndOfMemWrite;
  592.               }
  593.  
  594.             /* increase the global cache size counter
  595.              */
  596.             net_MemoryCacheSize += new_mem_seg->seg_size;
  597.  
  598.             TRACEMSG(("Cache size now: %d", net_MemoryCacheSize));
  599.  
  600.             cur_mem_seg_ptr = mem_seg->segment;
  601.             new_mem_seg_ptr = new_mem_seg->segment;
  602.  
  603.             /* fill in what we can to the old buffer
  604.              */
  605.             if(size_left_in_old_buffer)
  606.               {
  607.                 XP_MEMCPY(cur_mem_seg_ptr+mem_seg->in_use, 
  608.                           buffer, 
  609.                           (size_t) size_left_in_old_buffer);
  610.                 mem_seg->in_use = mem_seg->seg_size;  /* seg new size */
  611.               }
  612.  
  613.             /* fill in the new buffer
  614.              */
  615.             XP_MEMCPY(new_mem_seg_ptr, 
  616.                       buffer+size_left_in_old_buffer, 
  617.                       (size_t) size_for_new_buffer);
  618.             new_mem_seg->in_use = size_for_new_buffer;
  619.  
  620.             net_cache_adding_object++; /* semaphore */
  621.             XP_ListAddObjectToEnd(obj->memory_copy->list, new_mem_seg);
  622.             net_cache_adding_object--; /* semaphore */
  623.  
  624.             TRACEMSG(("Adding %d to New memory segment %p", len, new_mem_seg));
  625.           }
  626.         else
  627.           {
  628.               assert (mem_seg->segment);
  629.             cur_mem_seg_ptr = mem_seg->segment;
  630.  
  631.             /* fill in some more into the existing segment
  632.              */
  633.             XP_MEMCPY(cur_mem_seg_ptr + mem_seg->in_use, buffer, (size_t) len);
  634.             mem_seg->in_use += len;
  635.  
  636.             TRACEMSG(("Adding %d to existing memory segment %p", len, mem_seg));
  637.           }
  638.  
  639.       }
  640.  
  641. EndOfMemWrite:  /* target of a goto from an error above */
  642.  
  643.     if(obj->next_stream)
  644.       {
  645.         int status=0;
  646.     
  647.         XP_ASSERT (buffer);
  648.         XP_ASSERT (len > -1);
  649.         status = (*obj->next_stream->put_block)
  650.                                     (obj->next_stream, buffer, len);
  651.  
  652.         /* abort */
  653.         if(status < 0)
  654.             return(status);
  655.       }
  656.  
  657.     return(1);
  658. }
  659.  
  660. /*  complete the stream
  661. */
  662. PRIVATE void net_MemCacheComplete (NET_StreamClass *stream)
  663. {
  664.     CacheDataObject *obj=stream->data_object;    
  665.     /* refresh these entries in case meta changes them */
  666.     if(obj->URL_s && obj->memory_copy)
  667.       {
  668.         obj->memory_copy->cache_obj.expires       = obj->URL_s->expires;
  669.         obj->memory_copy->cache_obj.last_modified = obj->URL_s->last_modified;
  670.         StrAllocCopy(obj->memory_copy->cache_obj.charset, obj->URL_s->charset);
  671.       }
  672.  
  673.     if (obj->memory_copy)
  674.     {
  675.         /* now it's completed */
  676.         obj->memory_copy->completed = TRUE;
  677.         
  678.         /* if the object is zero size or 
  679.           * if the computed size is different that a given content type,
  680.           * zero the last modified date so that it wont get reused
  681.           * on a recheck or reload
  682.           */
  683.         if(!obj->computed_content_length
  684.            ||  (obj->memory_copy->cache_obj.content_length
  685.                 && obj->memory_copy->cache_obj.content_length 
  686.                    != obj->computed_content_length
  687.                ) 
  688.           )
  689.             {
  690.             obj->memory_copy->cache_obj.last_modified = 0;
  691.             }
  692.  
  693.         /* override the given content_length with the correct
  694.          * content length
  695.          */
  696.         obj->memory_copy->cache_obj.content_length = 
  697.                                                 obj->computed_content_length;
  698.     
  699.          /* set current time */
  700.         obj->memory_copy->cache_obj.last_accessed = time(NULL); 
  701.     }
  702.  
  703.  
  704. /* loser: */
  705.     /* complete the next stream
  706.      */
  707.     if(obj->next_stream)
  708.       {
  709.         (*obj->next_stream->complete)(obj->next_stream);
  710.         FREE(obj->next_stream);
  711.       }
  712.  
  713.     /* finally, check to see if we want to delete this, after going
  714.        through all that work just to put it in here.  */
  715.     if(obj->memory_copy &&
  716.         obj->memory_copy->delete_me && 
  717.         (obj->memory_copy->mem_read_lock == 0))
  718.         {
  719.           net_FreeMemoryCopy(obj->memory_copy);
  720.         }
  721.  
  722.  
  723.     /* don't need the data obj any more since this is the end 
  724.      * of the stream... */
  725.     PR_REMOVE_LINK(&obj->links);
  726.     FREE(obj);
  727.  
  728.     /* this is what keeps the cache from growing without end */
  729.     net_ReduceMemoryCacheTo(net_MaxMemoryCacheSize);
  730.  
  731.     return;
  732. }
  733.  
  734. /*  add the object into the cache, even if it hasn't been finished
  735.     getting cached.  When it's finished, net_MemCacheComplete will
  736.     set the obj->memory_copy->comlpleted flag.
  737. */
  738. PRIVATE void net_MemCacheAddObjectToCache (CacheDataObject * obj)
  739. {
  740.     int status;
  741.  
  742.     /* if the hash table doesn't exist yet, initialize it now 
  743.      */
  744.     if(!net_MemoryCacheHashList)
  745.       {
  746.  
  747.         net_MemoryCacheHashList = XP_HashListNew(499, 
  748.                                     (XP_HashingFunction) net_CacheHashFunc,
  749.                                     (XP_HashCompFunction) net_CacheHashComp); 
  750.         if(!net_MemoryCacheHashList)
  751.           {
  752.             net_FreeMemoryCopy(obj->memory_copy);
  753.             goto loser;
  754.             return;
  755.           }
  756.  
  757.       }
  758.  
  759.     /* if the delete list doesn't exist yet, initialize it now 
  760.      */
  761.     if(!net_MemoryCacheList)
  762.       {
  763.         net_MemoryCacheList = XP_ListNew();
  764.         if(!net_MemoryCacheList)
  765.           {
  766.             net_FreeMemoryCopy(obj->memory_copy);
  767.             goto loser;
  768.             return;
  769.           }
  770.       }
  771.  
  772.     /* memory copy could have been free'd and clear if an
  773.       * error occured in the write, so check to make sure
  774.      * it's still around.
  775.      */
  776.     if(obj->memory_copy)
  777.       {
  778.         /* set the completed flag so that we know we're in
  779.          * the process of caching this object.
  780.          */
  781.         obj->memory_copy->completed = FALSE;
  782.         obj->memory_copy->aborted = FALSE;
  783.  
  784.         /* if the document requested not to be cached, 
  785.          * set the flag to only serve it for reloads and such
  786.          */
  787.         if(obj->URL_s && obj->URL_s->dont_cache)
  788.             obj->memory_copy->current_page_only = TRUE;
  789.         
  790.         /* add the struct to the delete list */
  791.         net_cache_adding_object++; /* semaphore */
  792.         XP_ListAddObject(net_MemoryCacheList, obj->memory_copy);
  793.         net_cache_adding_object--; /* semaphore */
  794.  
  795.         /* add the struct to the hash list */
  796.         net_cache_adding_object++; /* semaphore */
  797.         status = XP_HashListAddObject(net_MemoryCacheHashList, 
  798.                                       obj->memory_copy);
  799.         net_cache_adding_object--; /* semaphore */
  800.  
  801.         /* check for hash collision */
  802.         if(status == XP_HASH_DUPLICATE_OBJECT)
  803.           {
  804.             net_MemoryCacheObject * tmp_obj;
  805.  
  806.             tmp_obj = (net_MemoryCacheObject *)
  807.                                   XP_HashListFindObject(net_MemoryCacheHashList, 
  808.                                                       obj->memory_copy);
  809.  
  810.             if ((tmp_obj->mem_read_lock == 0) && tmp_obj->completed)
  811.             {
  812.                 /* If there is nobody currently reading or writing the
  813.                  * duplicate hash entry, then free the old copy.
  814.                  * This will remove it from the hash list, also.
  815.                  */
  816.                 net_FreeMemoryCopy(tmp_obj);
  817.    
  818.                 TRACEMSG(("Found duplicate object in cache.  Removing old object"));
  819.     
  820.                 /* re add the object 
  821.                  */
  822.                 net_cache_adding_object++; /* semaphore */
  823.                 XP_HashListAddObject(net_MemoryCacheHashList, 
  824.                                      obj->memory_copy);
  825.                 net_cache_adding_object--; /* semaphore */
  826.             }
  827.             else
  828.             {
  829.                 /* We can't cache this entry, the duplicate is
  830.                  * currently being read or written.  So delete this entry.
  831.                  */
  832.                 net_FreeMemoryCopy(obj->memory_copy);
  833.             }
  834.           }
  835.       }
  836.  
  837. loser:
  838.  
  839.     return;
  840. }
  841.  
  842. /* Abort the stream 
  843.  * 
  844.  */
  845. PRIVATE void net_MemCacheAbort (NET_StreamClass *stream, int status)
  846. {
  847.     CacheDataObject *obj=stream->data_object;    
  848.     /* abort the next stream
  849.      */
  850.     if(obj->next_stream)
  851.       {
  852.         (*obj->next_stream->abort)(obj->next_stream, status);
  853.         FREE(obj->next_stream);
  854.       }
  855.  
  856.     /* set the aborted flag so that the object will really be deleted
  857.      * if there are no read locks. */
  858.     obj->memory_copy->aborted = TRUE;
  859.     net_FreeMemoryCopy(obj->memory_copy);
  860.  
  861.     PR_REMOVE_LINK(&obj->links);
  862.     FREE(obj);
  863.  
  864.     return;
  865. }
  866.  
  867. /* setup the stream
  868.  */
  869. MODULE_PRIVATE NET_StreamClass * 
  870. NET_MemCacheConverter (FO_Present_Types format_out,
  871.                     void       *converter_obj,
  872.                     URL_Struct *URL_s,
  873.                     MWContext  *window_id)
  874. {
  875.     CacheDataObject * data_object=0;
  876.     NET_StreamClass * stream=0;
  877.     net_MemorySegment * mem_seg=0;
  878.     net_MemoryCacheObject *memory_copy=0;
  879.     net_MemCacheConverterObject *mem_conv_obj = converter_obj;
  880.     
  881.     TRACEMSG(("Setting up cache stream. Have URL: %s\n", URL_s->address));
  882.  
  883.     TRACEMSG(("Setting up memory cache"));
  884.  
  885.     /* check to see if the object is larger than the
  886.      * size of the max memory cache 
  887.      *
  888.      * skip the size check if the must_cache flag is set.
  889.      */
  890.     if(!URL_s->must_cache && (uint32)URL_s->content_length >= net_MaxMemoryCacheSize)
  891.         goto malloc_failure; /* dont cache this URL */
  892.  
  893.     /* set up the memory caching struct.
  894.       *
  895.      * malloc the segment first since it's the most likely malloc to fail
  896.      */
  897.     mem_seg = XP_NEW(net_MemorySegment);
  898.  
  899.     if(!mem_seg)
  900.         return(NULL);
  901.  
  902.     mem_seg->in_use = 0;
  903.  
  904.     if(URL_s->content_length > 0)
  905.       {
  906.         /* use the content_length if we can since that would be
  907.          * the most efficient 
  908.          */
  909.         if(URL_s->content_length > MAX_MEMORY_ALLOC_SIZE)
  910.           {
  911.             mem_seg->segment =  (char*)XP_ALLOC(MAX_MEMORY_ALLOC_SIZE);
  912.             mem_seg->seg_size = MAX_MEMORY_ALLOC_SIZE;
  913.           }
  914.         else
  915.           {
  916.             /* add 10 just in case it's needed due to a bad content type :) */
  917.             mem_seg->segment =  (char*)XP_ALLOC(URL_s->content_length+10);
  918.             mem_seg->seg_size = URL_s->content_length+10;
  919.           }
  920.       }
  921.     else
  922.       {
  923.         /* this is a case of no content length, use standard size segments */
  924.         mem_seg->segment  = (char*)XP_ALLOC(MEMORY_CACHE_SEGMENT_SIZE);
  925.         mem_seg->seg_size = MEMORY_CACHE_SEGMENT_SIZE;
  926.       }
  927.  
  928.     if(!mem_seg->segment)
  929.       {
  930.         FREE(mem_seg);
  931.         goto malloc_failure;  /* skip memory cacheing */
  932.       }
  933.  
  934.     /* malloc the main cache holding structure */
  935.     memory_copy = XP_NEW(net_MemoryCacheObject);
  936.     if(!memory_copy)
  937.       {
  938.         FREE(mem_seg);
  939.         FREE(mem_seg->segment);
  940.         goto malloc_failure;  /* skip memory cacheing */
  941.       }
  942.     memset(memory_copy, 0, sizeof(net_MemoryCacheObject));
  943.  
  944.     memory_copy->list = XP_ListNew();
  945.     if(!memory_copy->list)
  946.       {
  947.         FREE(mem_seg);
  948.         FREE(mem_seg->segment);
  949.         FREE(memory_copy);
  950.         goto malloc_failure;  /* skip memory cacheing */
  951.       }
  952.  
  953.     /* if we get this far add the object to the size count
  954.      * since if we fail anywhere below here it will get
  955.      * subtracted by net_FreeMemoryCopy(memory_copy);
  956.      */
  957.     net_MemoryCacheSize += mem_seg->seg_size;
  958.  
  959.     /* add the segment malloced above to the segment list */
  960.     net_cache_adding_object++; /* semaphore */
  961.     XP_ListAddObject(memory_copy->list, mem_seg);
  962.     net_cache_adding_object--; /* semaphore */
  963.  
  964.     StrAllocCopy(memory_copy->cache_obj.address, URL_s->address);
  965.     memory_copy->cache_obj.method = URL_s->method;
  966.     if(URL_s->post_data)
  967.       {
  968.         memory_copy->cache_obj.post_data_size = URL_s->post_data_size;
  969.         StrAllocCopy(memory_copy->cache_obj.post_data, URL_s->post_data);
  970.         StrAllocCopy(memory_copy->cache_obj.post_headers, URL_s->post_headers);
  971.       }
  972.  
  973.     memory_copy->cache_obj.expires        = URL_s->expires;
  974.     memory_copy->cache_obj.last_modified  = URL_s->last_modified;
  975.    
  976.     memory_copy->cache_obj.content_length      = URL_s->content_length;
  977.     memory_copy->cache_obj.real_content_length = URL_s->content_length;
  978.     memory_copy->cache_obj.is_netsite     = URL_s->is_netsite;
  979.  
  980.     StrAllocCopy(memory_copy->cache_obj.content_type, URL_s->content_type);
  981.     StrAllocCopy(memory_copy->cache_obj.charset, URL_s->charset);
  982.     StrAllocCopy(memory_copy->cache_obj.content_encoding, 
  983.                  URL_s->content_encoding);
  984.  
  985.     /* check for malloc failure on critical fields above */
  986.     if(!memory_copy->cache_obj.address || !memory_copy->cache_obj.content_type)
  987.       {
  988.         net_FreeMemoryCopy(memory_copy);
  989.         goto malloc_failure;  /* skip memory cacheing */
  990.       }
  991.     
  992.     /* copy security info */
  993.     memory_copy->cache_obj.security_on             = URL_s->security_on;
  994.     StrAllocCopy(memory_copy->cache_obj.key_cipher,  URL_s->key_cipher);
  995.     memory_copy->cache_obj.key_size                = URL_s->key_size;
  996.     memory_copy->cache_obj.key_secret_size         = URL_s->key_secret_size;
  997.     if (URL_s->certificate)
  998.         memory_copy->cache_obj.certificate = CERT_DupCertificate(URL_s->certificate);
  999.  
  1000.     /* build the stream object */
  1001.     stream = XP_NEW(NET_StreamClass);
  1002.     if(!stream)
  1003.       {
  1004.         net_FreeMemoryCopy(memory_copy);
  1005.         goto malloc_failure;  /* skip memory cacheing */
  1006.       }
  1007.  
  1008.     /* this structure gets passed back into the write, complete
  1009.      * and abort stream functions
  1010.      */
  1011.     data_object = XP_NEW(CacheDataObject);
  1012.     if (!data_object)
  1013.       {
  1014.         FREE(stream);
  1015.         net_FreeMemoryCopy(memory_copy);
  1016.         goto malloc_failure;  /* skip memory cacheing */
  1017.       }
  1018.  
  1019.     /* init the object */
  1020.     XP_MEMSET(data_object, 0, sizeof(CacheDataObject));
  1021.  
  1022.     /* assign the cache object to the stream data object
  1023.      */
  1024.     data_object->memory_copy = memory_copy;
  1025.  
  1026.     if (mem_conv_obj->dont_hold_URL_s == FALSE)
  1027.         data_object->URL_s = URL_s;
  1028.  
  1029.     TRACEMSG(("Returning stream from NET_CacheConverter\n"));
  1030.  
  1031.     stream->name           = "Cache stream";
  1032.     stream->complete       = (MKStreamCompleteFunc) net_MemCacheComplete;
  1033.     stream->abort          = (MKStreamAbortFunc) net_MemCacheAbort;
  1034.     stream->put_block      = (MKStreamWriteFunc) net_MemCacheWrite;
  1035.     stream->is_write_ready = (MKStreamWriteReadyFunc) net_MemCacheWriteReady;
  1036.     stream->data_object    = data_object;  /* document info object */
  1037.     stream->window_id      = window_id;
  1038.  
  1039.     /* next stream is passed in by the caller but can be null */
  1040.     data_object->next_stream = mem_conv_obj->next_stream;
  1041.  
  1042.     PR_APPEND_LINK(&data_object->links, &mem_active_cache_data_objects);
  1043.  
  1044.     /* add the cache object to the hash list, but don't
  1045.      * complete it yet
  1046.      */
  1047.        net_MemCacheAddObjectToCache(data_object);
  1048.  
  1049.     return stream;
  1050.  
  1051. malloc_failure: /* target of malloc failure */
  1052.  
  1053.     TRACEMSG(("NOT Caching this URL"));
  1054.  
  1055.     /* bypass the whole cache mechanism
  1056.      */
  1057.     if(format_out != FO_CACHE_ONLY)
  1058.       {
  1059.         format_out = CLEAR_CACHE_BIT(format_out);
  1060.         return(mem_conv_obj->next_stream);
  1061.       }
  1062.  
  1063.     return(NULL);
  1064. }
  1065.  
  1066. PRIVATE net_MemoryCacheObject *
  1067. net_FindObjectInMemoryCache(URL_Struct *URL_s)
  1068. {
  1069.     net_MemoryCacheObject tmp_cache_obj, *return_obj;
  1070.  
  1071.     /* fill in the temporary cache object so we can
  1072.      * use it for searching
  1073.      */
  1074.     XP_MEMSET(&tmp_cache_obj, 0, sizeof(tmp_cache_obj));
  1075.     tmp_cache_obj.cache_obj.method    = URL_s->method;
  1076.     tmp_cache_obj.cache_obj.address   = URL_s->address;
  1077.     tmp_cache_obj.cache_obj.post_data = URL_s->post_data;
  1078.     tmp_cache_obj.cache_obj.post_data_size = URL_s->post_data_size;
  1079.  
  1080.     return_obj = (net_MemoryCacheObject *)
  1081.         XP_HashListFindObject(net_MemoryCacheHashList, &tmp_cache_obj);
  1082.     if (return_obj)
  1083.         return (return_obj);
  1084.     else 
  1085.     {
  1086.         /*    try unescaping the URL address - MHTML parts are
  1087.             escaped, and weren't getting picked up in the
  1088.             cache */
  1089.         tmp_cache_obj.cache_obj.address = XP_STRDUP(URL_s->address);
  1090.         if (tmp_cache_obj.cache_obj.address)
  1091.         {
  1092.             /* unescape it */
  1093.             NET_UnEscape(tmp_cache_obj.cache_obj.address);
  1094.             if (tmp_cache_obj.cache_obj.address)
  1095.             {
  1096.                 return_obj = (net_MemoryCacheObject *)
  1097.                     XP_HashListFindObject(net_MemoryCacheHashList, &tmp_cache_obj);
  1098.                 XP_FREE(tmp_cache_obj.cache_obj.address);
  1099.             }
  1100.         }
  1101.         return (return_obj);
  1102.     }
  1103. }
  1104.  
  1105. /* set or unset a lock on a memory cache object
  1106.  */
  1107. MODULE_PRIVATE void
  1108. NET_ChangeMemCacheLock(URL_Struct *URL_s, Bool set)
  1109. {
  1110.     net_MemoryCacheObject * found_cache_obj;
  1111.  
  1112.     /* look up cache struct */
  1113.     found_cache_obj = net_FindObjectInMemoryCache(URL_s);
  1114.  
  1115.     if(found_cache_obj && found_cache_obj->completed)
  1116.       {
  1117.         XP_ASSERT(found_cache_obj->external_locks >= 0);
  1118.  
  1119.         if(set)
  1120.           {
  1121.             /* increment lock counter */
  1122.             found_cache_obj->external_locks++;
  1123.           }
  1124.         else
  1125.           {
  1126.             /* decrement lock counter */
  1127.             found_cache_obj->external_locks--;
  1128.  
  1129.             XP_ASSERT(found_cache_obj->external_locks >= 0);
  1130.  
  1131.             if(found_cache_obj->external_locks < 0)
  1132.                 found_cache_obj->external_locks = 0;
  1133.           }
  1134.       }
  1135. }
  1136.  
  1137. /* remove a URL from the memory cache
  1138.  */
  1139. MODULE_PRIVATE void
  1140. NET_RemoveURLFromMemCache(URL_Struct *URL_s)
  1141. {
  1142.     net_MemoryCacheObject * found_cache_obj;
  1143.  
  1144.     found_cache_obj = net_FindObjectInMemoryCache(URL_s);
  1145.  
  1146.     if(found_cache_obj &&
  1147.         (found_cache_obj->mem_read_lock == 0) &&
  1148.         found_cache_obj->completed)
  1149.         net_FreeMemoryCopy(found_cache_obj);
  1150. }
  1151.  
  1152. /* Returns TRUE if the URL is currently in the
  1153.  * memory cache and false otherwise.
  1154.  */
  1155. PUBLIC Bool
  1156. NET_IsURLInMemCache(URL_Struct *URL_s)
  1157. {
  1158.     if(net_FindObjectInMemoryCache(URL_s))
  1159.         return(TRUE);
  1160.     else
  1161.         return(FALSE);
  1162. }
  1163.  
  1164. /* this function looks up a URL in the hash table and
  1165.  * returns MEMORY_CACHE_TYPE_URL if it's found and
  1166.  * should be loaded via the memory cache.  
  1167.  *
  1168.  * It also sets several entries in the passed in URL
  1169.  * struct including "memory_copy" which is a pointer
  1170.  * to the found net_MemoryCacheObject struct.
  1171.  */
  1172. MODULE_PRIVATE int
  1173. NET_FindURLInMemCache(URL_Struct * URL_s, MWContext *ctxt)
  1174. {
  1175.     net_MemoryCacheObject *found_cache_obj;
  1176.  
  1177.     TRACEMSG(("Checking for URL in cache"));
  1178.  
  1179.     if(!net_MemoryCacheHashList)
  1180.         return(0);
  1181.  
  1182.     found_cache_obj = net_FindObjectInMemoryCache(URL_s);
  1183.  
  1184.     if(found_cache_obj)
  1185.       {
  1186.         TRACEMSG(("mkcache: found URL in memory cache!"));
  1187.  
  1188.         if(found_cache_obj->current_page_only)
  1189.           {
  1190.             History_entry * he = SHIST_GetCurrent(&ctxt->hist);
  1191.             int hist_num;
  1192.  
  1193.             /* if the current_page_only flag is set 
  1194.              * then the page should only be
  1195.              * served if it's the current document loaded
  1196.              * (i.e. reloads)
  1197.              *
  1198.              * @@@ if !he then let it through, this allows the
  1199.              *     doc info window to work
  1200.              */
  1201.             if(he)
  1202.               {
  1203.                 hist_num = SHIST_GetIndex(&ctxt->hist, he);
  1204.  
  1205.                 /* we can tell if the document being loaded is the
  1206.                   * same as the current document by looking up it's
  1207.                   * index in the history.  If they don't match
  1208.                   * then it isn't the current document
  1209.                   */
  1210.                 if(URL_s->history_num != hist_num)
  1211.                     return 0;
  1212.               }
  1213.           }
  1214.  
  1215.         /* set a pointer to the structure in the URL struct
  1216.          */
  1217.         URL_s->memory_copy = found_cache_obj;
  1218.  
  1219.         /* copy the contents of the URL struct so that the content type
  1220.          * and other stuff gets recorded
  1221.          */
  1222.         StrAllocCopy(URL_s->content_type,     
  1223.                      found_cache_obj->cache_obj.content_type);
  1224.         StrAllocCopy(URL_s->charset,          
  1225.                      found_cache_obj->cache_obj.charset);
  1226.         StrAllocCopy(URL_s->content_encoding, 
  1227.                      found_cache_obj->cache_obj.content_encoding);
  1228.         URL_s->content_length      = found_cache_obj->cache_obj.content_length;
  1229.         URL_s->real_content_length = found_cache_obj->cache_obj.content_length;
  1230.          URL_s->expires             = found_cache_obj->cache_obj.expires;
  1231.          URL_s->last_modified       = found_cache_obj->cache_obj.last_modified;
  1232.          URL_s->is_netsite          = found_cache_obj->cache_obj.is_netsite;
  1233.  
  1234.         /* copy security info */
  1235.         URL_s->security_on         = found_cache_obj->cache_obj.security_on;
  1236.         URL_s->key_size            = found_cache_obj->cache_obj.key_size;
  1237.         URL_s->key_secret_size     = found_cache_obj->cache_obj.key_secret_size;
  1238.         StrAllocCopy(URL_s->key_cipher, found_cache_obj->cache_obj.key_cipher);
  1239.  
  1240.         /* remove any existing certificate */
  1241.         CERT_DestroyCertificate(URL_s->certificate);
  1242.         URL_s->certificate = NULL;
  1243.         
  1244.         if (found_cache_obj->cache_obj.certificate)
  1245.           URL_s->certificate =
  1246.             CERT_DupCertificate(found_cache_obj->cache_obj.certificate);
  1247.  
  1248.         net_cache_adding_object++; /* semaphore */
  1249.         /* reorder objects so that the list is in last accessed order */
  1250.         XP_ListRemoveObject(net_MemoryCacheList, found_cache_obj);
  1251.         XP_ListAddObject(net_MemoryCacheList, found_cache_obj);
  1252.         net_cache_adding_object--; /* semaphore */
  1253.  
  1254.         TRACEMSG(("Cached copy is valid. returning method"));
  1255.  
  1256.         return(MEMORY_CACHE_TYPE_URL);
  1257.       }
  1258.  
  1259.     TRACEMSG(("URL not found in cache"));
  1260.  
  1261.     return(0);
  1262. }
  1263.  
  1264. /*****************************************************************
  1265.  *  Memory cache output module routine
  1266.  *
  1267.  * This set of routines pushes the document in memory up a stream
  1268.  * created by NET_StreamBuilder
  1269.  */
  1270.  
  1271. /* used to hold data between invokations of ProcessNet
  1272.  */
  1273. typedef struct _MemCacheConData {
  1274.     XP_List         *cur_list_ptr;
  1275.     uint32           bytes_written_in_segment;
  1276.     NET_StreamClass *stream;
  1277. } MemCacheConData;
  1278.  
  1279. #define CD_CUR_LIST_PTR  connection_data->cur_list_ptr
  1280. #define CD_BYTES_WRITTEN_IN_SEGMENT connection_data->bytes_written_in_segment
  1281. #define CD_STREAM        connection_data->stream
  1282.  
  1283. #define CE_URL_S          cur_entry->URL_s
  1284. #define CE_WINDOW_ID      cur_entry->window_id
  1285. #define CE_FORMAT_OUT     cur_entry->format_out
  1286. #define CE_STATUS         cur_entry->status
  1287. #define CE_BYTES_RECEIVED cur_entry->bytes_received
  1288.  
  1289. /* begin the load, This is called from NET_GetURL
  1290.  */
  1291. #ifdef MOZ_MAIL_NEWS
  1292. extern int
  1293. net_InitializeNewsFeData (ActiveEntry * cur_entry);
  1294. extern int
  1295. IMAP_InitializeImapFeData (ActiveEntry * cur_entry);
  1296. extern void
  1297. IMAP_URLFinished(URL_Struct *URL_s);
  1298. #endif /* MOZ_MAIL_NEWS */
  1299.  
  1300. PRIVATE int32
  1301. net_MemoryCacheLoad (ActiveEntry * cur_entry)
  1302. {
  1303.     /* get memory for Connection Data */
  1304.     MemCacheConData * connection_data = XP_NEW(MemCacheConData);
  1305.     net_MemorySegment * mem_seg;
  1306.     uint32  chunk_size;
  1307.     char   *mem_seg_ptr;
  1308.     char   *first_buffer;
  1309.  
  1310.     TRACEMSG(("Entering NET_MemoryCacheLoad!\n"));
  1311.  
  1312.     if(!connection_data)
  1313.       {
  1314.         CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
  1315.         CE_STATUS = MK_OUT_OF_MEMORY;
  1316.         return (CE_STATUS);
  1317.       }
  1318.  
  1319.     if (!CE_URL_S->memory_copy)
  1320.     {
  1321.         /* the memory_copy has been freed before fully
  1322.          * making it into the cache.  In other words, it's
  1323.          * been aborted.  Abort this load then, also.
  1324.          * We should probably never hit this case, however.
  1325.          * If we got here, it means we've just found this entry
  1326.          * in the hash table, which means we have not deleted it
  1327.          * and we have not set the delete_me flag.
  1328.          */
  1329.         XP_ASSERT(FALSE);
  1330.         CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
  1331.         return (CE_STATUS);
  1332.     }
  1333.  
  1334.     cur_entry->protocol = MEMORY_CACHE_TYPE_URL;
  1335.     cur_entry->memory_file = TRUE;
  1336.  
  1337.     /* point to the first list struct that contains data
  1338.      */
  1339.     CD_CUR_LIST_PTR = CE_URL_S->memory_copy->list->next;
  1340.     CD_BYTES_WRITTEN_IN_SEGMENT = 0;
  1341.  
  1342.     /* put a read lock on the data
  1343.      */
  1344.     CE_URL_S->memory_copy->mem_read_lock++;
  1345.  
  1346.     cur_entry->con_data = connection_data;
  1347.  
  1348.     cur_entry->local_file = TRUE;
  1349.  
  1350.     cur_entry->socket = NULL;
  1351.  
  1352.     NET_SetCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
  1353.  
  1354.     cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);
  1355.     
  1356.     FE_EnableClicking(CE_WINDOW_ID);
  1357.  
  1358. #ifdef MOZ_MAIL_NEWS    
  1359.     if (cur_entry->format_out == FO_PRESENT)
  1360.     {
  1361.       if (NET_URL_Type(cur_entry->URL_s->address) == NEWS_TYPE_URL)
  1362.       {
  1363.         /* #### DISGUSTING KLUDGE to make cacheing work for news articles. */
  1364.         cur_entry->status = net_InitializeNewsFeData (cur_entry);
  1365.         if (cur_entry->status < 0)
  1366.           {
  1367.             /* #### what error message? */
  1368.             return cur_entry->status;
  1369.           }
  1370.       }
  1371.       else if (!XP_STRNCMP(CE_URL_S->address, "Mailbox://", 10))
  1372.       {
  1373.         /* #### DISGUSTING KLUDGE to make cacheing work for imap articles. */
  1374.         cur_entry->status = IMAP_InitializeImapFeData (cur_entry);
  1375.         if (cur_entry->status < 0)
  1376.           {
  1377.             /* #### what error message? */
  1378.             return cur_entry->status;
  1379.           }
  1380.       }
  1381.     }
  1382. #endif /* MOZ_MAIL_NEWS */      
  1383.  
  1384.     /* open the outgoing stream
  1385.      */
  1386.     CD_STREAM = NET_StreamBuilder(CE_FORMAT_OUT, CE_URL_S, CE_WINDOW_ID);
  1387.     if(!CD_STREAM)
  1388.       {
  1389.         NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
  1390.  
  1391.         FREE(connection_data);
  1392.  
  1393.         CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
  1394.         CE_STATUS = MK_UNABLE_TO_CONVERT;
  1395.         return (CE_STATUS);
  1396.       }
  1397.  
  1398.     if (!CE_URL_S->load_background)
  1399.         FE_GraphProgressInit(CE_WINDOW_ID, CE_URL_S, CE_URL_S->content_length);
  1400.  
  1401.     /* process one chunk of the
  1402.      * cache file so that
  1403.      * layout can continue
  1404.      * when images are in the cache
  1405.      */
  1406. #define FIRST_BUFF_SIZE 1024
  1407.     if (CE_URL_S->memory_copy->completed)
  1408.     {
  1409.         mem_seg = (net_MemorySegment *) CD_CUR_LIST_PTR->object;
  1410.  
  1411.         mem_seg_ptr = mem_seg->segment;
  1412.  
  1413.         chunk_size = MIN(FIRST_BUFF_SIZE,     
  1414.                          mem_seg->in_use-CD_BYTES_WRITTEN_IN_SEGMENT);
  1415.  
  1416.         /* malloc this first buffer because we can't use
  1417.           * the NET_SocketBuffer in calls from NET_GetURL 
  1418.          * because of reentrancy
  1419.          */
  1420.         first_buffer = (char *) XP_ALLOC(chunk_size);
  1421.  
  1422.         if(!first_buffer)
  1423.           {
  1424.             FREE(connection_data);
  1425.             CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
  1426.             return(MK_OUT_OF_MEMORY);
  1427.           }
  1428.  
  1429.         /* copy the segment because the parser will muck with it
  1430.          */
  1431.         XP_MEMCPY(first_buffer,
  1432.                   mem_seg_ptr+CD_BYTES_WRITTEN_IN_SEGMENT,
  1433.                   (size_t) chunk_size);
  1434.  
  1435.         CD_BYTES_WRITTEN_IN_SEGMENT += chunk_size;
  1436.  
  1437.         CE_STATUS = (*CD_STREAM->put_block)(CD_STREAM,
  1438.                                             first_buffer,
  1439.                                             chunk_size);
  1440.         if(CE_STATUS < 0)
  1441.           {
  1442.             NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
  1443.  
  1444.             if (!CE_URL_S->load_background)
  1445.                 FE_GraphProgressDestroy(CE_WINDOW_ID,
  1446.                                         CE_URL_S,
  1447.                                         CE_URL_S->content_length,
  1448.                                         CE_BYTES_RECEIVED);
  1449.  
  1450.             FREE(connection_data);
  1451.  
  1452.             return (CE_STATUS);
  1453.           }
  1454.  
  1455.         CE_BYTES_RECEIVED += chunk_size;
  1456.  
  1457.         /* check to see if we need to advance the pointer yet.
  1458.          * should hardly ever happen here since the first buffer
  1459.          * is so small
  1460.          */
  1461.         if(CD_BYTES_WRITTEN_IN_SEGMENT >= mem_seg->in_use)
  1462.           {
  1463.             CD_CUR_LIST_PTR = CD_CUR_LIST_PTR->next;
  1464.             CD_BYTES_WRITTEN_IN_SEGMENT = 0;
  1465.           }
  1466.  
  1467.         FREE(first_buffer);
  1468.     }
  1469.     else
  1470.     {
  1471.         CE_STATUS = 0;
  1472.     }
  1473.  
  1474.     return(CE_STATUS);
  1475.     
  1476. }
  1477.  
  1478. /* called repeatedly from NET_ProcessNet to push all the
  1479.  * data up the stream
  1480.  */
  1481. PRIVATE int32
  1482. net_ProcessMemoryCache (ActiveEntry * cur_entry)
  1483. {
  1484.     MemCacheConData * connection_data = (MemCacheConData *)cur_entry->con_data;
  1485.     net_MemorySegment * mem_seg;
  1486.     uint32  chunk_size;
  1487.     uint32  buffer_size;
  1488.     char  *mem_seg_ptr;
  1489.  
  1490.  
  1491.     if (!CE_URL_S->memory_copy)
  1492.     {
  1493.         /* the memory_copy has been freed before fully
  1494.          * making it into the cache.  In other words, it's
  1495.          * been aborted.  Abort this load then, also,
  1496.          * since it's a concurrent load.
  1497.          * We know that CE_URL_S->memory_copy will be NULL
  1498.          * if it's been removed, because we have a read lock
  1499.          * on the entry, and that will cause the delete_me
  1500.          * flag to be set, rather than just deleting the
  1501.          * object initially.  We catch the delete_me flag
  1502.          * here, and set CE_URL_S->memory_copy to NULL.
  1503.          */
  1504.         CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
  1505.         return (CE_STATUS);
  1506.     }
  1507.  
  1508.  
  1509.     /* wait until the object has been fully inserted into
  1510.      * the cache.
  1511.      */
  1512.     if (!CE_URL_S->memory_copy->completed &&
  1513.         !(CE_URL_S->memory_copy->delete_me &&
  1514.           CE_URL_S->memory_copy->aborted))
  1515.     {
  1516.         /* normal case - we are still waiting for it to complete */
  1517.         return 0;
  1518.     }
  1519.  
  1520.     if(!CD_CUR_LIST_PTR ||
  1521.         (CE_URL_S->memory_copy->aborted &&
  1522.           CE_URL_S->memory_copy->delete_me))
  1523.       {
  1524.         /* when CD_CUR_LIST_PTR turns NULL we are at the end
  1525.           * of the document.  Finish up the stream and exit.
  1526.          *
  1527.          * If the entry is aborted and the delete_me flag is set,
  1528.          * the stream was aborted in the middle of being written.
  1529.          * If we are here, then it's simultaneously being read also.
  1530.          * So, finish up.
  1531.          */
  1532.  
  1533.         /* complete the stream
  1534.          */
  1535.         (*CD_STREAM->complete)(CD_STREAM);
  1536.  
  1537.         /* free the stream
  1538.          */
  1539.         FREE(CD_STREAM);
  1540.  
  1541.         /* remove the read lock
  1542.          */
  1543.         CE_URL_S->memory_copy->mem_read_lock--;
  1544.  
  1545.         /* check to see if we should delete this memory cached object
  1546.          *
  1547.          * delete_me is set by FreeMemoryCacheObject when it tried
  1548.          * to free it but couldn't because of read locks on the
  1549.          * object
  1550.          */
  1551.         if(CE_URL_S->memory_copy->delete_me && 
  1552.                     CE_URL_S->memory_copy->mem_read_lock == 0)
  1553.           {
  1554.             net_FreeMemoryCopy(CE_URL_S->memory_copy);
  1555.             CE_URL_S->memory_copy = 0;
  1556.           }
  1557.  
  1558.         /* FREE the structs used by this protocol module
  1559.          */
  1560.         FREE(connection_data);
  1561.  
  1562.         /* set the status to success */
  1563.         if (CE_URL_S->memory_copy && CE_URL_S->memory_copy->completed)
  1564.             CE_STATUS = MK_DATA_LOADED;
  1565.         else
  1566.             CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
  1567.  
  1568.         /* clear the CallNetlibAllTheTime if there are no
  1569.          * other readers
  1570.          */
  1571.         NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
  1572.  
  1573.         if (!CE_URL_S->load_background)
  1574.             FE_GraphProgressDestroy(CE_WINDOW_ID,
  1575.                                     CE_URL_S,
  1576.                                     CE_URL_S->content_length,
  1577.                                     CE_BYTES_RECEIVED);
  1578.  
  1579. #ifdef MOZ_MAIL_NEWS
  1580.     if (!XP_STRNCMP(CE_URL_S->address, "Mailbox://", 10))
  1581.         /* #### DISGUSTING KLUDGE to make cacheing work for imap articles. */
  1582.         IMAP_URLFinished(CE_URL_S);
  1583. #endif
  1584.         /* Tell ProcessNet that we are all done */
  1585.         return(-1);
  1586.       }
  1587.  
  1588.     /* CD_CUR_LIST_PTR is pointing to the most current
  1589.      * memory segment list object
  1590.       */
  1591.     mem_seg = (net_MemorySegment *) CD_CUR_LIST_PTR->object;
  1592.  
  1593.     TRACEMSG(("ProcessMemoryCache: printing segment %p",mem_seg));
  1594.  
  1595.     mem_seg_ptr = mem_seg->segment;
  1596.  
  1597.     /* write out at least part of the buffer
  1598.      */
  1599.     buffer_size = (*CD_STREAM->is_write_ready)(CD_STREAM);
  1600.     buffer_size = MIN(buffer_size, (unsigned int) NET_Socket_Buffer_Size);
  1601.  
  1602.     /* make it ??? at the most
  1603.      * when coming out of the cache
  1604.      */
  1605.     buffer_size = MIN(buffer_size, NET_MEM_CACHE_OUTPUT_SIZE);
  1606.  
  1607.     chunk_size = MIN(buffer_size, mem_seg->in_use-CD_BYTES_WRITTEN_IN_SEGMENT);
  1608.  
  1609.     /* copy the segment because the parser will muck with it
  1610.      */
  1611.     XP_MEMCPY(NET_Socket_Buffer, 
  1612.               mem_seg_ptr+CD_BYTES_WRITTEN_IN_SEGMENT, 
  1613.               (size_t) chunk_size);
  1614.  
  1615.     /* remember how much of this segment we have written */
  1616.     CD_BYTES_WRITTEN_IN_SEGMENT += chunk_size;
  1617.  
  1618.     CE_STATUS = (*CD_STREAM->put_block)(CD_STREAM,
  1619.                                         NET_Socket_Buffer,
  1620.                                         chunk_size);
  1621.     CE_BYTES_RECEIVED += chunk_size;
  1622.  
  1623.     /* check to see if we need to advance the pointer yet
  1624.      */
  1625.     if(CD_BYTES_WRITTEN_IN_SEGMENT >= mem_seg->in_use)
  1626.       {
  1627.         CD_CUR_LIST_PTR = CD_CUR_LIST_PTR->next;
  1628.         CD_BYTES_WRITTEN_IN_SEGMENT = 0;
  1629.       }
  1630.  
  1631.     if (!CE_URL_S->load_background)
  1632.         FE_GraphProgress(CE_WINDOW_ID, CE_URL_S, CE_BYTES_RECEIVED,
  1633.                          chunk_size, CE_URL_S->content_length);
  1634.  
  1635.     if(CE_STATUS < 0)
  1636.       {
  1637.         NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
  1638.  
  1639.         (*CD_STREAM->abort)(CD_STREAM, CE_STATUS);
  1640.       }
  1641.  
  1642.     return(CE_STATUS);
  1643.  
  1644. }
  1645.  
  1646. /* called by functions in mkgeturl to interrupt the loading of
  1647.  * an object.  (Usually a user interrupt) 
  1648.  */
  1649. PRIVATE int32
  1650. net_InterruptMemoryCache (ActiveEntry * cur_entry)
  1651. {
  1652.     MemCacheConData * connection_data = (MemCacheConData *)cur_entry->con_data;
  1653.  
  1654.     /* abort and free the outgoing stream
  1655.      */
  1656.     (*CD_STREAM->abort)(CD_STREAM, MK_INTERRUPTED);
  1657.     FREE(CD_STREAM);
  1658.  
  1659.     if (!CE_URL_S->memory_copy)
  1660.     {
  1661.         /* the memory_copy has been freed before fully
  1662.          * making it into the cache.  In other words, it's
  1663.          * been aborted.  Abort this load then, also,
  1664.          * since it's a concurrent load.
  1665.          */
  1666.         CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
  1667.         return (CE_STATUS);
  1668.     }
  1669.     
  1670.     /* remove the read lock
  1671.      */
  1672.     CE_URL_S->memory_copy->mem_read_lock--;
  1673.  
  1674.     /* check to see if we should delete this memory cached object
  1675.      */
  1676.     if(CE_URL_S->memory_copy->delete_me && 
  1677.             CE_URL_S->memory_copy->mem_read_lock == 0)
  1678.       {
  1679.         net_FreeMemoryCopy(CE_URL_S->memory_copy);
  1680.         CE_URL_S->memory_copy = 0;
  1681.       }
  1682.  
  1683.     /* FREE the structs
  1684.      */
  1685.     FREE(connection_data);
  1686.     CE_STATUS = MK_INTERRUPTED;
  1687.  
  1688.     NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
  1689.  
  1690.     return(CE_STATUS);
  1691. }
  1692.  
  1693. /* create an HTML stream and push a bunch of HTML about
  1694.  * the memory cache
  1695.  */
  1696. MODULE_PRIVATE void 
  1697. NET_DisplayMemCacheInfoAsHTML(ActiveEntry * cur_entry)
  1698. {
  1699.     char *buffer = (char*)XP_ALLOC(2048);
  1700.     char *address;
  1701.     char *escaped;
  1702.        NET_StreamClass * stream;
  1703.     net_CacheObject * cache_obj;
  1704.     net_MemoryCacheObject * mem_cache_obj;
  1705.     Bool long_form = FALSE;
  1706.     int32 number_in_memory_cache;
  1707.     XP_List *list_ptr;
  1708.     int i;
  1709.  
  1710.     if(!buffer)
  1711.       {
  1712.         cur_entry->status = MK_UNABLE_TO_CONVERT;
  1713.         return;
  1714.       }
  1715.  
  1716.     if(strcasestr(cur_entry->URL_s->address, "?long"))
  1717.         long_form = TRUE;
  1718.     else if(strcasestr(cur_entry->URL_s->address, "?traceon"))
  1719.         NET_CacheTraceOn = TRUE;
  1720.     else if(strcasestr(cur_entry->URL_s->address, "?traceoff"))
  1721.         NET_CacheTraceOn = FALSE;
  1722.  
  1723.     StrAllocCopy(cur_entry->URL_s->content_type, TEXT_HTML);
  1724.  
  1725.     cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);
  1726.     stream = NET_StreamBuilder(cur_entry->format_out, 
  1727.                                cur_entry->URL_s, 
  1728.                                cur_entry->window_id);
  1729.  
  1730.     if(!stream)
  1731.       {
  1732.         cur_entry->status = MK_UNABLE_TO_CONVERT;
  1733.         FREE(buffer);
  1734.         return;
  1735.       }
  1736.  
  1737.     /* define a macro to push a string up the stream
  1738.      * and handle errors
  1739.      */
  1740. #define PUT_PART(part)                                                    \
  1741. cur_entry->status = (*stream->put_block)(stream,            \
  1742.                                         part ? part : "Unknown",        \
  1743.                                         part ? XP_STRLEN(part) : 7);    \
  1744. if(cur_entry->status < 0)                                                \
  1745.   goto END;
  1746.  
  1747.     if(!net_MemoryCacheList)
  1748.       {
  1749.         XP_STRCPY(buffer, "There are no objects in the memory cache");
  1750.         PUT_PART(buffer);
  1751.         goto END;
  1752.       }
  1753.  
  1754.     number_in_memory_cache = XP_ListCount(net_MemoryCacheList);
  1755.  
  1756.     /* add the header info */
  1757.     XP_SPRINTF(buffer, 
  1758. "<TITLE>Information about the Netscape memory cache</TITLE>\n"
  1759. "<h2>Memory Cache statistics</h2>\n"
  1760. "<TABLE>\n"
  1761. "<TR>\n"
  1762. "<TD ALIGN=RIGHT><b>Maximum size:</TD>\n"
  1763. "<TD>%ld</TD>\n"
  1764. "</TR>\n"
  1765. "<TR>\n"
  1766. "<TD ALIGN=RIGHT><b>Current size:</TD>\n"
  1767. "<TD>%ld</TD>\n"
  1768. "</TR>\n"
  1769. "<TR>\n"
  1770. "<TD ALIGN=RIGHT><b>Number of files in cache:</TD>\n"
  1771. "<TD>%ld</TD>\n"
  1772. "</TR>\n"
  1773. "<TR>\n"
  1774. "<TD ALIGN=RIGHT><b>Average cache file size:</TD>\n"
  1775. "<TD>%ld</TD>\n"
  1776. "</TR>\n"
  1777. "</TABLE>\n"
  1778. "<HR>",
  1779. net_MaxMemoryCacheSize,
  1780. net_MemoryCacheSize,
  1781. number_in_memory_cache,
  1782. number_in_memory_cache ? net_MemoryCacheSize/number_in_memory_cache : 0);
  1783.  
  1784.     PUT_PART(buffer);
  1785.  
  1786.     /* define some macros to help us output HTML tables
  1787.      */
  1788. #if 0
  1789.  
  1790. #define TABLE_TOP(arg1)                \
  1791.     XP_SPRINTF(buffer,                 \
  1792. "<TR><TD ALIGN=RIGHT><b>%s</TD>\n"    \
  1793. "<TD>", arg1);                        \
  1794. PUT_PART(buffer);
  1795.  
  1796. #define TABLE_BOTTOM                \
  1797.     XP_SPRINTF(buffer,                 \
  1798. "</TD></TR>");                        \
  1799. PUT_PART(buffer);
  1800.  
  1801. #else
  1802.  
  1803. #define TABLE_TOP(arg1)                    \
  1804.     XP_STRCPY(buffer, "<tt>");            \
  1805.     for(i=XP_STRLEN(arg1); i < 16; i++)    \
  1806.         XP_STRCAT(buffer, " ");    \
  1807.     XP_STRCAT(buffer, arg1);            \
  1808.     XP_STRCAT(buffer, " </tt>");        \
  1809.     PUT_PART(buffer);
  1810.  
  1811. #define TABLE_BOTTOM                    \
  1812.     XP_STRCPY(buffer, "<BR>\n");        \
  1813.     PUT_PART(buffer);
  1814.  
  1815. #endif
  1816.  
  1817.     list_ptr = net_MemoryCacheList;
  1818.  
  1819.     while((mem_cache_obj = (net_MemoryCacheObject *) XP_ListNextObject(list_ptr)) != NULL)
  1820.       {
  1821.  
  1822.         cache_obj = &mem_cache_obj->cache_obj;
  1823.         address = XP_STRDUP(mem_cache_obj->cache_obj.address);
  1824.  
  1825.         /* put the URL out there */
  1826.         TABLE_TOP("URL:");
  1827.         XP_STRCPY(buffer, "<A TARGET=Internal_URL_Info HREF=about:");
  1828.         PUT_PART(buffer);
  1829.         PUT_PART(address);
  1830.         XP_STRCPY(buffer, ">");
  1831.         PUT_PART(buffer);
  1832.         escaped = NET_EscapeHTML(address);
  1833.         PUT_PART(escaped);
  1834.         FREE(address);
  1835.         FREE(escaped);
  1836.         XP_STRCPY(buffer, "</A>");
  1837.         PUT_PART(buffer);
  1838.         TABLE_BOTTOM;
  1839.  
  1840.         TABLE_TOP("Content Length:");
  1841.         XP_SPRINTF(buffer, "%lu", cache_obj->content_length);
  1842.         PUT_PART(buffer);
  1843.         TABLE_BOTTOM;
  1844.  
  1845.         TABLE_TOP("Content type:");
  1846.         PUT_PART(cache_obj->content_type);
  1847.         TABLE_BOTTOM;
  1848.  
  1849.         TABLE_TOP("Last Modified:");
  1850.         if(cache_obj->last_modified)
  1851.           {
  1852.             PUT_PART(ctime(&cache_obj->last_modified));
  1853.           }
  1854.         else
  1855.           {
  1856.             XP_STRCPY(buffer, "No date sent");
  1857.             PUT_PART(buffer);
  1858.           }
  1859.         TABLE_BOTTOM;
  1860.  
  1861.         TABLE_TOP("Expires:");
  1862.         if(cache_obj->expires)
  1863.           {
  1864.             PUT_PART(ctime(&cache_obj->expires));
  1865.           }
  1866.         else
  1867.           {
  1868.             XP_STRCPY(buffer, "No expiration date sent");
  1869.             PUT_PART(buffer);
  1870.           }
  1871.         TABLE_BOTTOM;
  1872.  
  1873.         TABLE_TOP("Last accessed:");
  1874.         PUT_PART(ctime(&cache_obj->last_accessed));
  1875.         TABLE_BOTTOM;
  1876.  
  1877.         TABLE_TOP("Character set:");
  1878.         if(cache_obj->charset)
  1879.           {
  1880.             PUT_PART(cache_obj->charset);
  1881.           }
  1882.         else
  1883.           {
  1884.             XP_STRCPY(buffer, "iso-8859-1 (default)");
  1885.             PUT_PART(buffer);
  1886.           }
  1887.         TABLE_BOTTOM;
  1888.  
  1889.         TABLE_TOP("Secure:");
  1890.         XP_SPRINTF(buffer, "%s", cache_obj->security_on ? "TRUE" : "FALSE");
  1891.         PUT_PART(buffer);
  1892.         TABLE_BOTTOM;
  1893.  
  1894.         XP_STRCPY(buffer, "\n<P>\n");
  1895.         PUT_PART(buffer);
  1896.     
  1897.     
  1898.       }
  1899.  
  1900. END:
  1901.     FREE(buffer);
  1902.     if(cur_entry->status < 0)
  1903.         (*stream->abort)(stream, cur_entry->status);
  1904.     else
  1905.         (*stream->complete)(stream);
  1906.  
  1907.     return;
  1908. }
  1909.  
  1910. /*Accessor for use by Cache Browser */
  1911. PUBLIC net_CacheObject* 
  1912. NET_FirstMemCacheObject(XP_List* list_ptr) 
  1913. {
  1914.     if (net_MemoryCacheList)
  1915.     {
  1916.         list_ptr = net_MemoryCacheList;
  1917.         return NET_NextMemCacheObject(list_ptr);
  1918.     }
  1919.     else 
  1920.         return 0;
  1921. }
  1922.  
  1923. /*Accessor for use by Cache Browser */
  1924. PUBLIC net_CacheObject*
  1925. NET_NextMemCacheObject(XP_List* list_ptr)
  1926. {
  1927.     if (list_ptr)
  1928.     {
  1929.         net_MemoryCacheObject * tmp = (net_MemoryCacheObject *) XP_ListNextObject(list_ptr);
  1930.         if (tmp)
  1931.         {
  1932.             return &tmp->cache_obj;
  1933.         }
  1934.     }
  1935.     return 0;
  1936. }
  1937.  
  1938. #include "libmocha.h"
  1939.  
  1940. NET_StreamClass *
  1941. net_CloneWysiwygMemCacheEntry(MWContext *window_id, URL_Struct *URL_s,
  1942.                   uint32 nbytes, const char * wysiwyg_url,
  1943.                   const char * base_href)
  1944. {
  1945.     net_MemoryCacheObject *memory_copy;
  1946.     PRCList *link;
  1947.     CacheDataObject *data_object;
  1948.     NET_StreamClass *stream;
  1949.     XP_List *list;
  1950.     net_MemorySegment *seg;
  1951.     uint32 len;
  1952.  
  1953.     if (!(memory_copy = URL_s->memory_copy))
  1954.       {
  1955.         /* not hitting the cache -- check whether we're filling it */
  1956.         for (link = mem_active_cache_data_objects.next;
  1957.              link != &mem_active_cache_data_objects;
  1958.              link = link->next)
  1959.           {
  1960.             data_object = (CacheDataObject *)link;
  1961.             if (data_object->URL_s == URL_s &&
  1962.                 (memory_copy = data_object->memory_copy) != NULL)
  1963.               {
  1964.                 goto found;
  1965.               }
  1966.           }
  1967.         return NULL;
  1968.       }
  1969.  
  1970. found:
  1971.     stream = LM_WysiwygCacheConverter(window_id, URL_s, 
  1972.                       wysiwyg_url, base_href);
  1973.     if (!stream)
  1974.         return 0;
  1975.     list = memory_copy->list;
  1976.     while (nbytes != 0 &&
  1977.            (seg = (net_MemorySegment *) XP_ListNextObject(list)) != NULL)
  1978.       {
  1979.         len = seg->seg_size;
  1980.         if (len > nbytes)
  1981.             len = nbytes;
  1982.         if (stream->put_block(stream, seg->segment,
  1983.                               (int32)len) < 0)
  1984.             break;
  1985.         nbytes -= len;
  1986.       }
  1987.     if (nbytes != 0)
  1988.       {
  1989.         /* NB: Our caller must clear top_state->mocha_write_stream. */
  1990.         stream->abort(stream, MK_UNABLE_TO_CONVERT);
  1991.         XP_DELETE(stream);
  1992.         return 0;
  1993.       }
  1994.     return stream;
  1995. }
  1996.  
  1997. PRIVATE void
  1998. net_CleanupMemoryCacheProtocol(void)
  1999. {
  2000. }
  2001.  
  2002. void
  2003. NET_InitMemCacProtocol(void)
  2004. {
  2005.     static NET_ProtoImpl mem_cac_proto_impl;
  2006.  
  2007.     mem_cac_proto_impl.init = net_MemoryCacheLoad;
  2008.     mem_cac_proto_impl.process = net_ProcessMemoryCache;
  2009.     mem_cac_proto_impl.interrupt = net_InterruptMemoryCache;
  2010.     mem_cac_proto_impl.cleanup = net_CleanupMemoryCacheProtocol;
  2011.  
  2012.     NET_RegisterProtocolImplementation(&mem_cac_proto_impl, MEMORY_CACHE_TYPE_URL);
  2013. }
  2014.  
  2015. #endif /* MOZILLA_CLIENT */
  2016.