/****************************************************************************
 * NCSA Mosaic for the X Window System                                      *
 * Software Development Group                                               *
 * National Center for Supercomputing Applications                          *
 * University of Illinois at Urbana-Champaign                               *
 * 605 E. Springfield, Champaign IL 61820                                   *
 * mosaic@ncsa.uiuc.edu                                                     *
 *                                                                          *
 * Copyright (C) 1993, Board of Trustees of the University of Illinois      *
 *                                                                          *
 * NCSA Mosaic software, both binary and source (hereafter, Software) is    *
 * copyrighted by The Board of Trustees of the University of Illinois       *
 * (UI), and ownership remains with the UI.                                 *
 *                                                                          *
 * The UI grants you (hereafter, Licensee) a license to use the Software    *
 * for academic, research and internal business purposes only, without a    *
 * fee.  Licensee may distribute the binary and source code (if released)   *
 * to third parties provided that the copyright notice and this statement   *
 * appears on all copies and that no charge is associated with such         *
 * copies.                                                                  *
 *                                                                          *
 * Licensee may make derivative works.  However, if Licensee distributes    *
 * any derivative work based on or derived from the Software, then          *
 * Licensee will (1) notify NCSA regarding its distribution of the          *
 * derivative work, and (2) clearly notify users that such derivative       *
 * work is a modified version and not the original NCSA Mosaic              *
 * distributed by the UI.                                                   *
 *                                                                          *
 * Any Licensee wishing to make commercial use of the Software should       *
 * contact the UI, c/o NCSA, to negotiate an appropriate license for such   *
 * commercial use.  Commercial use includes (1) integration of all or       *
 * part of the source code into a product for sale or license by or on      *
 * behalf of Licensee to third parties, or (2) distribution of the binary   *
 * code or source code to third parties that need it to utilize a           *
 * commercial product sold or licensed by or on behalf of Licensee.         *
 *                                                                          *
 * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR   *
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED          *
 * WARRANTY.  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE    *
 * USERS OF THIS SOFTWARE.                                                  *
 *                                                                          *
 * By using or copying this Software, Licensee agrees to abide by the       *
 * copyright law and all other applicable laws of the U.S. including, but   *
 * not limited to, export control laws, and the terms of this license.      *
 * UI shall have the right to terminate this license immediately by         *
 * written notice upon Licensee's breach of, or non-compliance with, any    *
 * of its terms.  Licensee may be held legally responsible for any          *
 * copyright infringement that is caused or encouraged by Licensee's        *
 * failure to abide by the terms of this license.                           *
 *                                                                          *
 * Comments and questions are welcome and can be sent to                    *
 * mosaic-x@ncsa.uiuc.edu.                                                  *
 ****************************************************************************/

/* Copyright (C) 1998, 1999, 2000 - The VMS Mosaic Project */

#include "../config.h"
#include "../libwww2/htaautil.h"
#include "../libwww2/HTnews.h"
#include "mosaic.h"
#include "gui.h"
#include "gui-documents.h"
#include "gui-extras.h"
#include "gui-dialogs.h"
#include "mo-www.h"
#include "globalhist.h"
#include "annotate.h"
#include "history.h"
#include "../libhtmlw/htmlp.h"
#include "../libnut/str-tools.h"

#ifdef CCI
#include "cci.h"
#include "cciBindings.h"
#include "cciBindings2.h"
/* From cciBindings.c */
extern int cci_get;
int CCIprotocol_handler_found;
#endif

#include <Xm/List.h>

extern char pre_title[80];
extern char *cached_url;
extern int binary_transfer;
extern char *startup_document, *home_document;
extern char reloading;
extern int do_head;
extern int securityType;

#ifndef DISABLE_TRACE
extern int srcTrace;
#endif

int loading_inlined_images = 0;
char *url_base_override = NULL;
int keep_url_base_override = 0;
int interrupted = 0;

/* Kludge to pass last modified time from HTMIME.c */
extern char *HTTP_last_modified;
extern char *HTTP_expires;

/* From HTTP.c */
extern char *encrypt_cipher;
extern int encrypt_bits;

/* From HTAccess.c. */
extern char *use_this_url_instead;

/* Add more sense and sensibility to rbm */
void mo_popup_set_something();

static Boolean check_imagedelay (char *url);
static void mo_back_possible (mo_window *win);
static void mo_forward_possible (mo_window *win);
static void mo_annotate_edit_possible (mo_window *win);
static void mo_annotate_edit_impossible (mo_window *win);
static void mo_set_text (Widget w, char *txt, char *ans, int id, 
                         char *targetanchor, void *cached_widgets);

/* For selective image loading */
extern char **imagedelay_sites;
extern Boolean currently_delaying_images;

/****************************************************************************
 * name:    check_imagedelay
 * purpose: checks the win->current_node->url against the imagedelay_sites
 * inputs:  
 *   - char *url - the current url
 * returns: 
 *   1 if there is a match in imagedelay_sites
 *   0 if there is not a match
 * 
 ****************************************************************************/
static Boolean check_imagedelay (char *url) {

    long i;
    
    if (imagedelay_sites) {
        for (i = 0; imagedelay_sites[i]; i++) {
            if (strstr(url, imagedelay_sites[i])) {
                return 1;
            }
        }
    }
    
    return 0;
}


/****************************************************************************
 * name:    mo_snarf_scrollbar_values
 * purpose: Store current viewing state in the current node, in case
 *          we want to return to the same location later.
 * inputs:  
 *   - mo_window *win: Current window.
 * returns: 
 *   mo_succeed
 *   (mo_fail if no current node exists)
 * remarks: 
 *   Snarfs current docid position in the HTML widget.
 ****************************************************************************/
static mo_status mo_snarf_scrollbar_values (mo_window *win)
{
  /* Make sure we have a node. */
  if (!win->current_node)
      return mo_fail;

  win->current_node->docid = HTMLPositionToId(win->scrolled_win, 0, 3);

  /* Do the cached stuff thing. */
  win->current_node->cached_widgets = HTMLGetWidgetInfo(win->scrolled_win);
  win->current_node->cached_forms = HTMLGetFormInfo(win->scrolled_win);

  if (win->frames) {
      mo_window *wframe = win->frames;
      mo_window *next = NULL;
      mo_frame *nframe = win->current_node->frames;
      int already_had;

      while (nframe && wframe) {
	  /* Have we already put it in this node? */
	  if (nframe->cached_widgets) {
	      already_had = 1;
	  } else {
	      nframe->cached_widgets = HTMLGetWidgetInfo(wframe->scrolled_win);
	      already_had = 0;
	  }
	  if (nframe->cached_widgets && !already_had) {
	      WidgetInfo *wid = (WidgetInfo *) nframe->cached_widgets;
	      FormInfo *form;

	      wid->cache_count++;

	      form = HTMLGetFormInfo(wframe->scrolled_win);
	      if (wid->cache_count == 1) {
	          /* Save original form list; form callbacks need it */
		  wid->cached_forms = form;
		  if (form) {
		      form->cached = 1;
		  }
	      } else {
		  if (form && !form->cached) {
		      /* Free others; they are not needed for callbacks */
		      HTMLFreeFormInfo(form);
		  }
	      }

	  }
	  nframe->docid = HTMLPositionToId(wframe->scrolled_win, 0, 0);
	  nframe = nframe->next;
          if (wframe->frames) {
	      next = wframe->next_frame;
	      wframe = wframe->frames;
	  } else {
	      wframe = wframe->next_frame;
	  }
	  if (!wframe && next) {
	      wframe = next;
	  }
      }
  }

  return mo_succeed;
}


/* ---------------------- mo_reset_document_headers ----------------------- */

static mo_status mo_reset_document_headers (mo_window *win)
{
  char *buf = NULL;

  if (win->current_node) {
      XmxTextSetString(win->title_text, win->current_node->title);
      XmxTextSetString(win->url_text, win->current_node->url);
  }

  /* Make the menubar be the title space */
  if (get_pref_boolean(eTITLEISWINDOWTITLE) || get_pref_boolean(eUSEICONBAR)) {
	if (win && win->base && win->current_node &&
	    win->current_node->title && *(win->current_node->title)) {
		buf = (char *)malloc(strlen(pre_title) +
			strlen(win->current_node->title) + 15);
		if (!buf) {
			perror("Title Buffer");
			return(mo_fail);
		}
		sprintf(buf, "%s [%s", pre_title, win->current_node->title);
		/* Annoying junk at end */
		buf[strlen(buf)] = '\0';
		strcat(buf, "]");
		buf[strlen(buf)] = '\0';
		XtVaSetValues(win->base,
			      XmNtitle, buf,
			      NULL);
		free(buf);
	} else if (win && win->base) {
		buf = (char *)malloc(strlen(pre_title) + 15);
		if (!buf) {
			perror("Title Buffer");
			return(mo_fail);
		}
		sprintf(buf, "%s: [%s]", pre_title, "No Title");
		buf[strlen(buf)] = '\0';
		XtVaSetValues(win->base,
			      XmNtitle, buf,
			      NULL);
		free(buf);
	}
  }

  return mo_succeed;
}

/* --------------------------- mo_back_possible --------------------------- */

/* This could be cached, but since it shouldn't take too long... */
static void mo_back_possible (mo_window *win)
{
  static int init = 0;
  static int usetext;

  if (!init) {
      usetext = get_pref_boolean(eUSETEXTBUTTONBAR);
      init = 1;
  }

  if (usetext) {
      mo_tool_state(&(win->tools[BTN_PREV]), XmxSensitive, BTN_PREV);
      XmxRSetSensitive(win->menubar, mo_back, XmxSensitive);
  }
  mo_popup_set_something("Back", XmxSensitive, NULL);

  return;
}


/****************************************************************************
 * name:    mo_back_impossible
 * purpose: Can't go back (nothing in the history list).
 ****************************************************************************/
mo_status mo_back_impossible (mo_window *win)
{
  static int init = 0;
  static int usetext;

  if (!init) {
      usetext = get_pref_boolean(eUSETEXTBUTTONBAR);
      init = 1;
  }

  if (usetext) {
	XmxRSetSensitive(win->menubar, mo_back, XmxNotSensitive);
        mo_tool_state(&(win->tools[BTN_PREV]), XmxNotSensitive, BTN_PREV);
  }
  mo_popup_set_something("Back", XmxNotSensitive, NULL);

  return mo_succeed;
}

static void mo_forward_possible (mo_window *win)
{
  static int init = 0;
  static int usetext;

  if (!init) {
      usetext = get_pref_boolean(eUSETEXTBUTTONBAR);
      init = 1;
  }

  if (usetext) {
      mo_tool_state(&(win->tools[BTN_NEXT]), XmxSensitive, BTN_NEXT);
      XmxRSetSensitive(win->menubar, mo_forward, XmxSensitive);
  }
	
  mo_popup_set_something("Forward", XmxSensitive, NULL);

  return;
}


/****************************************************************************
 * name:    mo_forward_impossible
 * purpose: Can't go forward (nothing in the history list).
 ****************************************************************************/
mo_status mo_forward_impossible (mo_window *win)
{
  static int init = 0;
  static int usetext;

  if (!init) {
      usetext = get_pref_boolean(eUSETEXTBUTTONBAR);
      init = 1;
  }

  if (usetext) {
      mo_tool_state(&(win->tools[BTN_NEXT]), XmxNotSensitive, BTN_NEXT);
      XmxRSetSensitive(win->menubar, mo_forward, XmxNotSensitive);
  }
    
    mo_popup_set_something("Forward", XmxNotSensitive, NULL);

    return mo_succeed;
}

/* ---------------------- mo_annotate_edit_possible ----------------------- */

static void mo_annotate_edit_possible (mo_window *win)
{
  XmxRSetSensitive(win->menubar, mo_annotate_edit, XmxSensitive);
  XmxRSetSensitive(win->menubar, mo_annotate_delete, XmxSensitive);
  return;
}

static void mo_annotate_edit_impossible (mo_window *win)
{
  XmxRSetSensitive(win->menubar, mo_annotate_edit, XmxNotSensitive);
  XmxRSetSensitive(win->menubar, mo_annotate_delete, XmxNotSensitive);
  return;
}


/* ------------------------------------------------------------------------ */

static void mo_set_text (Widget w, char *txt, char *ans, int id, 
                         char *targetanchor, void *cached_widgets)
{
  static int init = 0;
  static int top;

  if (!init) {
      top = get_pref_boolean(eANNOTATIONS_ON_TOP);
      init = 1;
  }

  /* Any data transfer that takes place in here must be inlined image loading */
  loading_inlined_images = 1;  /* Use correct transfer progress message */

  interrupted = 0;
  mo_set_image_cache_nuke_threshold();

  if (top) {
    HTMLSetText(w, txt, ans ? ans : "\0", "\0", id, targetanchor,
		cached_widgets);
  } else {
    HTMLSetText(w, txt, "\0", ans ? ans : "\0", id, targetanchor,
		cached_widgets);
  }
  loading_inlined_images = 0;
  interrupted = 0;
  mo_gui_done_with_icon();
}


/****************************************************************************
 * name:    mo_do_window_text
 * purpose: Set a window's text and do lots of other housekeeping
 *          and GUI-maintenance things.
 * inputs:  
 *   - mo_window *win: The current window.
 *   - char      *url: The URL for the text; may not be canonicalized
 *                     and otherwise ready for inclusion in history lists,
 *                     the window's overhead URL display, etc.
 *   - char      *txt: The new text for the window.
 *   - char  *txthead: The start of the malloc'd block of text corresponding
 *                     to txt.
 *   - int register_visit: If TRUE, then this text should be registerd
 *                         as a new node in the history list.  If FALSE,
 *                         then we're just moving around in the history list.
 *   - char      *ref: Reference (possible title) for this text.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This is the mother of all functions in Mosaic.  Probably should be
 *   rethought and broken down.
 ****************************************************************************/
mo_status mo_do_window_text (mo_window *win, char *url, char *txt,
                             char *txthead, int register_visit, char *ref,
                             char *last_modified, char *expires)
{
    char *ans;
    char *cipher;
    Boolean did_we_image_delay = 0;
    mo_window *load_win;
    Boolean is_frame = 0;
    Boolean new_node = 0;
    static int init = 0;
    static int focus, track, track_anchors, on_top;

    if (!init) {
	/* Do these calls only once */
        track = get_pref_boolean(eTRACK_POINTER_MOTION);
        on_top = get_pref_boolean(eANNOTATIONS_ON_TOP);
	track_anchors = get_pref_boolean(eTRACK_TARGET_ANCHORS);
        focus = get_pref_boolean(eFOCUS_FOLLOWS_MOUSE);
        init = 1;
    }

    if (win->do_frame) {
	load_win = win->do_frame;
	is_frame = 1;
	if (load_win->new_node) {
	    new_node = 1;
	}
    } else {
	load_win = win;
	/* Update this early on */
	XmxTextSetString(win->url_text, url);
    }
    /* Need to be sure icon interrupt is cleared, otherwise images
     * may not get loaded when revisiting a currently loaded page */
    mo_gui_clear_icon();

    /* Reset the global for imagekill */
    currently_delaying_images = 0;

    /* Mark as Refresh URL refreshable */
    win->refreshable = True;

    if (!win->delay_image_loads) {
        if (check_imagedelay(url)) {
            win->delay_image_loads = 1;
            currently_delaying_images = 1;
            did_we_image_delay = 1;
        }
    }
#ifdef CCI
    /* Send document over cci if needed */
    if (txt)
 	MoCCISendBrowserViewOutput(url, "text/html", txt, strlen(txt));
#endif
      /* Track application mode */
      {
          int newmode = moMODE_PLAIN;
          
          if (!my_strncasecmp(url, "ftp:", 4)) {
	      newmode = moMODE_FTP;
          } else if (!my_strncasecmp(url, "news:", 5)) {
              int p, n, pt, nt, f;

              news_status(url, &pt, &nt, &p, &n, &f);

              mo_tool_state(&(win->tools[BTN_PTHR]),
                              pt ? XmxSensitive : XmxNotSensitive, BTN_PTHR);
	      XmxRSetSensitive(win->menubar, mo_news_prevt,
			       pt ? XmxSensitive : XmxNotSensitive);

              mo_tool_state(&(win->tools[BTN_NTHR]),
                              nt ? XmxSensitive : XmxNotSensitive, BTN_NTHR);
	      XmxRSetSensitive(win->menubar, mo_news_nextt, 
			       nt ? XmxSensitive : XmxNotSensitive);

              mo_tool_state(&(win->tools[BTN_PART]),
                              p ? XmxSensitive : XmxNotSensitive, BTN_PART);
	      XmxRSetSensitive(win->menubar, mo_news_prev, 
			       p ? XmxSensitive : XmxNotSensitive);

	      mo_tool_state(&(win->tools[BTN_NART]),
                              n ? XmxSensitive : XmxNotSensitive, BTN_NART);
	      XmxRSetSensitive(win->menubar, mo_news_next, 
			       n ? XmxSensitive : XmxNotSensitive);

              mo_tool_state(&(win->tools[BTN_POST]), XmxSensitive, BTN_POST);

              mo_tool_state(&(win->tools[BTN_FOLLOW]),
                              f ? XmxSensitive : XmxNotSensitive, BTN_FOLLOW);
	      XmxRSetSensitive(win->menubar, mo_news_follow, 
			       f ? XmxSensitive : XmxNotSensitive);
	      /* Set the popup too */
	      mo_popup_set_something("Previous Thread", 
				     pt ? XmxSensitive : XmxNotSensitive, NULL);
	      mo_popup_set_something("Next Thread",
				     nt ? XmxSensitive : XmxNotSensitive, NULL);
	      mo_popup_set_something("Previous Article", 
				     p ? XmxSensitive : XmxNotSensitive, NULL);
	      mo_popup_set_something("Next Article", 
				     n ? XmxSensitive : XmxNotSensitive, NULL);
	      mo_popup_set_something("Followup",
				     f ? XmxSensitive : XmxNotSensitive, NULL);
              newmode = moMODE_NEWS;
          }
          if (newmode != win->mode) {
              win->mode = newmode;
              mo_switch_mode(win);
          }
      }

  mo_set_current_cached_win(win);

  if (track) {
      XmString xmstr = XmStringCreateLtoR(" ", XmSTRING_DEFAULT_CHARSET);
      XtVaSetValues(win->tracker_label, XmNlabelString, (XtArgVal)xmstr, NULL);
      XmStringFree(xmstr);
  }

  /* If !register_visit, we're just screwing around with current_node
   * already, so don't bother snarfing scrollbar values. */
  if (register_visit && (!is_frame || new_node))
      mo_snarf_scrollbar_values(win);

  /* Don't track Meta (HEAD) requests */
  if (register_visit < 2)
      mo_here_we_are_son(url);
  
  {
    /* Since mo_fetch_annotation_links uses the communications code,
     * we need to play games with binary_transfer. */
    int tmp = binary_transfer;

    binary_transfer = 0;
    ans = mo_fetch_annotation_links(url, on_top);

    binary_transfer = tmp;
  }

  /* If there is a BASE tag in the document that contains a "real"
   * URL, this will be non-NULL by the time we exit and base_callback
   * will have been called.  If flag is set then, we are displaying
   * an edited document and forcing a BASE url for it */
  /* cached_url HAS to be set here, since Resolve counts on it. */
  if (cached_url)
      free(cached_url);
  if (!keep_url_base_override) {
      url_base_override = NULL;
      cached_url = mo_url_canonicalize(url, "");
  } else {
      cached_url = mo_url_canonicalize(url_base_override, "");
      keep_url_base_override = 0;
  }
  if (load_win->cached_url)
      free(load_win->cached_url);
  load_win->cached_url = strdup(cached_url);

  {
    int id = 0, freeta = 0;
    void *cached_widgets = NULL;
    char *target_anchor = load_win->target_anchor;

    if (!register_visit && win->current_node && !is_frame) {
        id = win->current_node->docid;
        cached_widgets = win->current_node->cached_widgets;
    } else if (is_frame && !register_visit && win->current_node) {
	mo_frame *nframe = win->current_node->frames;

	while (nframe) {
	    if (!strcmp(nframe->url, url)) {
		WidgetInfo *wid = (WidgetInfo *) nframe->cached_widgets;

		id = nframe->docid;
		if (wid) {
		    if (wid->cache_invalid) {
			/* The frame's text has been reloaded */
			id = 0;
			wid->cache_count--;
			if (wid->cache_count < 1) {
			    if (wid->cached_forms) {
				HTMLFreeFormInfo(wid->cached_forms);
			    }
			    HTMLFreeWidgetInfo(nframe->cached_widgets);
			    nframe->cached_widgets = NULL;
			}
		    } else {
			cached_widgets = nframe->cached_widgets;
		    }
		}
		break;
	    }
	    nframe = nframe->next;
	}
    }
    /* If the window doesn't have a target anchor already,
     * see if there's one in this node's URL. */
    if (!target_anchor && win->current_node && !is_frame) {
        target_anchor = mo_url_extract_anchor(win->current_node->url);
        freeta = 1;
    }

    if (!txt || !txthead) {
        /* Just make it look OK...  band-aid city. */
        txt = strdup("\0");
        txthead = txt;
    }

    /* Track target anchor's visited */
    if (target_anchor && *target_anchor && track_anchors)
      mo_track_url_anchors_visited(url);	

    /* Save the encryption cipher (if any) */ 
    if (register_visit && encrypt_cipher && !is_frame)
      cipher = strdup(encrypt_cipher);
    else
      cipher = NULL;

    mo_set_text(load_win->scrolled_win, txt, ans, id,
		target_anchor, cached_widgets);

    /* HREF ListBox Stuff */
    if (win->links_win && !is_frame)
      mo_update_links_window(win);
    
    if (load_win->target_anchor)
      free(load_win->target_anchor);

    load_win->target_anchor = NULL;

    if (freeta)
      free(target_anchor);
  }

  if (url_base_override) {
      /* Get the override URL -- this should be all we need to do here. */
      url = url_base_override;
      mo_here_we_are_son(url);
  }

  /* Every time we view the document, we reset the search_start
     struct so searches will start at the beginning of the document. */
  ((ElementRef *)win->search_start)->id = 0;
  win->src_search_pos = 0;

  /* CURRENT_NODE ISN'T SET UNTIL HERE (assuming register_visit is 1).
   * Now that WbNtext has been set, we can pull out WbNtitle.
   * First, check and see if we have a URL.  If not, we probably
   * are only jumping around inside a document.
   */
  if (url && *url && !is_frame) {
      if (register_visit) {
          mo_record_visit(win, url, txt, txthead, ref, last_modified,
		          expires, cipher, encrypt_bits);
      } else {
          /* At the very least we want to pull out the new title,
           * if one exists. */
          if (win->current_node) {
              if (win->current_node->title)
                  free(win->current_node->title);
              win->current_node->title = mo_grok_title(win, url, ref);
          } else {
	      mo_node *node = (mo_node *)malloc(sizeof(mo_node));

	      node->url = strdup(url);
	      node->text = strdup(txt);
	      if (ref)
		  node->ref = strdup(ref);
	      else
		  node->ref = NULL;
	      node->title = mo_grok_title(win, url, ref);
	      node->authType = HTAA_NONE;
	      node->docid = 1;
	      node->last_modified = 0;
	      node->expires = 0;
	      node->previous = NULL;
	      node->next = NULL;
	      node->image_file = NULL;
	      node->cipher = NULL;
	      win->current_node = node;
	  }
      }
  } else if (url && *url && is_frame) {
      load_win->frametext = txt;
      load_win->frametexthead = txthead;
      if (register_visit && new_node && win->current_node) {
	  char *text = strdup(win->current_node->text);

          mo_record_visit(win, win->current_node->url, text, text,
			  win->current_node->ref,
			  win->current_node->last_modified,
		          win->current_node->expires,
			  win->current_node->cipher,
			  win->current_node->cipher_bits);
      }
  }

 if (!is_frame || new_node) {
  mo_reset_document_headers(win);

  if (win->history_list && win->current_node) {
      XmListSelectPos(win->history_list, win->current_node->position, False);
      XmListSetBottomPos(win->history_list, win->current_node->position);
  }

  /* Update source text if necessary. */
  if (win->source_text && XtIsManaged(win->source_text) && win->current_node) {
      XmxTextSetString(win->source_text, win->current_node->text);
      XmxTextSetString(win->source_url_text, win->current_node->url);
      XmxTextSetString(win->source_date_text,
		(win->current_node->last_modified ?
		 win->current_node->last_modified : "Unknown"));
  }

  if (win->current_node && win->current_node->previous)
      mo_back_possible(win);
  else
      mo_back_impossible(win);
  
  if (win->current_node && win->current_node->next)
      mo_forward_possible(win);
  else
      mo_forward_impossible(win);

  if (win->current_node && 
#ifndef VAXC
      mo_is_editable_annotation(win, win->current_node->text))
#else
     (mo_status *)mo_is_editable_annotation(win, win->current_node->text))
#endif /* VAXC, BSN */
      mo_annotate_edit_possible(win);
  else
      mo_annotate_edit_impossible(win);

  mo_gui_check_security_icon_in_win(win->current_node->authType, win);
 }
  /* Every time we load a new page set the focus to hotkeys.  We do
   * this because we may have gotten here via forms and since we
   * don't kill any widgets, some unmanaged widget out there could
   * have the focus */
  if (!focus && win->have_focus) {
      XtSetKeyboardFocus(win->base, win->view);
      /* Make traversal start at top of document should there be forms */
  }
  HTMLTraverseTabGroups(load_win->view, XmTRAVERSE_HOME);

  if (did_we_image_delay) {
      win->delay_image_loads = 0;
      did_we_image_delay = 0;
  }
  
  return mo_succeed;
}


/****************************************************************************
 * name:    mo_set_win_current_node
 * purpose: Given a window and a node, set the window's current node.
 *          This assumes node is already all put together, in the history
 *          list for the window, etc.
 * inputs:  
 *   - mo_window *win: The current window.
 *   - mo_node  *node: The node to use.
 * returns: 
 *   Result of calling mo_do_window_text.
 * remarks: 
 *   This routine is meant to be used to move forward, backward,
 *   and to arbitrarily locations in the history list.
 ****************************************************************************/
mo_status mo_set_win_current_node (mo_window *win, mo_node *node)
{
  typedef struct tmp {
      void *wid;
      void *form;
      struct tmp *next;
  } tmp;
  tmp *start = NULL;
  tmp *freeit;
  void *wto_free = NULL;
  void *fto_free = NULL;
  mo_status r;
      
  /* Must be NULL so we always check node URL for one */
  if (win->target_anchor) {
      free(win->target_anchor);
      win->target_anchor = NULL;
  }

  mo_snarf_scrollbar_values(win);

  if (win->current_node && win->reloading) {
      wto_free = win->current_node->cached_widgets;
      fto_free = win->current_node->cached_forms;

      win->current_node->cached_widgets = NULL;
      win->current_node->cached_forms = NULL;

      if (win->current_node->frames) {
	  mo_frame *frame = win->current_node->frames;

	  start = freeit = (tmp *)malloc(sizeof(tmp));
	  while (frame) {
	      WidgetInfo *wid = (WidgetInfo *) frame->cached_widgets;

	      freeit->next = NULL;
	      freeit->wid = NULL;
	      if (wid) {
		  wid->cache_count--;
		  if (wid->cache_count < 1) {
		      freeit->wid = frame->cached_widgets;
		  } else {
		      wid->cache_invalid = 1;
		  }
	      }
	      frame->cached_widgets = NULL;
	      frame = frame->next;
	      if (frame) {
		  freeit->next = (tmp *)malloc(sizeof(tmp));
		  freeit = freeit->next;
	      }
	  }
      }
  }

  win->current_node = node;

  mo_set_current_cached_win(win);

#ifdef CCI
  /* Send Anchor history to CCI if CCI wants it */
  MoCCISendAnchorToCCI(win->current_node->url, 0);
#endif

  win->new_node = 1;
  r = mo_do_window_text(win, win->current_node->url, 
                        win->current_node->text, 
                        win->current_node->texthead,
                        FALSE, win->current_node->ref,
			win->current_node->last_modified,
			win->current_node->expires);
  win->new_node = 0;

  if (win->reloading) {
      if (wto_free)
        HTMLFreeWidgetInfo(wto_free);
      if (fto_free)
        HTMLFreeFormInfo(fto_free);

      while (start) {
	  WidgetInfo *wid = (WidgetInfo *)start->wid;

	  if (wid) {
	      if (wid->cached_forms) {
		  HTMLFreeFormInfo(wid->cached_forms);
	      }
	      HTMLFreeWidgetInfo(start->wid);
	  }
	  freeit = start;
	  start = start->next;
	  free(freeit);
      }
      win->reloading = 0;
  }

  return r;
}


/****************************************************************************
 * name:    mo_reload_window_text
 * purpose: Reload the current window's text by pulling it over the
 *          network again.
 * inputs:  
 *   - mo_window *win: The current window.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This frees the current window's texthead.  This calls mo_pull_er_over
 *   directly.
 ****************************************************************************/
mo_status mo_reload_window_text (mo_window *win, int reload_images_also)
{
  HTMLWidget hw = (HTMLWidget) win->scrolled_win;
  mo_window *frame;
  static int init = 0;
  static int reload_images;
  static int nocache;

  if (!init) {
      reload_images = get_pref_boolean(eRELOAD_RELOADS_IMAGES);
      nocache = get_pref_boolean(eRELOAD_PRAGMA_NO_CACHE);
      init = 1;
  }
    
  if (win->current_node)
      win->current_node->docid = HTMLPositionToId(win->scrolled_win, 0, 3);

  mo_set_current_cached_win(win);

  /* Uh oh, this is trouble... */
  if (!win->current_node)
      return mo_load_window_text 
          (win, startup_document ? startup_document : home_document, NULL);

  /* Free all images in the current document. */
  if (reload_images || reload_images_also) {
      mo_zap_cached_images_here(win);
      if (win->frames) {
          mo_window *next = NULL;

	  frame = win->frames;
          while (frame) {
	      mo_zap_cached_images_here(frame);
	      if (frame->frames) {
		  next = frame->next_frame;
		  frame = frame->frames;
	      } else {
		  frame = frame->next_frame;
	      }
	      if (!frame && next) {
		  frame = next;
	      }
	  }
      }
  }

  /* Free the current document's text. */
  /* REALLY we shouldn't do this until we've verified we have new text that's
   * actually good here -- for instance, if we have a document on display,
   * then go to binary transfer mode, then do reload, we should pick up the
   * access override here and keep the old copy up on screen. */
  if (win->current_node->texthead) {
      free(win->current_node->texthead);
      win->current_node->texthead = NULL;
  }

  /* Free any current internal image viewer file */
  if (win->current_node->image_file) {
      remove(win->current_node->image_file);
      free(win->current_node->image_file);
      win->current_node->image_file = NULL;
  }

  /* Set binary_transfer as per current window. */
  binary_transfer = win->binary_transfer;
  interrupted = 0;

  if (nocache) {
      reloading = 1;
  }

  /* Stop animations because they will run if have to prompt for a cookie */
  hw->html.draw_count++;
  /* Stop frame animations */
  frame = win->frames;
  while (frame) {
      hw = (HTMLWidget) frame->scrolled_win;
      hw->html.draw_count++;
      frame = frame->next_frame;
  }

  win->current_node->text = mo_pull_er_over(win->current_node->url, 
                                            &win->current_node->texthead);

  /* Security type may change on a reload */
  win->current_node->authType = securityType;

  if (win->current_node->cipher)
    free(win->current_node->cipher);
  win->current_node->cipher = encrypt_cipher;
  encrypt_cipher = NULL;

  if (HTTP_last_modified) {
    if (win->current_node->last_modified)
      free(win->current_node->last_modified);
    win->current_node->last_modified = strdup(HTTP_last_modified);
  }
  if (HTTP_expires) {
    if (win->current_node->expires)
      free(win->current_node->expires);
    win->current_node->expires = strdup(HTTP_expires);
  }

  reloading = 0;

  /* Check for redirect or replacement cookiejar: URL */
  if (use_this_url_instead) {
      free(win->current_node->url);
      win->current_node->url = use_this_url_instead;
  }

  /* Check if frames have been disabled prior to reloading window with frames */
  if (win->frames && !win->frame_support) {
      /* If so, delete the frames */
      mo_delete_frames(win->frames);
      win->frames = NULL;
  }  
  /* Clear out the cached stuff, if any exists. */
  win->reloading = 1;

  mo_set_win_current_node(win, win->current_node);

  win->reloading = 0;

  /* If news: URL, then we need to auto-scroll to the >>> marker if it
   * is here.  We use a hacked version of the searching function here
   * which will need to be updated when we rewrite.
   */
  if (win->current_node && win->current_node->url &&
      !my_strncasecmp(win->current_node->url, "news:", 5)) {
	mo_search_window(win, ">>>", 0, 1, 1);
  }

  return mo_succeed;
}


/****************************************************************************
 * name:    mo_reload_frame_text
 * purpose: Reload the current frame's text by pulling it over the
 *          network again.
 * inputs:  
 *   - mo_window *win: The frame.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This frees the frame's texthead.  This calls mo_pull_er_over
 *   directly.
 ****************************************************************************/
mo_status mo_reload_frame_text (mo_window *win, mo_window *parent)
{
  HTMLWidget hw = (HTMLWidget) win->scrolled_win;
  mo_window *frame;
  mo_frame *nframe;
  static int init = 0;
  static int reload_images;
  static int nocache;
  int top_reloading;
    
  if (!init) {
      reload_images = get_pref_boolean(eRELOAD_RELOADS_IMAGES);
      nocache = get_pref_boolean(eRELOAD_PRAGMA_NO_CACHE);
      init = 1;
  }

  /* Free all images in the current document. */
  if (reload_images)
      mo_zap_cached_images_here(win);

  /* Set binary_transfer as per current window. */
  binary_transfer = parent->binary_transfer;

  interrupted = 0;

  if (nocache) {
      reloading = 1;
  }

  /* Stop animations because they will run if have to prompt for a cookie */
  hw->html.draw_count++;
  /* Stop frame animations */
  frame = win->frames;
  while (frame) {
      hw = (HTMLWidget) frame->scrolled_win;
      hw->html.draw_count++;
      frame = frame->next_frame;
  }

  win->frametext = mo_pull_er_over(win->frameurl, &win->frametexthead);

  /* Reset HTTP no_cache stuff */
  reloading = 0;

  /* Replace text in cache */
  mo_cache_data(win->frameurl, win->frametext, 1);
  /* Correct cache access count (reduce by 1 because mo_cache_data added 1) */
  mo_deaccess_cached_local_name(win->frameurl);
  /* Clear out the cached stuff, if any exists. */
  nframe = parent->current_node->frames;
  while (nframe) {
      if (!strcmp(nframe->url, win->frameurl)) {
	  nframe->docid = 1;
	  if (nframe->cached_widgets) {
	      WidgetInfo *wid = (WidgetInfo *) nframe->cached_widgets;

	      wid->cache_count--;
	      if (wid->cache_count < 1) {
		  if (wid->cached_forms) {
		      HTMLFreeFormInfo(wid->cached_forms);
		  }
		  HTMLFreeWidgetInfo(nframe->cached_widgets);
	      } else {
		  wid->cache_invalid = 1;
	      }
	      nframe->cached_widgets = NULL;
	  }
	  break;
      }
      nframe = nframe->next;
  }
  /* Check for redirect or replacement cookiejar: URL */
  if (use_this_url_instead) {
      free(win->frameurl);
      win->frameurl = use_this_url_instead;
  }

  top_reloading = parent->reloading;
  parent->reloading = 1;

  parent->do_frame = win;
  mo_do_window_text(parent, win->frameurl, win->frametext, win->frametext, 0,
		    NULL, 0, 0);
  parent->do_frame = NULL;

  parent->reloading = top_reloading;

  return mo_succeed;
}


/****************************************************************************
 * name:    mo_refresh_window_text
 * purpose: Reload the current window's text without pulling it over the net.
 * inputs:  
 *   - mo_window *win: The current window.
 * returns: 
 *   mo_succeed
 * remarks: 
 ****************************************************************************/
mo_status mo_refresh_window_text (mo_window *win)
{

  mo_set_current_cached_win(win);

  if (!win->current_node) {
      return mo_fail;
  }

  /* Clear out the cached stuff, if any exists. */
  win->reloading = 1;

  mo_set_win_current_node(win, win->current_node);

  mo_gui_check_security_icon_in_win(win->current_node->authType, win);

  return mo_succeed;
}


/****************************************************************************
 * name:    mo_load_window_text
 * purpose: Given a window and a raw URL, load the window.  The window
 *          is assumed to already exist with a document inside, etc.
 * inputs:  
 *   - mo_window *top: The current top level window.
 *   - char      *url: The URL to load.
 *   - char      *ref: The reference ("parent") URL.
 *         NOTE: actually, the ref field is the citation hypertext
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This is getting ugly.
 ****************************************************************************/
mo_status mo_load_window_text (mo_window *top, char *url, char *ref)
{
    char *newtext = NULL, *newtexthead = NULL;
    char *last_modified = 0;
    char *expires = 0;
    mo_status return_stat = mo_succeed;
    int free_url = 0;
    char *ori_url = url;
    int did_head = do_head;
    HTMLWidget hw;
    mo_window *win;

    if (top->do_frame)
	win = top->do_frame;
    else
	win = top;

    hw = (HTMLWidget) win->scrolled_win;

    win->target_anchor = mo_url_extract_anchor(url);

    if ((newtext = mo_special_urls(url))) {
        if (newtext[0] == '0') {
            url = &newtext[1];
            newtext = NULL;
        } else {
            newtexthead = newtext;
            goto special_urls;
        }   
    }
    
    /* If we're just referencing an anchor inside a document,
     * do the right thing.
     */
    if (url && *url == '#') {
      /* Now we make a copy of the current text and make sure we ask
       * for a new mo_node and entry in the history list. */
	if (win->is_frame) {
	    newtext = strdup(win->frametext);
        } else if (win->current_node) {
            newtext = strdup(win->current_node->text);
        } else {
            newtext = strdup("lose");
        }
        newtexthead = newtext;
	if (!win->is_frame) {
            url = mo_url_canonicalize_keep_anchor 
                (url, win->current_node ? win->current_node->url : "");
	} else {
            url = mo_url_canonicalize_keep_anchor(url, win->frameurl);
	}
        free_url = 1;
#ifdef CCI
        /* Send Anchor history to CCI if CCI wants it */
        MoCCISendAnchorToCCI(url, 1);
#endif
    } else {
        /* Get a full address for this URL. */
        /* Under some circumstances we may not have a current node yet
           and may wish to just run with it... so check for that. */
	if (win->is_frame) {
	    /* This is probably redundant */
            url = mo_url_canonicalize_keep_anchor(url, win->frameurl);
	    free_url = 1;
        } else if (win->current_node && win->current_node->url) {
            url = mo_url_canonicalize_keep_anchor(url, win->current_node->url);
	    free_url = 1;
        }
        /* Set binary_transfer as per current window. */
        binary_transfer = top->binary_transfer;
        mo_set_current_cached_win(top);

        {
            char *canon = mo_url_canonicalize(url, "");
	    mo_window *frame;

            interrupted = 0;
#ifdef CCI
            CCIprotocol_handler_found = 0;
 
            /* Send Anchor history to CCI if CCI wants to handle it */
            MoCCISendAnchorToCCI(url, 3);
 
            if (CCIprotocol_handler_found)
                return return_stat;         /* success */

            MoCCISendAnchorToCCI(url, 1);
#endif
	    /* Stop animations; they will run if have to prompt for a cookie */
	    hw->html.draw_count++;
	    /* Stop frame animations, unless loading a frame */
	    if (top->do_frame)
		frame = NULL;
	    else
		frame = win->frames;
	    while (frame) {
		hw = (HTMLWidget) frame->scrolled_win;
		hw->html.draw_count++;
		frame = frame->next_frame;
	    }

	    if (my_strncasecmp(ori_url, "cookiejar:", 10)) {
                newtext = mo_pull_er_over(canon, &newtexthead);
	    } else {
                newtext = mo_pull_er_over(ori_url, &newtexthead);
		if (url[10])	/* Original string is not modifiable */
		    url[10] = '\0';
	    }
            /* 
	     * Added so MCCIRequestGetURL could return failed when
	     * url fails
	     */
            if (newtext)
                if ((!strncmp(newtext, "<H1>ERROR<H1>", 10)) ||
                    (!strncmp(newtext, 
                              "<HEAD><TITLE>404 Not Found</TITLE></HEAD>", 28)))
                    return_stat = mo_fail;

	    /* Yes this is a really big hack (ETG) */
#ifdef CCI
            if (win->target_anchor && *win->target_anchor)
                MoCCIAddAnchorToURL(canon, url);
#endif
            if (HTTP_last_modified) {
                last_modified = strdup(HTTP_last_modified);
            }
            if (HTTP_expires) {
                expires = strdup(HTTP_expires);
            }
            free(canon);
        }

	/* Check for redirect URL */
        if (use_this_url_instead) {
            mo_here_we_are_son(url);
	    if (free_url)
		free(url);
            url = use_this_url_instead;
	    free_url = 1;
            
            /* Go get another target_anchor. */
            if (win->target_anchor)
                free(win->target_anchor);
            win->target_anchor = mo_url_extract_anchor(url);
       }
    }

        /* Now, if it's a telnet session, there should be no need
         * to do anything else.  Also check for override in text itself.
         */
        if (!my_strncasecmp(url, "telnet:", 7) ||
            !my_strncasecmp(url, "tn3270:", 7) ||
            !my_strncasecmp(url, "rlogin:", 7) ||
            (newtext && !strncmp(newtext, "<mosaic-access-override>", 24))) {

            /* We don't need this anymore. */
            free(newtext);

            /* We still want a global history entry but NOT a 
             * window history entry. */
            mo_here_we_are_son(url);
            /* ... and we want to redisplay the current window to
             * get the effect of the history entry today, not tomorrow. */
            mo_redisplay_window(win);
            /* We're not busy anymore... */
            mo_gui_done_with_icon();

        } else if (newtext) {
        
            /* Not a telnet session and not an override, but text present
               (the "usual" case): */
#ifdef CCI
            /* First check if we are using cci Get, if so, don't display
               the error message */
            if (cci_get && (return_stat == mo_fail)) {
#ifndef DISABLE_TRACE
                if (srcTrace) {
                    fprintf(stderr, "MCCI GET has passed in a wrong url\n");
                }
#endif
            } else
#endif
            {
              special_urls:
                /* Set the window text. */
                mo_do_window_text(top, url, newtext, newtexthead,
				  (did_head ? 2 : 1), 
                                  ref, last_modified, expires);
            }
        } else {
            /* No text at all. */
            mo_gui_done_with_icon();
        }

#ifdef CCI
    /* Send Anchor history to CCI if CCI wants it */
    MoCCISendAnchorToCCI(url, 2);
    /* First check if we are using CCI Get, if so, don't display
       the error message */
    if (cci_get && (return_stat == mo_fail)) {
#ifndef DISABLE_TRACE
	if (srcTrace) {
	    fprintf(stderr, "MCCI GET has passed in a wrong url\n");
	}
#endif
    } else
#endif
	if (!win->is_frame && win->current_node) {
            mo_gui_check_security_icon_in_win(win->current_node->authType, win);
    }
    if (last_modified)
	free(last_modified);
    if (expires)
	free(expires);

/*
  if (cci_event) MoCCISendEventOutput(LINK_LOADED);
*/

  /* If news: URL, then we need to auto-scroll to the >>> marker if it
     is here.  We use a hacked version of the searching function here
     which will need to be updated when we rewrite. */

  if (!win->is_frame && win->current_node && win->current_node->url &&
      !my_strncasecmp(win->current_node->url, "news:", 5)) {
	mo_search_window(win, ">>>", 0, 1, 1);
  }

  if (free_url)
      free(url);
  return return_stat;
}


mo_status mo_post_load_window_text (mo_window *top, char *url,
                                    char *content_type, char *post_data)
{
  char *newtext = NULL, *newtexthead = NULL;
  mo_window *win;
#ifdef CCI
  char *actionID;
#endif
  int free_url = 0;

  if (top->do_frame)
      win = top->do_frame;
  else
      win = top;

  win->target_anchor = mo_url_extract_anchor(url);

#ifdef CCI
  actionID = strdup(url);       /* Make a copy of url for cci's register id */
#endif

  /* If we're just referencing an anchor inside a document,
     do the right thing. */
  if (url && *url == '#') {
      /* I believe this section is never executed */
      /* Now we make a copy of the current text and make sure we ask
         for a new mo_node and entry in the history list. */
      if (win->is_frame) {
	  newtext = strdup(win->frametext);
      } else if (win->current_node) {
          newtext = strdup(win->current_node->text);
      } else {
          newtext = strdup("lose");
      }
      newtexthead = newtext;
      if (!win->is_frame) {
          url = mo_url_canonicalize_keep_anchor 
              (url, win->current_node ? win->current_node->url : "");
      } else {
          url = mo_url_canonicalize_keep_anchor(url, win->frameurl);
      }
      free_url = 1;
  } else {
      /* Get a full address for this URL. */
      /* Under some circumstances we may not have a current node yet
         and may wish to just run with it... so check for that. */
      if (win->is_frame) {
          url = mo_url_canonicalize_keep_anchor(url, win->frameurl);
	  free_url = 1;
      } else if (win->current_node && win->current_node->url) {
          url = mo_url_canonicalize_keep_anchor(url, win->current_node->url);
	  free_url = 1;
      }

      /* Set binary_transfer as per current window. */
      binary_transfer = top->binary_transfer;
      mo_set_current_cached_win(top);

      {
        char *canon = mo_url_canonicalize(url, "");
        interrupted = 0;

#ifdef CCI
	if (!MoCCIFormToClient(actionID, NULL, content_type, post_data, 0))
#endif
	    /* Always call if no CCI stuff */
       	    newtext = mo_post_pull_er_over(canon, content_type, 
			post_data, &newtexthead);
        free(canon);
      }
      /* Check for redirect URL */
      if (use_this_url_instead) {
            mo_here_we_are_son(url);
	    if (free_url)
		free(url);
            url = use_this_url_instead;
	    free_url = 1;
      }
  }

  /* Now, if it's a telnet session, there should be no need
     to do anything else.  Also check for override in text itself. */
  if (!my_strncasecmp(url, "telnet:", 7) ||
      !my_strncasecmp(url, "tn3270:", 7) ||
      !my_strncasecmp(url, "rlogin:", 7) ||
      (newtext && !strncmp(newtext, "<mosaic-access-override>", 24))) {
      /* We don't need this anymore. */
      free(newtext);

      /* We still want a global history entry but NOT a 
         window history entry. */
      mo_here_we_are_son(url);
      /* ... and we want to redisplay the current window to
         get the effect of the history entry today, not tomorrow. */
      mo_redisplay_window(win);
      /* We're not busy anymore... */
      mo_gui_done_with_icon();
  } else if (newtext) {
      /* Not a telnet session and not an override, but text present
         (the "usual" case): */

      /* Set the window text. */
      mo_do_window_text(top, url, newtext, newtexthead, 1, NULL, 0, 0);
  } else {
      /* No text at all. */
      mo_gui_done_with_icon();
  }

/*
  if (cci_event) MoCCISendEventOutput(LINK_LOADED);
*/

  if (free_url)
      free(url);

  return mo_succeed;
}


/****************************************************************************
 * name:    mo_duplicate_window_text
 * purpose: Given an old window and a new window, make a copy of the text
 *          in the old window and install it in the new window.
 * inputs:  
 *   - mo_window *oldw: The old window.
 *   - mo_window *neww: The new window.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This is how windows are cloned: a new window is created and this
 *   call sets up its contents.
 ****************************************************************************/
mo_status mo_duplicate_window_text (mo_window *oldw, mo_window *neww)
{
  /* We can get away with just cloning text here and forgetting
     about texthead, obviously, since we're making a new copy. */
  char *newtext;

  if (!oldw->current_node)
    return mo_fail;

  newtext = strdup(oldw->current_node->text);

  mo_do_window_text(neww, oldw->current_node->url, 
     newtext, newtext, TRUE, 
     oldw->current_node->ref,
     oldw->current_node->last_modified,
     oldw->current_node->expires);

  return mo_succeed;
}


/****************************************************************************
 * name:    mo_access_document
 * purpose: Given a URL, access the document by loading the current 
 *          window's text.
 * inputs:  
 *   - mo_window *win: The current window.
 *   - char      *url: URL to access.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This should be the standard call for accessing a document.
 ****************************************************************************/
mo_status mo_access_document (mo_window *win, char *url)
{

  mo_set_current_cached_win(win);

  mo_load_window_text(win, url, NULL);

  return mo_succeed;
}

