mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
978 lines
25 KiB
978 lines
25 KiB
/*
|
|
Enhanced NCSA Mosaic from Spyglass
|
|
"Guitar"
|
|
|
|
Copyright 1994 Spyglass, Inc.
|
|
All Rights Reserved
|
|
|
|
Author(s):
|
|
Eric W. Sink [email protected]
|
|
Jim Seidman [email protected]
|
|
*/
|
|
|
|
#include "all.h"
|
|
#include "pool.h"
|
|
|
|
#ifdef MAC
|
|
#include "guitwin.h"
|
|
#endif
|
|
|
|
#ifdef FEATURE_IAPI
|
|
long gSerialWindowID = FIRST_SERIAL_WINDOW_ID;
|
|
#endif
|
|
|
|
/*
|
|
This function takes a system-legal pathname and
|
|
changes it into a Web-legal pathname. In
|
|
the case of UNIX, no conversion should be
|
|
necessary.
|
|
N.B: The name can get larger as a result of this conversion!
|
|
*/
|
|
void FixPathName(char *path)
|
|
{
|
|
#ifdef WIN32
|
|
char *p;
|
|
char *escaped;
|
|
|
|
p = path;
|
|
while (p && *p)
|
|
{
|
|
if (*p == ':')
|
|
{
|
|
*p = '|';
|
|
}
|
|
p++;
|
|
}
|
|
|
|
p = path;
|
|
while (p && *p)
|
|
{
|
|
if (*p == '\\')
|
|
{
|
|
*p = '/';
|
|
}
|
|
p++;
|
|
}
|
|
|
|
escaped = HTEscape(path, URL_PATH, '|');
|
|
strcpy(path, escaped);
|
|
GTR_FREE(escaped);
|
|
#endif
|
|
#ifdef MAC
|
|
char *p;
|
|
char *escaped;
|
|
|
|
escaped = HTEscape(path, URL_XALPHAS, ':');
|
|
p = escaped;
|
|
while (p && *p)
|
|
{
|
|
if (*p == ':')
|
|
*p = '/';
|
|
p++;
|
|
}
|
|
strcpy(path, escaped);
|
|
GTR_FREE(escaped);
|
|
#endif
|
|
}
|
|
|
|
#ifdef WIN32
|
|
void W3Doc_UpdateBasePointSizes(void)
|
|
{
|
|
struct Mwin *tw;
|
|
int count;
|
|
int i;
|
|
struct _www *w3doc;
|
|
int j;
|
|
|
|
for (tw = Mlist; tw; tw = tw->next)
|
|
{
|
|
count = Hash_Count(&tw->doc_cache);
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
Hash_GetIndexedEntry(&tw->doc_cache, i, NULL, NULL, (void **) &w3doc);
|
|
w3doc->base_point_size = GTR_GetCurrentBasePointSize(gPrefs.iUserTextSize);
|
|
w3doc->nLastFormattedLine = -1;
|
|
|
|
for (j=0; j!=-1; j=w3doc->aElements[j].next)
|
|
{
|
|
if (w3doc->aElements[j].type == ELE_TEXT)
|
|
{
|
|
w3doc->aElements[j].portion.text.cached_font_index = -1;
|
|
}
|
|
}
|
|
}
|
|
if (tw->w3doc)
|
|
{
|
|
TW_ForceReformat(tw);
|
|
TW_ForceHitTest(tw);
|
|
}
|
|
}
|
|
}
|
|
#endif /* WIN32 */
|
|
|
|
void W3Doc_FreeContents(struct Mwin *tw, struct _www *w3doc)
|
|
{
|
|
int i;
|
|
struct _element *pElements;
|
|
|
|
if (!w3doc)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (w3doc->szActualURL)
|
|
{
|
|
GTR_FREE(w3doc->szActualURL);
|
|
w3doc->szActualURL = NULL;
|
|
}
|
|
|
|
POOL_Dispose (&w3doc->pool);
|
|
|
|
if (w3doc->pLineInfo)
|
|
{
|
|
GTR_FREE(w3doc->pLineInfo);
|
|
w3doc->pLineInfo = NULL;
|
|
w3doc->nLineSpace = 0;
|
|
w3doc->nLineCount = 0;
|
|
}
|
|
|
|
if (w3doc->pFormatState)
|
|
{
|
|
GTR_FREE(w3doc->pFormatState);
|
|
w3doc->pFormatState = NULL;
|
|
}
|
|
|
|
if (w3doc->piiBackground)
|
|
{
|
|
w3doc->piiBackground->refCount--;
|
|
}
|
|
|
|
pElements = w3doc->aElements;
|
|
|
|
if (pElements)
|
|
{
|
|
if (w3doc->elementCount)
|
|
{
|
|
for (i = 0; i >= 0; i = pElements[i].next)
|
|
{
|
|
switch (pElements[i].type)
|
|
{
|
|
case ELE_IMAGE:
|
|
case ELE_FORMIMAGE:
|
|
if (pElements[i].portion.img.myImage)
|
|
{
|
|
Image_DeleteElement(pElements[i].portion.img.myImage, w3doc, i);
|
|
}
|
|
if (pElements[i].portion.img.myMap)
|
|
{
|
|
pElements[i].portion.img.myMap->refCount--;
|
|
}
|
|
break;
|
|
}
|
|
#ifdef WIN32
|
|
if (tw->win && pElements[i].form && pElements[i].form->hWndControl)
|
|
{
|
|
DestroyWindow(pElements[i].form->hWndControl);
|
|
}
|
|
#endif /* WIN32 */
|
|
#ifdef MAC
|
|
if (tw->win && pElements[i].form)
|
|
{
|
|
switch (pElements[i].type)
|
|
{
|
|
case ELE_CHECKBOX:
|
|
case ELE_RADIO:
|
|
case ELE_SUBMIT:
|
|
case ELE_RESET:
|
|
/* We reset the visibility first so that disposal won't
|
|
leave a big white (and un-clipped!) hole on the screen. */
|
|
(**pElements[i].form->u.hControl).contrlVis = 0;
|
|
DisposeControl(pElements[i].form->u.hControl);
|
|
break;
|
|
case ELE_LIST:
|
|
case ELE_MULTILIST:
|
|
LDoDraw(FALSE, pElements[i].form->u.hList);
|
|
if ((**pElements[i].form->u.hList).vScroll)
|
|
(**(**pElements[i].form->u.hList).vScroll).contrlVis = 0;
|
|
LDispose(pElements[i].form->u.hList);
|
|
break;
|
|
case ELE_COMBO:
|
|
DeleteMenu((**pElements[i].form->u.menu.hMenu).menuID);
|
|
DisposeMenu(pElements[i].form->u.menu.hMenu);
|
|
break;
|
|
case ELE_EDIT:
|
|
case ELE_PASSWORD:
|
|
case ELE_TEXTAREA:
|
|
if (tw->teActive == pElements[i].form->u.edit.hEdit)
|
|
{
|
|
TEDeactivate(pElements[i].form->u.edit.hEdit);
|
|
tw->teActive = NULL;
|
|
}
|
|
TEDispose(pElements[i].form->u.edit.hEdit);
|
|
break;
|
|
}
|
|
}
|
|
#endif /* MAC */
|
|
#ifdef UNIX
|
|
if (tw->win && pElements[i].form)
|
|
{
|
|
switch (pElements[i].type)
|
|
{
|
|
case ELE_CHECKBOX:
|
|
case ELE_RADIO:
|
|
case ELE_SUBMIT:
|
|
case ELE_RESET:
|
|
/* We reset the visibility first so that disposal won't
|
|
leave a big white (and un-clipped!) hole on the screen. */
|
|
DestroyWidget(&pElements[i].form);
|
|
break;
|
|
case ELE_LIST:
|
|
case ELE_MULTILIST:
|
|
DestroyWidget(&pElements[i].form);
|
|
break;
|
|
case ELE_COMBO:
|
|
DestroyWidget(&pElements[i].form);
|
|
break;
|
|
case ELE_EDIT:
|
|
case ELE_PASSWORD:
|
|
case ELE_TEXTAREA:
|
|
DestroyWidget(&pElements[i].form);
|
|
break;
|
|
}
|
|
}
|
|
#endif /* UNIX */
|
|
if (tw->win && pElements[i].form)
|
|
{
|
|
if (pElements[i].form->pHashValues)
|
|
{
|
|
Hash_Destroy(pElements[i].form->pHashValues);
|
|
}
|
|
GTR_FREE(pElements[i].form);
|
|
}
|
|
}
|
|
}
|
|
GTR_FREE(w3doc->aElements);
|
|
w3doc->aElements = NULL;
|
|
}
|
|
|
|
if (w3doc->title)
|
|
{
|
|
GTR_FREE(w3doc->title);
|
|
w3doc->title = NULL;
|
|
}
|
|
|
|
if (w3doc->source)
|
|
{
|
|
CS_Destroy(w3doc->source);
|
|
}
|
|
|
|
#ifdef FEATURE_TABLES
|
|
TW_W3DocTableCleanup(w3doc);
|
|
#endif /* FEATURE_TABLES */
|
|
|
|
{
|
|
struct ImageElementNode *ien;
|
|
struct ImageElementNode *next;
|
|
|
|
ien = w3doc->image_list;
|
|
while (ien)
|
|
{
|
|
next = ien->next;
|
|
GTR_FREE(ien);
|
|
ien = next;
|
|
}
|
|
}
|
|
|
|
memset(w3doc, 0, sizeof(struct _www));
|
|
}
|
|
|
|
/*
|
|
This routine takes the three main components of a MiniRequest,
|
|
and converts them to a string, so that we can hash on them
|
|
*/
|
|
void MRQ_MakeString(char *s, char *url, BOOL bPost, char *szPostData)
|
|
{
|
|
int len;
|
|
char xor;
|
|
|
|
len = 0;
|
|
xor = 0;
|
|
|
|
if (szPostData)
|
|
{
|
|
char *p = szPostData;
|
|
|
|
while (*p)
|
|
{
|
|
len++;
|
|
xor ^= *p;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
sprintf(s, "%s:%s:%d:0x%x",
|
|
url,
|
|
(bPost ? "POST" : "GET"),
|
|
len,
|
|
xor
|
|
);
|
|
}
|
|
|
|
static void W3Doc_AddToCache(struct Mwin *tw, char *url, BOOL bPost, char *szPostData, struct _www *w3doc)
|
|
{
|
|
struct _www *killdoc;
|
|
int count;
|
|
int deleteMe;
|
|
int ndx;
|
|
char hash_string[MAX_URL_STRING * 2];
|
|
|
|
count = Hash_Count(&tw->doc_cache);
|
|
|
|
if (count >= gPrefs.doc_cache_size)
|
|
{
|
|
deleteMe = -1;
|
|
for (ndx = 0; ndx < count; ndx++)
|
|
{
|
|
Hash_GetIndexedEntry(&tw->doc_cache, ndx, NULL, NULL, (void **) &killdoc);
|
|
if (!killdoc->refCount)
|
|
{
|
|
deleteMe = ndx;
|
|
break;
|
|
}
|
|
}
|
|
if (deleteMe >= 0)
|
|
{
|
|
W3Doc_FreeContents(tw, killdoc);
|
|
GTR_FREE(killdoc);
|
|
Hash_DeleteIndexedEntry(&tw->doc_cache, deleteMe);
|
|
}
|
|
else
|
|
{
|
|
/* The cache is now overfull */
|
|
}
|
|
}
|
|
|
|
MRQ_MakeString(hash_string, url, bPost, szPostData);
|
|
Hash_Add(&tw->doc_cache, hash_string, NULL, w3doc);
|
|
}
|
|
|
|
HTAtom GTR_GetDefaultCharset(void)
|
|
{
|
|
if (gPrefs.szDefaultCharSet[0])
|
|
{
|
|
return HTAtom_for(gPrefs.szDefaultCharSet);
|
|
}
|
|
else
|
|
{
|
|
return HTAtom_for(LATIN1_CHARSET_NAME);
|
|
}
|
|
}
|
|
|
|
struct _www *W3Doc_CreateAndInit(struct Mwin *tw, HTRequest *req, struct CharStream *src)
|
|
{
|
|
struct _www *w3doc;
|
|
|
|
w3doc = (struct _www *) GTR_CALLOC(sizeof(struct _www), 1);
|
|
|
|
#ifdef FEATURE_STATUS_ICONS
|
|
w3doc->security = SECURITY_NONE;
|
|
#endif
|
|
|
|
if (!w3doc)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (tw->SDI_url)
|
|
{
|
|
w3doc->szActualURL = GTR_strdup(tw->SDI_url);
|
|
GTR_FREE (tw->SDI_url);
|
|
tw->SDI_url = NULL;
|
|
}
|
|
else
|
|
{
|
|
w3doc->szActualURL = GTR_strdup(req->destination->szActualURL);
|
|
}
|
|
|
|
w3doc->source = src;
|
|
|
|
#ifdef _GIBRALTAR
|
|
|
|
w3doc->last_modified = req->last_modified;
|
|
w3doc->expires = req->expires;
|
|
|
|
#endif // _GIBRALTAR
|
|
|
|
/* allocate string pool and element list */
|
|
if (POOL_Create (&w3doc->pool, NULL) != 0)
|
|
{
|
|
GTR_FREE (w3doc);
|
|
return NULL;
|
|
}
|
|
|
|
w3doc->elementSpace = INIT_ELE_SPACE;
|
|
w3doc->aElements = (struct _element *) GTR_CALLOC(sizeof(struct _element), w3doc->elementSpace);
|
|
if (!w3doc->aElements)
|
|
{
|
|
POOL_Dispose (&w3doc->pool);
|
|
GTR_FREE (w3doc);
|
|
return NULL;
|
|
}
|
|
|
|
w3doc->elementCount = 0;
|
|
w3doc->elementTail = -1;
|
|
w3doc->aElements[0].next = -1;
|
|
w3doc->iFirstVisibleElement = 0;
|
|
w3doc->nLastFormattedLine = -1;
|
|
|
|
#ifndef MAC
|
|
w3doc->next_control_id = FIRST_CONTROL_ID;
|
|
#endif
|
|
|
|
w3doc->pStyles = STY_GetStyleSheet();
|
|
|
|
w3doc->base_point_size = GTR_GetCurrentBasePointSize(gPrefs.iUserTextSize);
|
|
if (gPrefs.szDefaultCharSet[0])
|
|
{
|
|
w3doc->atomCharSet = HTAtom_for(gPrefs.szDefaultCharSet);
|
|
}
|
|
else
|
|
{
|
|
w3doc->atomCharSet = HTAtom_for(LATIN1_CHARSET_NAME);
|
|
}
|
|
|
|
w3doc->selStart.elementIndex = -1;
|
|
w3doc->selStart.offset = -1;
|
|
w3doc->selEnd.elementIndex = -1;
|
|
w3doc->selEnd.offset = -1;
|
|
w3doc->bStartIsAnchor = TRUE;
|
|
|
|
w3doc->yscale = 1;
|
|
w3doc->bIsComplete = FALSE;
|
|
|
|
w3doc->color_alink = gPrefs.anchor_color_active;
|
|
w3doc->color_vlink = gPrefs.anchor_color_beenthere;
|
|
w3doc->color_link = gPrefs.anchor_color;
|
|
w3doc->color_text = gPrefs.window_color_text;
|
|
w3doc->color_bgcolor = gPrefs.window_bgcolor;
|
|
|
|
#ifdef UNIX
|
|
if (!gPrefs.bIgnoreDocumentAttributes)
|
|
{
|
|
w3doc->wbg_color = tw->wbg_color;
|
|
w3doc->wfg_color = tw->wfg_color;
|
|
w3doc->wts_color = tw->wts_color;
|
|
w3doc->wbs_color = tw->wbs_color;
|
|
w3doc->sel_fg_color = tw->sel_fg_color;
|
|
}
|
|
#endif /* UNIX */
|
|
|
|
#ifdef WIN32
|
|
{
|
|
HDC hdc;
|
|
|
|
hdc = GetDC(NULL);
|
|
w3doc->nLogPixelsY = GetDeviceCaps(hdc, LOGPIXELSY);
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
#endif /* WIN32 */
|
|
|
|
if (!(req->iFlags & HTREQ_VIEWSOURCE))
|
|
{
|
|
W3Doc_AddToCache(tw, w3doc->szActualURL, (req->method == METHOD_POST), req->szPostData, w3doc);
|
|
}
|
|
|
|
if (tw->w3doc)
|
|
{
|
|
W3Doc_DisconnectFromWindow(tw->w3doc, tw);
|
|
}
|
|
W3Doc_ConnectToWindow(w3doc, tw);
|
|
|
|
if (req->iFlags & HTREQ_RECORD)
|
|
{
|
|
TW_AddToHistory(tw, w3doc->szActualURL, (req->method == METHOD_POST), req->szPostData);
|
|
GHist_Add(w3doc->szActualURL, NULL, time(NULL));
|
|
}
|
|
|
|
if (req->iFlags & HTREQ_USINGCACHE)
|
|
{
|
|
w3doc->lFlags |= W3DOC_FLAG_USEDCACHE;
|
|
}
|
|
|
|
if (req->iFlags & HTREQ_VIEWSOURCE)
|
|
{
|
|
w3doc->lFlags |= W3DOC_FLAG_VIEWSOURCE;
|
|
}
|
|
|
|
TW_UpdateTBar(tw);
|
|
|
|
return w3doc;
|
|
}
|
|
|
|
void W3Doc_ConnectToWindow(struct _www *w3doc, struct Mwin *tw)
|
|
{
|
|
XX_DMsg(DBG_FORM, ("Connect: 0x%x to 0x%x\n", w3doc, tw));
|
|
|
|
if (tw->w3doc != w3doc)
|
|
{
|
|
w3doc->refCount++;
|
|
tw->w3doc = w3doc;
|
|
tw->offt = w3doc->offt;
|
|
tw->offl = w3doc->offl;
|
|
}
|
|
|
|
#ifdef UNIX
|
|
TW_UpdateDocColors(tw);
|
|
#endif
|
|
|
|
TW_SetWindowName(tw);
|
|
|
|
FORM_ShowAllChildWindows(w3doc, SW_SHOW);
|
|
}
|
|
|
|
void W3Doc_DisconnectFromWindow(struct _www *w3doc, struct Mwin *tw)
|
|
{
|
|
int ndx;
|
|
|
|
XX_DMsg(DBG_FORM, ("Disconnect: 0x%x from 0x%x\n", w3doc, tw));
|
|
|
|
if (w3doc)
|
|
{
|
|
if (tw->win)
|
|
{
|
|
FORM_ShowAllChildWindows(w3doc, SW_HIDE);
|
|
}
|
|
#ifdef MAC
|
|
if (tw->teActive != tw->teURL)
|
|
{
|
|
tw->teActive = NULL;
|
|
}
|
|
#endif
|
|
w3doc->offt = tw->offt;
|
|
w3doc->offl = tw->offl;
|
|
|
|
w3doc->refCount--;
|
|
|
|
#ifdef UNIX
|
|
/** NOTE may need to inform Xt of selection clear **/
|
|
ClearSelection(tw);
|
|
#endif
|
|
|
|
/* If the document wasn't completely downloaded, throw it out of the cache */
|
|
if (!w3doc->bIsComplete)
|
|
{
|
|
XX_DMsg(DBG_WWW, ("W3Doc_DisconnectFromWindow: document not complete, removing from cache\n"));
|
|
ndx = Hash_FindByData(&tw->doc_cache, NULL, NULL, w3doc);
|
|
if (ndx >= 0)
|
|
{
|
|
Hash_DeleteIndexedEntry(&tw->doc_cache, ndx);
|
|
}
|
|
W3Doc_FreeContents(tw, w3doc);
|
|
}
|
|
|
|
tw->w3doc = NULL;
|
|
}
|
|
}
|
|
|
|
struct _www *W3Doc_CloneDocument(struct _www *src)
|
|
{
|
|
struct _www *dest;
|
|
|
|
dest = GTR_MALLOC(sizeof(struct _www));
|
|
if (!dest)
|
|
return NULL;
|
|
memset(dest, 0, sizeof(*dest));
|
|
|
|
/* We don't actually make copies of these */
|
|
dest->szActualURL = src->szActualURL;
|
|
dest->title = src->title;
|
|
|
|
/* We don't clone the source */
|
|
|
|
/* We don't actually make a copy of the pool either */
|
|
POOL_Clone (&src->pool, &dest->pool);
|
|
|
|
/* We make room for a few extra elements assuming that we'll grow during reformatting */
|
|
dest->elementSpace = (src->elementCount + src->elementCount / 4);
|
|
dest->aElements = GTR_MALLOC(dest->elementSpace * sizeof(struct _element));
|
|
if (!dest->aElements)
|
|
{
|
|
GTR_FREE(dest);
|
|
return NULL;
|
|
}
|
|
dest->elementCount = src->elementCount;
|
|
memcpy(dest->aElements, src->aElements, src->elementCount * sizeof(struct _element));
|
|
dest->elementTail = src->elementTail;
|
|
dest->iFirstVisibleElement = 0;
|
|
dest->nLastFormattedLine = -1;
|
|
|
|
dest->iFirstFormEl = src->iFirstFormEl;
|
|
|
|
dest->pStyles = src->pStyles;
|
|
dest->selStart.elementIndex = -1;
|
|
dest->selStart.offset = -1;
|
|
dest->selEnd.elementIndex = -1;
|
|
dest->selEnd.offset = -1;
|
|
dest->bStartIsAnchor = TRUE;
|
|
|
|
dest->yscale = 1;
|
|
dest->bIsComplete = src->bIsComplete;
|
|
|
|
#ifdef FEATURE_TABLES
|
|
TW_CloneW3docTableInfo(dest,src);
|
|
#endif /* FEATURE_TABLES */
|
|
|
|
dest->base_point_size = src->base_point_size;
|
|
dest->color_vlink = src->color_vlink;
|
|
dest->color_alink = src->color_alink;
|
|
dest->color_link = src->color_link;
|
|
dest->color_text = src->color_text;
|
|
dest->color_bgcolor = src->color_bgcolor;
|
|
dest->atomCharSet = src->atomCharSet;
|
|
|
|
#ifdef UNIX
|
|
dest->wbg_color = src->wbg_color;
|
|
dest->wfg_color = src->wfg_color;
|
|
dest->wts_color = src->wts_color;
|
|
dest->wbs_color = src->wbs_color;
|
|
dest->sel_fg_color = src->sel_fg_color;
|
|
#endif /* UNIX */
|
|
|
|
#ifdef WIN32
|
|
dest->nLogPixelsY = src->nLogPixelsY;
|
|
#endif /* WIN32 */
|
|
|
|
return dest;
|
|
}
|
|
|
|
void W3Doc_KillClone(struct _www *w3doc)
|
|
{
|
|
GTR_FREE(w3doc->aElements);
|
|
GTR_FREE(w3doc);
|
|
}
|
|
|
|
BOOL W3Doc_HasMissingImages(struct _www *w3doc)
|
|
{
|
|
int i;
|
|
struct _element *pel;
|
|
|
|
if (w3doc && w3doc->aElements)
|
|
{
|
|
for (i = 0; i >= 0; i = w3doc->aElements[i].next)
|
|
{
|
|
pel = &(w3doc->aElements[i]);
|
|
if (((pel->type == ELE_IMAGE) || (pel->type == ELE_FORMIMAGE)) && pel->portion.img.myImage &&
|
|
pel->portion.img.myImage->flags & (IMG_NOTLOADED | IMG_PARTIAL))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void W3Doc_DeleteAll(struct Mwin *tw)
|
|
{
|
|
int i;
|
|
int count;
|
|
struct _www *w3doc;
|
|
|
|
count = Hash_Count(&tw->doc_cache);
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
Hash_GetIndexedEntry(&tw->doc_cache, i, NULL, NULL, (void **) &w3doc);
|
|
W3Doc_FreeContents(tw, w3doc);
|
|
GTR_FREE(w3doc);
|
|
}
|
|
|
|
Hash_FreeContents(&tw->doc_cache);
|
|
}
|
|
|
|
#ifdef USE_MEMMANAGE
|
|
/* Reduce the amount of memory used by documents by deleting cached documents */
|
|
static BOOL W3Doc_ReduceMemory(int nWanted)
|
|
{
|
|
int ndx;
|
|
int nHistPos;
|
|
int nHistCount;
|
|
struct Mwin *mw, *lastmw;
|
|
BOOL bDidFree;
|
|
struct _www *w3doc, *w3tokill;
|
|
struct nAge;
|
|
int ndxtokill;
|
|
struct Mwin *mwtokill;
|
|
int nAge;
|
|
|
|
bDidFree = FALSE;
|
|
|
|
/* Delete an old document from one of the window caches. We always want
|
|
to leave at least one document behind the currently visible one so that
|
|
we won't have to disable the "back" menu item and button. We also leave
|
|
an extra document for the currently active window. */
|
|
nAge = 2;
|
|
w3tokill = NULL;
|
|
mwtokill = NULL;
|
|
ndxtokill = -1;
|
|
lastmw = NULL;
|
|
for (mw = Mlist; mw && mw != lastmw; mw = mw->next)
|
|
{
|
|
if (mw->wintype != GHTML)
|
|
continue;
|
|
|
|
/* Walk the history list until we find the oldest history element
|
|
still cached. */
|
|
/* TODO: Reverse the way that this works so that it instead goes
|
|
through the document cache finding each item in the history
|
|
list. Right now this method isn't effective if the user
|
|
backed up and pruned the history list. */
|
|
nHistCount = HTList_count(mw->history);
|
|
nHistPos = mw->history_index + nAge;
|
|
for (; nHistPos < nHistCount; nHistPos++)
|
|
{
|
|
struct MiniRequest *mrq;
|
|
char buf[MAX_URL_STRING * 2];
|
|
|
|
mrq = HTList_objectAt(mw->history, nHistPos);
|
|
MRQ_MakeString(buf, mrq->url, mrq->bPost, mrq->szPostData);
|
|
|
|
/* Check to see if this historical document is in the cache */
|
|
ndx = Hash_Find(&mw->doc_cache, buf, NULL, (void **) &w3doc);
|
|
if (ndx >= 0)
|
|
{
|
|
/* This is a document we could potentially kill */
|
|
mwtokill = mw;
|
|
ndxtokill = ndx;
|
|
w3tokill = w3doc;
|
|
/* Set the target age for future hits to be one older than
|
|
this document. */
|
|
nAge = nHistPos - mw->history_index + 1;
|
|
/* Unless this is the current window, in which case we'll take
|
|
one of the same age. */
|
|
if (mw == gTW_Current)
|
|
nAge--;
|
|
}
|
|
else
|
|
{
|
|
/* We've exhausted the chances for this window. */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (w3tokill)
|
|
{
|
|
/* We've found a document to kill */
|
|
W3Doc_FreeContents(mwtokill, w3tokill);
|
|
GTR_FREE(w3tokill);
|
|
Hash_DeleteIndexedEntry(&mwtokill->doc_cache, ndxtokill);
|
|
bDidFree = TRUE;
|
|
}
|
|
|
|
return bDidFree;
|
|
}
|
|
#endif
|
|
|
|
struct Mwin *NewMwin(int type)
|
|
{
|
|
struct Mwin *ntw;
|
|
|
|
ntw = (struct Mwin *) GTR_CALLOC(sizeof(struct Mwin), 1);
|
|
if (!ntw)
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
ntw->wintype = (short) type;
|
|
|
|
ntw->iMagic = SPYGLASS_MWIN_MAGIC;
|
|
|
|
switch (type)
|
|
{
|
|
case GHTML:
|
|
case GWINDOWLESS:
|
|
{
|
|
ntw->w3doc = NULL;
|
|
|
|
if (Hash_Init(&ntw->doc_cache))
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
|
|
GTR_FREE(ntw);
|
|
return NULL;
|
|
}
|
|
|
|
ntw->request = HTRequest_new();
|
|
if (!ntw->request)
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
|
|
GTR_FREE(ntw);
|
|
return NULL;
|
|
}
|
|
HTFormatInit(ntw->request->conversions);
|
|
ntw->request->output_format = WWW_PRESENT;
|
|
|
|
ntw->post_request = HTRequest_new();
|
|
if (!ntw->post_request)
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
|
|
HTRequest_delete(ntw->request);
|
|
GTR_FREE(ntw);
|
|
return NULL;
|
|
}
|
|
HTFormatInit(ntw->post_request->conversions);
|
|
ntw->post_request->method = METHOD_POST;
|
|
ntw->post_request->content_type = HTAtom_for("application/x-www-form-urlencoded");
|
|
|
|
ntw->image_request = HTRequest_new();
|
|
if (!ntw->image_request)
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
|
|
HTRequest_delete(ntw->request);
|
|
HTRequest_delete(ntw->post_request);
|
|
GTR_FREE(ntw);
|
|
return NULL;
|
|
}
|
|
HTFormatInit(ntw->image_request->conversions);
|
|
ntw->image_request->output_format = HTAtom_for("www/inline_image");
|
|
|
|
ntw->history = HTList_new();
|
|
break;
|
|
}
|
|
|
|
#ifdef UNIX
|
|
case GSOUND:
|
|
case GIMAGE:
|
|
{
|
|
ntw->w3doc = NULL;
|
|
|
|
ntw->request = NULL;
|
|
|
|
ntw->image_request = NULL;
|
|
ntw->history = NULL;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef FEATURE_IAPI
|
|
#ifdef UNIX
|
|
ntw->lErrorOccurred = 0; /* init error code */
|
|
#endif
|
|
|
|
if (type != GWINDOWLESS)
|
|
ntw->serialID = gSerialWindowID++;
|
|
#endif
|
|
|
|
ntw->next = Mlist;
|
|
Mlist = ntw;
|
|
|
|
return ntw;
|
|
}
|
|
|
|
|
|
/* Determine whether a link has already been visited */
|
|
BOOL TW_WasVisited(struct _www * pdoc, struct _element * pel)
|
|
{
|
|
extern struct hash_table gGlobalHistory;
|
|
char buf[MAX_URL_STRING + 1];
|
|
char *ppound;
|
|
time_t then;
|
|
time_t now;
|
|
|
|
/*
|
|
Under Windows, one of the routines we're calling here
|
|
has an 8 byte memory leak in it. It calls strdup from
|
|
a function called tzset. The 8 byte memory leak was
|
|
reported by BoundsChecker, and it happens only once
|
|
per program session.
|
|
*/
|
|
|
|
if (gPrefs.visitation_horizon <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* No way to tell for image maps */
|
|
if (pel->lFlags & ELEFLAG_IMAGEMAP)
|
|
return FALSE;
|
|
if (pel->lFlags & ELEFLAG_USEMAP)
|
|
return FALSE;
|
|
|
|
(pdoc->pool.f->GetChars)(&pdoc->pool, buf, pel->hrefOffset, pel->hrefLen);
|
|
|
|
buf[pel->hrefLen] = '\0';
|
|
|
|
/* We only consider documents, not local anchors inside documents */
|
|
if (buf[0] == '#')
|
|
return TRUE;
|
|
|
|
ppound = strrchr(buf, '#');
|
|
if (ppound)
|
|
*ppound = '\0';
|
|
|
|
if (Hash_Find(&gGlobalHistory, buf, NULL, (void **) &then) >= 0)
|
|
{
|
|
int age; /* in days */
|
|
|
|
/* Well, we've been there, but how recently ?? */
|
|
now = time(NULL);
|
|
|
|
/* See how many days ago this was. */
|
|
age = (now - then) / (24 * 60 * 60);
|
|
if (age <= (gPrefs.visitation_horizon - 1))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void TW_DisposeConnection(struct _CachedConn *pCon)
|
|
{
|
|
switch (pCon->type)
|
|
{
|
|
case CONN_NONE:
|
|
break;
|
|
case CONN_FTP:
|
|
FTP_DisposeFTPConnection(pCon);
|
|
break;
|
|
case CONN_HTTP:
|
|
HTTP_DisposeHTTPConnection(pCon);
|
|
break;
|
|
case CONN_NNTP:
|
|
#ifndef _GIBRALTAR
|
|
News_DisposeNewsConnection(pCon);
|
|
#endif
|
|
break;
|
|
case CONN_SMTP:
|
|
#ifndef _GIBRALTAR
|
|
Mail_DisposeMailConnection(pCon);
|
|
#endif
|
|
break;
|
|
default:
|
|
XX_Assert((0), ("TW_DisposeConnection: illegal connection type %d!", pCon->type));
|
|
}
|
|
pCon->isoc = NULL;
|
|
}
|
|
|
|
BOOL
|
|
TW_AmILastWindow(struct Mwin *tw)
|
|
{
|
|
struct Mwin *mw;
|
|
int count;
|
|
|
|
if (!tw || tw->wintype != GHTML)
|
|
return FALSE;
|
|
|
|
count = 0;
|
|
for (mw = Mlist; mw; mw = mw->next)
|
|
{
|
|
if (mw->wintype == GHTML)
|
|
count++;
|
|
}
|
|
|
|
if (count == 1)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|