/* Copyright 1997 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/***************************************************/
/* File   : Fetch.c                                */
/* Purpose: Low-level page fetch related functions */
/*          functions (as opposed to fetch.c where */
/*          all the higher level stuff goes on)    */
/* Author : A.D.Hodgkinson                         */
/* History: 25-Nov-96: Created                     */
/***************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "swis.h"
#include "flex.h"

#include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */

#include "wimp.h"
#include "wimplib.h"
#include "event.h"

#include "svcprint.h"
#include "Global.h"
#include "MiscDefs.h"
#include "TBEvents.h"
#include "Utils.h"

#include "Authorise.h"
#include "Browser.h"
#include "Cookies.h"
#include "FontManage.h"
#include "Forms.h"
#include "Frames.h"
#include "History.h"
#include "Images.h"
#include "Memory.h"
#include "Meta.h"
#include "Redraw.h"
#include "Reformat.h"
#include "Toolbars.h"
#include "URLutils.h"
#include "Windows.h"

#include "Fetch.h"

/* Globals */

int authorising = 0; /* Authorisation for a fetch is in progress */

/* Statics */

static urlstat  * fetch_list    = NULL; /* Points to the head of the linked list of structures. */
static char     * fetch_buffer  = NULL; /* For html_get_next_token to shovel in data.           */

/* Local definitons */

#define FetchBufferSize  8192 /* Buffer for getting data from the URL module in html_get_next_token. */

#define AuthorisationStr "Authorization: Basic "

/* Local compilation options */

#define FRAMES_SUPPORT
#undef  DUMP_HEADERS

/* Static function prototypes */

static HStream * fetch_find_anchor_token_r (browser_data * b, HStream * streambase, char * anchor);

/*************************************************/
/* fetch_start()                                 */
/*                                               */
/* Initiate a fetch from some URL.               */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             for the browser window that the   */
/*             fetch relates to; the URL is      */
/*             pointed to in that structure, as  */
/*             the last item in the history.     */
/*                                               */
/* Returns:    A pointer to a _kernel_oserror    */
/*             structure if an error occured, or */
/*             NULL if there was no error.       */
/*************************************************/

_kernel_oserror * fetch_start(browser_data * b)
{
  int               handle, method;
  _kernel_oserror * e;

  #ifdef TRACE
    if (tl & (1u<<6)) Printf("\nfetch_start: Called\n");
  #endif

  /* (Order of evaluation ensures the check for the contents of */
  /* the memory pointed to by browser_fetch_url only occurs if  */
  /* the pointer isn't null)                                    */

  if (!browser_fetch_url(b) || !*browser_fetch_url(b))
  {
    b->fetch_status = BS_IDLE;
    toolbars_cancel_status(b, Toolbars_Status_Fetching);
    return NULL;
  }

  /* URL method is set to POST if there is forms data, or GET */
  /* if not (see Fetch.c for the definitions)                 */

  method = b->extradata ? URL_Method_http_POST : URL_Method_http_GET;

  /* Find out if this is an internal URL, and if so, */
  /* set the 'displayed' field in the browser_data   */
  /* struct appropriately.                           */

  urlutils_set_displayed(b, b->urlfdata);

  /* Get, and start parsing the document */

  e = html_get(b->urlfdata,   /* Required document */
               b->extradata,  /* Extra bits to append for POST etc */
               &handle,       /* The library's handle for request */
               method,        /* See above - POST or GET at this point */
               NULL,          /* User name for Mailserv */
               1,             /* Allow HTML parsing, 1 = yes, 0 = no */
               0);            /* If 1, don't go through a proxy - e.g. for a reload */

  #ifdef TRACE
    if (b->extradata)
    {
      flexcount -= flex_size((flex_ptr) &b->extradata);
      if (tl & (1u<<13)) Printf("**   flexcount: %d\n",flexcount);
    }
  #endif

  if (b->extradata) flex_free((flex_ptr) &b->extradata);

  if (e)
  {
    b->fetch_status = BS_IDLE;
    toolbars_cancel_status(b, Toolbars_Status_Fetching);
    return e;
  }

  /* No error, so signal that the fetch has started. */

  b->fetch_handle = handle;
  b->fetch_status = BS_STARTED;
  toolbars_update_status(b, Toolbars_Status_Fetching);

  /* At this point e will always be NULL but that might change, */
  /* so the full trace code is being left in for now            */

  #ifdef TRACE
    if (tl & (1u<<6))
    {
      if (e) Printf("fetch_start: Exiting with error\n");
      else Printf("fetch_start: Successful\n");
    }
  #endif

  return e;
}

/*************************************************/
/* fetch_fetching()                              */
/*                                               */
/* Returns 1 if there is a fetch in progress     */
/* according to the contents of the data that    */
/* was pointed to (see Parameters), else 0.      */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the inquiry.          */
/*                                               */
/* Returns:    1 if a fetch is in progress;      */
/*             0 if a fetch is not in progress.  */
/*************************************************/

int fetch_fetching(browser_data * b)
{
  /* This is currently very simple - a fetch is considered to be in */
  /* progress so long as the fetch_status doesn't indicate BS_IDLE. */

  return (b->fetch_status != BS_IDLE);
}

/*************************************************/
/* fetch_token_data_address()                    */
/*                                               */
/* Returns a pointer to data associated with a   */
/* token, if it holds text, or NULL if that data */
/* means something else. (A token is an HStream  */
/* structure - see HTMLLib:struct.h).            */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             associated with the token;        */
/*                                               */
/*             Pointer to the token.             */
/*                                               */
/* Returns:    Pointer to the data if it is      */
/*             text, else NULL.                  */
/*************************************************/

void * fetch_token_data_address(browser_data * b, HStream * token)
{
  /* No data for horizontal rules or images */

  if (token->style & HR)                                                 return NULL;
  if (token->style & IMG)                                                return NULL;
  if ((token->style & INPUT) && HtmlINPUTtype(token) == inputtype_IMAGE) return NULL;

  /* Otherwise, return a pointer to the text */

  return token->text;
}

/*************************************************/
/* fetch_find_name_tag()                         */
/*                                               */
/* Finds the # separating an anchor name in a    */
/* URL.                                          */
/*                                               */
/* Parameters: A pointer to the URL string.      */
/*                                               */
/* Returns:    A pointer to the anchor string,   */
/*             including the leading #           */
/*************************************************/

char * fetch_find_name_tag(char * url)
{
  char * p;

  p = strchr(url,'/'); /* Get past the first /, as in http:/ */

  if (p) p = strchr(p + 1, '/'); /* Get past second /, as in http://                     */
  if (p) p = strchr(p + 1, '/'); /* Get past site specifier, as in http://www.this.that/ */
  if (p) p = strchr(p + 1, '#'); /* Find # in the document path                          */

  return p;
}

/*************************************************/
/* fetch_find_anchor_token()                     */
/*                                               */
/* Returns the address of the first token in the */
/* token list which has the given anchor name    */
/* associated with it, or NULL if none can be    */
/* found.                                        */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the token list;       */
/*                                               */
/*             Pointer to the anchor name.       */
/*                                               */
/* Returns:    Pointer to the token associated   */
/*             with the given anchor name, or    */
/*             NULL if none is found.            */
/*************************************************/

HStream * fetch_find_anchor_token(browser_data * b, char * anchor)
{
  return fetch_find_anchor_token_r(b, b->stream, anchor);
}

/*************************************************/
/* fetch_find_anchor_token_r()                   */
/*                                               */
/* Recursive back-end to fetch_find_anchor_token */
/* - takes an extra parameter giving the top of  */
/* the HStream list to scan.                     */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the token list;       */
/*                                               */
/*             Pointer to first item in HStream  */
/*             list to scan;                     */
/*                                               */
/*             Pointer to the anchor name.       */
/*                                               */
/* Returns:    As fetch_find_anchor_token.       */
/*************************************************/

static HStream * fetch_find_anchor_token_r(browser_data * b, HStream * streambase, char * anchor)
{
  HStream * tp;

  tp = streambase;

  /* Go down the token list, checking if a token represents an   */
  /* anchor, has a name, and that name matches the given one. If */
  /* so, return the token address, else go onto the next token.  */

  while (tp && (tp->flags & HFlags_DealtWithToken))
  {
    /* A table token? */

    if (
         tp->tag == TABLE &&
         ISBODY(tp)
       )
    {
      table_stream   * table      = (table_stream *) tp;
      table_row      * row        = NULL;
      table_headdata * head       = NULL;
      HStream        * tf;
      int              cellcount  = 0;
      int              cellmax    = table->ColSpan * table->RowSpan;

      /* Scan the table for the token, using a recursive */
      /* call to this function for each cell.            */

      if (table->cells)
      {
        row = table->List;

        while (row && cellcount < cellmax)
        {
          head = row->List;

          while (head && cellcount < cellmax)
          {
            switch (head->Tag)
            {
              case TagTableData:
              case TagTableHead:
              {
                tf = fetch_find_anchor_token_r(b, (HStream *) head->List, anchor);
                if (tf) return tf;
              }
              break;
            }

            cellcount ++;

            head = head->Next;

          /* Closure of 'while (head && ...)' */
          }

          row = row->Next;

        /* Closure of 'while (row && ...)' */
        }

      /* Closure of 'if (table->cells)' */
      }

    /* Closure of check to see if token represents a table */
    }
    else if (
              (tp->style & A) &&
              tp->name        &&
              !strcmp(tp->name, anchor)
            )
            return tp;

    tp = tp->next;
  }

  /* No match found - return NULL. */

  return NULL;
}

/*************************************************/
/* fetch_preprocess_token()                      */
/*                                               */
/* Takes a token for a given browser_data struct */
/* and preprocesses it - e.g. tells the image    */
/* library about image tokens so fetches can     */
/* start for those images.                       */
/*                                               */
/* Parameters: A pointer to a browser_data       */
/*             structure relevant to the token;  */
/*                                               */
/*             Pointer to the token.             */
/*************************************************/

void fetch_preprocess_token(browser_data * b, HStream * tptr)
{
  int reprocess_table = 0;

  /* finaltoken keeps track of the last token dealt with by this */
  /* routine, in the main token stream.                          */

  if (!tptr->parent) b->finaltoken = tptr;

  /* Are we reprocessing the contents of a table tag which has been */
  /* dealt with before?                                             */

  if (tptr->tag == TABLE && ISBODY(tptr)) reprocess_table = 1;

  /* Deal with smart quotes etc. */

  // This could have side effects placed here...! Sort it out! - the function just returns at present.
  reformat_change_text(b, tptr);

  /* Deal with document body tags (not within HEAD, FRAMESET etc. containers) */

  if (ISBODY(tptr))
  {
    /* Don't reprocess the token generally - this may accidentally */
    /* clear formflag, say, if the high level table structures     */
    /* we are passing through on the way to the lower level ones   */
    /* don't have the FORM bit set. Other such consequences are    */
    /* avoided by only doing the table handling if this is a table */
    /* tag with the HFlags_DealtWithToken bit set in its flags.    */

    if (!reprocess_table)
    {
      /* If the 'style' entry has the image (IMG) bit set, ask */
      /* the image library to handle a new image. tptr->src    */
      /* will be a char * to the URL of the image.             */

      if (tptr->style & IMG)
      {
        if (fetch_chkerror(b,
                           image_new_image(b,
                                           tptr->src ? tptr->src : "",
                                           tptr,
                                           0))) return;
      }

      /* Handle some form tags */

      if (tptr->style & FORM)
      {
        /* If there are no forms in this fetch so far, create a new one */

        if (!b->formflag)
        {
          if (fetch_chkerror(b, form_new_form(b, tptr))) return;
        }

        /* Deal with creating a new field as appropriate */

        if (tptr->style & INPUT)
        {
          switch(HtmlINPUTtype(tptr))
          {
            case inputtype_TEXT:    if (fetch_chkerror(b, form_new_field(b, tptr, form_text,     tptr->text                     ))) return; break;
            case inputtype_PASSWORD:if (fetch_chkerror(b, form_new_field(b, tptr, form_password, tptr->text                     ))) return; break;
            case inputtype_CHECKBOX:if (fetch_chkerror(b, form_new_field(b, tptr, form_checkbox, (char *) HtmlINPUTchecked(tptr)))) return; break;
            case inputtype_RADIO:   if (fetch_chkerror(b, form_new_field(b, tptr, form_radio,    (char *) HtmlINPUTchecked(tptr)))) return; break;
            case inputtype_IMAGE:   if (fetch_chkerror(b, form_new_field(b, tptr, form_image,    NULL                           ))) return; break;
            case inputtype_HIDDEN:  if (fetch_chkerror(b, form_new_field(b, tptr, form_hidden,   NULL                           ))) return; break;
            case inputtype_SUBMIT:  if (fetch_chkerror(b, form_new_field(b, tptr, form_submit,   NULL                           ))) return; break;
            case inputtype_RESET:   if (fetch_chkerror(b, form_new_field(b, tptr, form_reset,    NULL                           ))) return; break;
          }
        }

        /* Handle text areas */

        if ((tptr->style) & TEXTAREA)
        {
          if (fetch_chkerror(b, form_new_field(b, tptr, form_textarea, tptr->text))) return;
        }

        /* Handle selection buttons */

        if ((tptr->style) & SELECT)
        {
          if (fetch_chkerror(b, form_new_field(b, tptr, form_select, (char *) HtmlSELECToptions(tptr)))) return;
        }

        b->formflag = 1;
      }
      else b->formflag = 0;
    }

    /* Tables - need to preprocess any HStreams attached as part of a table */
    /* tag. Because any one token is only run through this preprocessor     */
    /* once, and because when this table tag is run through it all of the   */
    /* HStreams within may not have arrived yet (the page is only partially */
    /* fetched), it is still necessary to rescan the attached HStreams at a */
    /* later date (e.g. as part of the reformatting process) to ensure they */
    /* are all preprocessed correctly.                                      */

    if (tptr->tag == TABLE)
    {
      table_stream   * table = (table_stream *) tptr;
      table_row      * R;
      table_headdata * D;
      HStream        * attached;

      R = table->List;

      /* Scan the rows and cells */

      while (R)
      {
        D = R->List;

        while (D)
        {
          if (D->Tag)
          {
            switch (D->Tag)
            {
              case TagTableData:
              case TagTableHead:
              {
                attached = (HStream *) D->List;

                /* Preprocess any attached HStream list - must */
                /* check table tags even if they've been done  */
                /* before to look for new HStreams, otherwise  */
                /* avoid preprocessing the same thing twice.   */

                while (attached)
                {
                  if (
                       (
                         ISBODY(attached) &&
                         attached->tag == TABLE
                       )
                       ||
                       (
                         !(attached->flags & HFlags_DealtWithToken)
                       )
                     )
                     fetch_preprocess_token(b, attached);

                  attached = attached->next;
                }
              }
              break;
            }
          }

          D = D->Next;
        }

        R = R->Next;
      }
    }

    /* Closure of long 'if' to see if the HStream structure represented */
    /* a body tag or header information - the code is run if it's a     */
    /* body tag.                                                        */
  }

  #ifdef FRAMES_SUPPORT

    else if ISFRAMESET(tptr)
    {
      browser_data * parent;
      browser_data * child = b;

      parent = b->parent;
      if (!parent) parent = b;

      if (tptr->size)
      {
        int level = tptr->size;

        /* If filling_frame is equal to the number of children, then */
        /* they've all been filled - the frameset must be broken.    */

        if (
             !child->nchildren ||
             (
               child->nchildren                        &&
               child->filling_frame < child->nchildren
             )
           )
        {
          while (level > 1)
          {
            /* If in a nested frameset, find out what browser_data struct  */
            /* to put the frames in. This should go in the next frame that */
            /* is to be filled in according to the parent.                 */

            child = (browser_data *) child->children[child->filling_frame];
            level--;
          }

          /* If stepping down a level, i.e. after a /frameset tag, */
          /* will want to increment the filling_frame field for    */
          /* this browser to say that the child we just stepped    */
          /* down for has been filled with a frameset. There's the */
          /* complication of a /frameset being followed by another */
          /* frameset and the level therefore staying the same;    */
          /* this is dealt with in the frameset section below.     */

          if (tptr->size < parent->nesting_level)
          {
            child->filling_frame++;
          }

          /* The aforementioned frameset section... */

          if (!(tptr->style & FRAME))
          {
            /* Define a new frameset. */

            if (tptr->size == parent->nesting_level && child->parent)
            {
              /* If at the same level as before on receiving a frameset  */
              /* tag, must be doing nested frames and just had a         */
              /* /frameset before this tag came along. So need to        */
              /* increment the filling_frame counter of the *parent*     */
              /* (remember, we're at the level of the frame to fill in,  */
              /* not in the level below as with the code above that      */
              /* checked the level had stepped down). Therefore, need    */
              /* to find out again what browser_data struct is to be     */
              /* given the frameset based on the new filled_frame value. */

              child->parent->filling_frame++;
              child = (browser_data *) child->parent->children[child->parent->filling_frame];
            }

            /* Must force scrollbars off in this current view, */
            /* as a frameset is about to appear over it.       */

            windows_check_tools(child, NULL);

            /* Finally, define the frameset at the required depth. */

            frames_define_frameset(child, tptr);
          }
          else
          {
            /* Fill in details of a frame. */

            frames_define_frame(child, tptr);
          }

          parent->nesting_level = tptr->size;
        }

        #ifdef STRICT_PARSER

          else
          {
            erb.errnum = Utils_Error_Custom_Message;

            StrNCpy0(erb.errmess,
                     lookup_token("FramNest:Frames definition is badly nested; could not complete the frames layout.",
                                  0,0));

            show_error_ret(&erb);
          }

        #endif
      }
    }

  #endif

  else if ISHEAD(tptr)
  {
    /* Deal with header (HEAD) tags */

    if ((tptr->style & TITLE) && tptr->text)
    {
      /* The tag is TITLE, and there is title text. */

      char   title[MaxTiBLen - 1];
      char * p = title;
      char * end;

      /* Can't overflow maximum length so just crop the string to fit */

      StrNCpy0(title, tptr->text);

      /* Strip any spaces at the start */

      while (*p == ' ') p++;

      /* Strip any spaces at the end */

      end = (char *) ((int) p + strlen(p) - 1);
      if (end > p) while (*end == ' ') *end-- = 0;

      /* If there's anything left now... */

      if (*p != 0)
      {
        /* Set the title */

        if (!b->ancestor && fetch_chkerror(b, window_set_title(0, b->self_id, p))) return;

        /* Try adding this title to the history, ignoring any errors */

        history_add_title(p, browser_fetch_url(b));
      }
    }

    if (tptr->tag == BODY)
    {
      /* The BODY tag. All sorts of exciting stuff in here... */

      if (HtmlBODYbackground(tptr))
      {
        /* If there's a URL for the image, ask the image library for it */
        /* and remember the image number in the browser_data structure  */

        image_new_image(b, HtmlBODYbackground(tptr), tptr, 2);
      }

      /* Get the 24-bit background colour, if any. */

      if (HtmlBODYbgcolour(tptr) != NULL_COLOUR)
      {
        b->backgroundcol = HtmlBODYbgcolour(tptr);

        #ifdef TRACE
          if (tl & (1u<<6)) Printf("fetch_preprocess_token: Background colour set to %d\n", b->backgroundcol);
        #endif

        /* If there's no actual background image, set the anti-alias */
        /* colour to be the same as the background colour.           */

        if (b->backimage < 0) b->aacol = b->backgroundcol;

        browser_update_bottom(b, 0);
      }

      /* Get the rest of the colour info out. */

      if (HtmlBODYtext (tptr) != NULL_COLOUR) b->textcol = HtmlBODYtext (tptr);
      if (HtmlBODYlink (tptr) != NULL_COLOUR) b->linkcol = HtmlBODYlink (tptr);
      if (HtmlBODYvlink(tptr) != NULL_COLOUR) b->usedcol = HtmlBODYvlink(tptr);
      if (HtmlBODYalink(tptr) != NULL_COLOUR) b->follcol = HtmlBODYalink(tptr);

      /* Also pull out the onload and onunload scripts. */

      if (HtmlBODYonload  (tptr)) b->onload   = HtmlBODYonload  (tptr);
      if (HtmlBODYonunload(tptr)) b->onunload = HtmlBODYonunload(tptr);
    }

    /* Deal with META... tags */

    if (tptr->tag == META)
    {
      meta_process_tag(b, tptr);
    }

    /* Closure of long else to see if the HStream structure represented */
    /* a body tag or header information - the code is run if it's a     */
    /* head tag.                                                        */
  }

  /* If we've reached here, the token has been dealt with */
  /* successfully - so mark this in its flags word.       */

  tptr->flags |= HFlags_DealtWithToken;

  return;
}

/*************************************************/
/* fetch_fetcher()                               */
/*                                               */
/* The main part of the fetch routine. Handles   */
/* the processing of data from the URL module,   */
/* after fetch_start() has asked it to start     */
/* getting data from a server.                   */
/*                                               */
/* Parameters: A pointer to a browser_data       */
/*             structure, to which the fetch     */
/*             relates.                          */
/*************************************************/

void fetch_fetcher(browser_data * b)
{
  HStream  * tptr;
  int        start = -1;
  int        i, remain, sofar, waiting;

  for (i = 0; i < 20; i ++) /* Get several tokens on each null event */
  {
    /* For BS_DATAFETCH, save the data to a file */

    if (b->fetch_status == BS_DATAFETCH)
    {
      #ifdef TRACE
        if (tl & (1u<<6)) Printf("fetch_fetcher: fetch_status = BS_DATAFETCH.\n");
      #endif

      /* This code gets called by the stuff further down advancing */
      /* the status to BS_DATAFETCH.                               */

      if (b->savefile) /* Proceed if there's a file to save to */
      {
        char              buffer[4096];
        int               success, bytes, done;
        _kernel_oserror * e;

        /* Get a chunk of data */
// Printf("savefile\n");
        e = html_get_next_chunk(NULL,
                                b->fetch_handle,
                                buffer,
                                sizeof(buffer),
                                &done,
                                &bytes);

        success = !e;
// Printf("success, bytes, done: %d, %d, %d\n",success,bytes,done);
        /* If there's an error, show it but continue */

        if (!success) show_error_ret(e);

        /* If there was not an error, write a chunk of file */

        if (success && bytes) success = fwrite(buffer, 1, bytes, b->savefile);

        /* If the expected number of bytes was not written, */
        /* show whatever error fwrite generated             */

        if (success != bytes && bytes)
        {
          success = 0;
          show_error_ret(_kernel_last_oserror());
        }

        /* If apparently successful and finished, read the pathname */
        /* of the file so the filetype can be set.                  */

        if (success && done)
        {
          _swix(OS_Args,
                _INR(0,3),

                7, /* Read pathname of open file */
                b->savefile->__file,
                buffer,
                sizeof(buffer));
        }

        /* If finished or there was some error above, stop the fetch */

        if (!success || done) fetch_stop(b, 1); /* This closes the output file, too */

        /* If successful and finished, set the filetype */

        if (success && done)
        {
          _swix(OS_File,
                _INR(0,2),

                18,
                buffer,
                b->savetype);
        }

        /* Finally, ensure toolbars are up to date. */

        toolbars_update_progress(b);
      }
    }

    #ifdef TRACE
      if (tl & (1u<<6)) Printf("fetch_fetcher: Get next token\n");
    #endif

    /* Get the next token, with fetch_chkerror allowing us to exit */
    /* relatively cleanly should an error occur.                   */

    if (
         fetch_chkerror(
                         b, html_get_next_token(
                                                 b,
                                                 b->fetch_handle,
                                                 &remain,
                                                 &sofar,
                                                 &tptr,
                                                 &waiting,
                                                 (flex_ptr) &b->source,
                                                 browser_fetch_url(b),
                                                 0
                                               )
                       )
       )
       return;

    /* Show the fetch's progress */

    toolbars_update_progress(b);

    /* If waiting = 3 the data being fetched isn't parseable. Alternatively, */
    /* if savelink is set, there is source present and the last token was    */
    /* fetched OK (so waiting = 0), then treat the data as non parseable -   */
    /* i.e., open a save dialogue for the data.                              */

    if (waiting == 3 || (b->savelink && b->source && !waiting))
    {
      // char pathname[256];

      #ifdef TRACE
        if (tl & (1u<<6)) Printf("fetch_fetcher: fetch_status moved to BS_DATAFETCH\n");
      #endif

//      b->fetch_status = BS_DATAFETCH;
//      b->savetype     = remain;
//
//      if (
//           waiting == 3 ||
//           (
//             b->savelink &&
//             b->source   &&
//             !waiting
//           )
//         )
//      {
//        char pathname[256];
//
//        b->status_fetch = BS_DATAFETCH;
//        b->savetype     = remn;
//
//        fetch_build_leaf(browser_fetching_url(b),
//                         pathname,
//                         sizeof(pathname));
//
//        if (
//             !saveas(remn,
//                     pathname,
//                     -1,
//                     fetch_save_url,
//                     NULL,
//                     NULL,
//                     (void *) b)
//
//             || !b->savefile
//           )
//           fetch_stop(b, TRUE);
//
//        return;
//      }

      // And do everything else; or for now, say sorry...

      if (choices.full_screen || 1)
      {
        fetch_stop(b, 1);
        StrNCpy0(erb.errmess, lookup_token("NotInline:Sorry, can't handle this data format...",0,0));
        erb.errnum = Utils_Error_Custom_Message;
        show_error_ret(&erb);
      }
      else
      {
        b->fetch_status = BS_DATAFETCH;
        b->savetype     = remain;
        b->savelink     = 1;

        b->savefile=fopen(":4.$.Test","wb");
      }

      return;
    }

    /* If waiting = 2, a redirect has occurred */

    else if (waiting == 2)
    {
      char * url;
      int    internal = 0;

      #ifdef TRACE
        if (tl & (1u<<6)) Printf("fetch_fetcher: Redirect to %s\n",(char *) remain);
      #endif

      /* Get the new URL pointed to by 'url' */

      url = (char *) remain;

      if (b->displayed != Display_Fetched_Page) internal = 1;

      /* Record the pre-redirection URL in the global history */

      if (!internal)
      {
        history_record_global(browser_fetch_url(b));

        /* Allocate space for new URL and copy it into that space */

        #ifdef TRACE
          if (tl & (1u<<12)) Printf("fetch_fetcher: Chunk CK_FURL set to %d\n",strlen(url) + 1);
        #endif

        if (fetch_chkerror(b, memory_set_chunk_size(b, NULL, CK_FURL, strlen(url) + 1))) return;
        strcpy(b->urlfdata, url);
      }
      else
      {
        char furl[2048];

        /* Allocate space for new URL plus old URL and separator, */
        /* and copy them into that space                          */

        #ifdef TRACE
          if (tl & (1u<<12)) Printf("fetch_fetcher: Chunk CK_FURL set to %d\n",strlen(url) + strlen(furl) + 2);
        #endif

        StrNCpy0(furl, browser_fetch_url(b));

        if (fetch_chkerror(b, memory_set_chunk_size(b, NULL, CK_FURL, strlen(url) + strlen(furl) + 2))) return;

        strcpy(b->urlfdata, url);
        strcat(b->urlfdata, ":");
        strcat(b->urlfdata, furl);
      }

      /* Reflect the new URL in the status and URL bars */

      toolbars_update_status(b, Toolbars_Status_Fetching);
      toolbars_update_url(b);
    }

    else if (!waiting)
    {
      /* If it isn't already non-zero, set 'start' to the number of */
      /* the last line in the line list                             */

      if (start < 0) start = b->cell->nlines - 1;

      /* We're not waiting, have we got a token? */

      if (b->fetch_status == BS_STARTED)
      {
        /* Yes - this is the first token on this page. Get the window */
        /* ready for the new page - this includes ditching old data.  */

        int l;

        /* Make the current display URL = current fetch URL... */

        l = strlen(browser_fetch_url(b)); /* Get the fetching URL string length */

        /* Allocate memory for it, and copy the string across */

        #ifdef TRACE
          if (tl & (1u<<12)) Printf("fetch_fetcher: Chunk CK_DURL set to %d\n",l + 1);
        #endif

        if (fetch_chkerror(b, memory_set_chunk_size(b, NULL, CK_DURL, l + 1))) return;

        strcpy(b->urlddata, browser_fetch_url(b));
        toolbars_hide_internal(b->urlddata);

        /* Update the title bar */

        if (!b->ancestor) /* Child windows don't have title bars... */
        {
          char title[MaxTiBLen - 1];

          StrNCpy0(title, browser_current_url(b));

          if (fetch_chkerror(b, window_set_title(0, b->self_id, title))) return;
        }

        /* Write to the global history */

        history_record_global(browser_fetch_url(b));

        /* Set status to FETCHING instead of STARTED */

        b->fetch_status = BS_FETCHING;

        /* If there was previous display data present, get rid of it */

        if ((b->display_handle) && (b->display_handle != b->fetch_handle))
        {
          html_close(b->display_handle);
          b->display_handle = 0;
        }

        /* Signal that the display data is coming from the fetch data, */
        /* so that the fetch data doesn't get accidentally ditched     */
        /* until it's finished with                                    */

        b->display_handle = b->fetch_handle;

        /* Initialise various things inside the browser_data structure */
        /* to do with colours and so-forth                             */

        #ifdef TRACE
          if (tl & (1u<<18)) Printf("New fetch for %p, stream %p\n",b,tptr);
        #endif

        b->stream        = tptr;              /* Pointer to list of HStream structures */
        b->finaltoken    = NULL;              /* Last HStream structure dealt with     */
        b->lastchar      = ' ';               /* Last character dealt with             */

        b->backgroundcol = -1;                /* Background colour, or -1 for default  */

        b->backimage     = -1;                /* Image no. of background image, 0=none */
        b->textcol       = choices.col_text;  /* Body text default colour              */
        b->linkcol       = choices.col_link;  /* Link text default colour              */
        b->usedcol       = choices.col_used;  /* Followed link default colour          */
        b->aacol         = redraw_backcol(b); /* Colour to anti-alias to, or -1=none   */
        b->follcol       = choices.col_foll;  /* Following link default colour         */
        b->selecol       = choices.col_sele;  /* Selected (highlighted) link colour    */

        b->onload        = NULL;              /* <BODY onload> attribute               */
        b->onunload      = NULL;              /* <BODY onunload> attribute             */

        /* Ensure the nesting level and filling frame counters are reset */

        b->nesting_level = 0;
        b->filling_frame = 0;

        /* Cancel any pending automatic fetches */

        if (b->meta_refresh_at) deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) meta_check_refresh, b);
        b->meta_refresh_at  = 0;
        b->meta_refresh_url = NULL;

        /* Cancel pending reformats */

        reformat_stop_pending(b);

        /* Hideously long comment alert...                                  */
        /*                                                                  */
        /* Although if a frame loads a document containing another frameset */
        /* this is in one sense a nested frame defintion, in another the    */
        /* second document is independent of the first; certainly as far as */
        /* incrementing the filling_frame field of the parent goes, the     */
        /* <frame> tag that loaded this document into the frame in the      */
        /* first place will already have done that.                         */
        /*                                                                  */
        /* Consequently, whilst all child frames have an ancestor - the     */
        /* original, base browser that defined the first of possibly many   */
        /* framesets - only genuinely nested frameset arrays have parents.  */
        /* That is, a parent can only have children; it may not also be a   */
        /* child (i.e. have a parent), it may only have an ancestor.        */
        /*                                                                  */
        /* Genuinely nested frames consist of one document with more than   */
        /* one set of <frameset> tags. Here, filling_frame considerations   */
        /* demand the use of a nested_level count and a parent as well as   */
        /* an ancestor. For those single documents, we won't be running     */
        /* this code when second or further framesets come in, so the       */
        /* parent field will get estabilshed and remain as long as needed   */
        /* by the frames routines.                                          */

        b->parent = NULL;

        /* Yup - that whole comment for one tiny line of code. Woo...       */
        /*                                                                  */
        /* 'Course, that said, it's useful for every child to know who its  */
        /* parent is. That's what the real_parent field is for.             */

//        {
//          WimpGetWindowStateBlock state;
//
//          state.window_handle = b->window_handle;
//
//          if (!wimp_get_window_state(&state))
//          {
//            b->display_width = b->display_extent = state.visible_area.xmax - state.visible_area.xmin;
//          }
//        }

        /* Don't want to set the pointer_over field, as then it may not seem to */
        /* have changed from one fetch to another; the pointer can get 'stuck'  */
        /* in the 'link' shape.                                                 */

        b->highlight      = NULL; /* No tokens are highlighted */
        b->selected       = NULL; /* No tokens are selected    */
        b->selected_owner = NULL;

        #ifdef TRACE
          if (tl & (1u<<6)) Printf("\nfetch_fetcher: Document colours etc. set to default values\n");
        #endif

        /* Clear the status bar contents block for an ancestor */
        /* window beginning a new fetch.                       */

        if (!b->ancestor && b->nstatus)
        {
          #ifdef TRACE

            if (
                 (tl & (1u<<1)) ||
                 (tl & (1u<<6))
               )
               Printf("fetch_fetcher: Freeing status_contents array\n");

          #endif

          b->nstatus = 0;
          memory_set_chunk_size(b, NULL, CK_STAT, 0);
        }

        /* Clear allocated memory for the forms, and tell */
        /* the font library that the fonts aren't needed  */
//        /* anymore. Images are cleared after the fetch,   */
//        /* so that any images common between the two can  */
//        /* be preserved.                                  */

        form_discard(b);
        fm_lose_fonts(b);

        /* Flag that images need to be garbage collected later */

// Um... ToDo list time...
        image_discard(b);
//        b->clear_images = 1;

        /* IMPORTANT, must call the reformatter here to ensure that all various   */
        /* line list data is invalidated, discarded, and any new stuff is valid.  */
        /* Otherwise, could have bits of the application subsequently using old   */
        /* line data and things will go very wrong very quickly.                  */
        /*                                                                        */
        /* DON'T put anything that might try and read line data before this call! */

        b->display_extent = b->display_width; /* Ensure a new fetch starts with the horizontal extent matching the visible area */
        reformat_format_from(b, -1, 1, -1);
        reformat_check_extent(b);

        /* Collapse any frames within this browser */

        frames_collapse_set(b);

        /* Ensure window tools are up to date */

        if (b->ancestor || b->full_screen) windows_set_tools(b, NULL, !b->ancestor, 0, 0, 0);

        /* If there's a # inside the URL (i.e. we're supposed to jump to an */
        /* anchor) then set the token to display first to be DISPLAY_NAMED, */
        /* a large number which acts as a flag to say 'jump to anchor'. The */
        /* fetch polling routine (see FetchPage.c) should notice this and   */
        /* start looking for a token with the appropriate name, and if it   */
        /* finds it, display that token.                                    */

        if (fetch_find_name_tag(browser_current_url(b))) b->display_request = DISPLAY_NAMED;

        /* Ensure the pointer shape is correct */

        browser_pointer_check(0, NULL, NULL, b);

        /* Reflect the new browser status */

        toolbars_update_status(b, Toolbars_Status_Fetching);

        /* Since the new fetch is now official, update the current and previous */
        /* page variables                                                       */

        if (!b->ancestor)
        {
          /* Not speed critical, so avoid lots of nasty C-isms with malloc */
          /* and so-on, by running through OS_CLI.                         */

          _swix(OS_CLI,
                _IN(0),

                "Set Browse$PreviousPage <Browse$CurrentPage>");

          _swix(OS_SetVarVal,
                _INR(0,4),

                "Browse$CurrentPage",
                b->urlfdata,
                strlen(b->urlfdata),
                0,
                4);
        }

        /* (Initialisation to an empty state is now complete, so we're */
        /* ready to fetch a new page).                                 */
      }

      /* We're not waiting, but if there's also no data left to fetch, */
      /* then we're just chugging through the list of tokens that the  */
      /* library is generating, telling various bits of the code about */
      /* their contents (e.g. a new image, a new form). In this case,  */
      /* change the fetch status so the status bar can reflect the new */
      /* situation.                                                    */

      if (!remain)
      {
        b->fetch_status = BS_PROCESS;
        toolbars_update_status(b, Toolbars_Status_Processing);
      }

      /* If tptr is null, there are no HStream structures (see the */
      /* html_get_next_token call). But we're not waiting either,  */
      /* so must be at the end of the file - stop the fetch.       */

      if (!tptr)
      {
        if (b->last_token->tag == TABLE && ISBODY(b->last_token))
        {
          /* If the last thing the reformatter dealt with was a table, */
          /* then extra table structures could have been added by      */
          /* HTMLLib and the reformatter may only have partially       */
          /* finished laying the table out. So kick off a reformat     */
          /* for that table to ensure it is up to date.                */
          /*                                                           */
          /* Of course, it is important to ensure that any tokens that */
          /* were added to the token stream are preprocessed before    */
          /* starting a reformat.                                      */

          fetch_preprocess_token(b, b->last_token);
          reformat_format_from(b, b->cell->nlines - 2, 1, -1);
        }

        #ifdef TRACE
          if (tl & (1u<<6)) Printf("\nfetch_fetcher: Finished, so stopping and exiting.\n");
        #endif

        fetch_stop(b, 1);
        return;
      }
      else fetch_preprocess_token(b, tptr);

    /* Closure of series of ifs that checked the state of 'waiting' */
    /* amongst other things, to handle redirections etc. The bulk   */
    /* of the code deals with a conventional fetch.                 */
    }

  /* Closure of for loop that deals with several fetches per null */
  }

  /* If start is >= 0, there is data that can be used for     */
  /* displaying the page; so start a reformat based on that   */
  /* data. Start from 'one line up' as the last line may have */
  /* been only partially finished when it was last redrawn.   */

  if (start >= 0) reformat_format_from(b, start - 1, 0, -1);
}

/*************************************************/
/* fetch_chkerror()                              */
/*                                               */
/* Called by low level fetch routines instead of */
/* the ChkError macro, as it stops the current   */
/* fetch correctly before reporting the error.   */
/*                                               */
/* Parameters: Pointer to a browser_data         */
/*             structure relevant to the fetch;  */
/*             Pointer to a _kernel_oserror      */
/*             structure, which contains the     */
/*             error to report (or NULL).        */
/*                                               */
/* Returns:    0 if there was no error, else 1.  */
/*************************************************/

int fetch_chkerror(browser_data * b, _kernel_oserror * e)
{
  if (e)
  {
    /* There is an error - cancel the fetch */

    fetch_cancel(b);

    /* Report the error */

    show_error_ret(e);

    /* Flag the error in the returned value */

    return 1;
  }

  return 0;
}

/*************************************************/
/* fetch_cancel()                                */
/*                                               */
/* Aborts a fetch, closing any relevant streams, */
/* freeing up any claimed memory that was only   */
/* relevant to the fetch, but leaves the page    */
/* fetched so far visible.                       */
/*                                               */
/* Parameters: A pointer to the browser_data     */
/*             structure relevant to the fetch   */
/*             to be cancelled.                  */
/*************************************************/

_kernel_oserror * fetch_cancel(browser_data * b)
{
  /* If there is a fetch, and the HTML data isn't being used by the  */
  /* display routines, close the fetch handle and free up any memory */
  /* associated with it.                                             */

  if ((b->fetch_handle) && ((b->fetch_handle) != (b->display_handle))) html_close(b->fetch_handle);
  b->fetch_handle = 0;

  /* If a META tag is about to do a reload, cancel this */

  if (b->meta_refresh_at) deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) meta_check_refresh, b);
  b->meta_refresh_at = 0;

  /* If not fetching, exit here */

  if (!fetch_fetching(b)) return NULL;

  /* Stop everything else */

  fetch_stop(b, 1);

  /* Ensure the page is correctly formatted */

  if (b->cell->nlines) reformat_format_from(b, b->cell->nlines - 1, 1, -1);

  return(NULL);
}

/*************************************************/
/* fetch_stop()                                  */
/*                                               */
/* Stops a fetch, optionally discarding the      */
/* HTML source, making sure the browser window   */
/* state (buttons, status bar animation etc.) is */
/* correct, any open files are closed, and so    */
/* forth. In the UI sense this is higher level   */
/* than fetch_cancel, though fetch_cancel calls  */
/* this as part of doing other cancel actions,   */
/* and is therefore the higher level function.   */
/*                                               */
/* Parameters: A pointer to the browser_data     */
/*             structure relevant to the fetch   */
/*             to be stopped;                    */
/*             1 to keep the HTML source, 0 to   */
/*             destroy it.                       */
/*************************************************/

void fetch_stop(browser_data * b, int keep_source)
{
  /* Destroy the source, provided the browser was fetching any */

  if (fetch_fetching(b) && !keep_source) browser_destroy_source(b);

  /* Set the fetch status to idle */

  b->fetch_status = BS_IDLE;

  /* If data was being saved to a file, close that file */

  if (b->savefile)
  {
    fclose(b->savefile);
    b->savefile = NULL;
  }

  /* The savelink flag tells the browser to save the next fetch as data, */
  /* even if it is parsable. Want to make sure that flag is clear now to */
  /* avoid complications later on.                                       */

  b->savelink = 0;

  /* If there is a fetch, and the associated HTML document isn't being */
  /* used by the display routines, close that fetch handle and free    */
  /* any memory associated with it.                                    */

  if ((b->fetch_handle) && (b->fetch_handle != b->display_handle)) html_close(b->fetch_handle);
  b->fetch_handle = 0;

  /* Discard the URL being fetched */

  #ifdef TRACE
    if (tl & (1u<<12)) Printf("fetch_stop: Chunk CK_FURL set to 0\n");
  #endif

  memory_set_chunk_size(b, NULL, CK_FURL, 0);
  b->reloading = 0;

  toolbars_cancel_status(b, Toolbars_Status_Fetching);

  /* Check that the window extent is large enough to fit the whole page in */

  reformat_check_extent(b);

  /* Set up the window buttons */

  toolbars_set_button_states(b);
}

/*************************************************/
/* fetch_authorisation_proceed()                 */
/*                                               */
/* Given a browser_data structure with a URL     */
/* containing a host and a pointer to a realm    */
/* string, proceed with an authorisation         */
/* request based on the data in the global       */
/* 'authorise' flex block (handled by the        */
/* functions in Authorise.c).                    */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the authorisation     */
/*             request;                          */
/*                                               */
/*             Pointer to a urlstat structure    */
/*             giving the fetch context, or NULL */
/*             to get it from the browser_data   */
/*             structure's fetch_handle field    */
/*             (no good for images, obviously);  */
/*                                               */
/*             Pointer to a string containing    */
/*             the realm for the request;        */
/*                                               */
/*             Pointer to the request URL.       */
/*************************************************/

void fetch_authorisation_proceed(browser_data * b, urlstat * context, char * realm, char * url)
{
  int               ok, l, s, offset;
  char              host    [128];
  char              base64  [(MaxAuthUser + MaxAuthPass + 2) * 4 / 3];
  char              authcode[(MaxAuthUser + MaxAuthPass + 2)];
  urlstat         * up;
  _kernel_oserror * e;

  /* Clear the 'authorising' flag */

  authorising = 0;

  if (!context)
  {
    /* set 'up' to point to the head of the urlstat structure linked list */

    up = fetch_list;

    /* It's another linked list traversal... As long as we aren't at the */
    /* end of the list, and we haven't reached the item relating to this */
    /* fetch, keep looking.                                              */

    while (up && up->session != b->fetch_handle) up = up->next;

    /* After the above loop, 'up' points to the structure for this */
    /* fetch or is null; in the latter case, give an error.        */

    if (!up)
    {
      fetch_cancel(b);

      erb.errnum = Utils_Error_Custom_Normal; /* Nasty error but can recover from it here */

      StrNCpy0(erb.errmess,
               lookup_token("StrNotFd:Internal error: Can't find structure in %0.",
                            0,
                            "fetch_authorisation_proceed()"));

      show_error_ret(&erb);

      return;
    }
  }
  else up = context;

  /* Mark this fetch as authorised once already - if the server */
  /* resends an authorisation request the fetcher will know     */
  /* that the authorisation failed (see html_get_next_token).   */

  up->authorised = 2;

  if (up->extradata) s = flex_size((flex_ptr) &up->extradata);
  else               s = 0;

  /* Work out the host name */

  urlutils_host_name_from_url(url, host, sizeof(host));

  /* Store the details in the authcode block */

  offset = authorise_find_user_name(host, realm);
  if (offset < 0)
  {
    fetch_authorisation_fail(b);
    return;
  }

  strcpy(authcode, authorise + offset);

  strcat(authcode, ":");

  offset = authorise_find_password(host, realm);
  if (offset < 0)
  {
    fetch_authorisation_fail(b);
    return;
  }

  strcat(authcode, authorise + offset);

  /* Encode the block */

  l = encode_base64(authcode, strlen(authcode), base64);
  base64[l] = 0;

  /* Allocate memory for the encoded data as a */
  /* header entry.                             */
  /*                                           */
  /* +2 accounts for CR + LF termination.      */

  l += strlen(AuthorisationStr) + 2;

  if (s) ok = flex_extend((flex_ptr) &up->extradata, s + l);
  else   ok = flex_alloc((flex_ptr) &up->extradata, l + 1);

  if (!ok)
  {
    fetch_cancel(b);

    make_no_fetch_memory_error(12);
    show_error_ret(&erb);

    return;
  }

  if (s) memmove(up->extradata + l, up->extradata, s);

  /* Copy the data in */

  strcpy(up->extradata, AuthorisationStr);
  strncpy(up->extradata + strlen(AuthorisationStr), base64, l - 23);
  up->extradata[l - 2] = '\r';
  up->extradata[l - 1] = '\n';

  if (!s) up->extradata[l] = 0;

  /* Restart the fetch with authentication */

  e = url_get_url(0,
                  up->session,
                  up->method,
                  url,
                  up->extradata,
                  NULL,
                  2);

  if (e)
  {
    fetch_cancel(b);
    show_error_ret(e);
  }

  return;
}

/*************************************************/
/* fetch_authorisation_fail()                    */
/*                                               */
/* Called when authorisation for a URL fails in  */
/* some way. Reports an appropriate error and    */
/* stops the fetch.                              */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the fetch.            */
/*************************************************/

void fetch_authorisation_fail(browser_data * b)
{
  /* Cancel the fetch */

  authorising = 0;
  fetch_cancel(b);

  /* Give the error */

  erb.errnum = Utils_Error_Custom_Message;

  StrNCpy0(erb.errmess,
           lookup_token("BadAuthor:Authorisation failed; you must use a valid user name and password.",
                        0,
                        0));

  show_error_ret(&erb);
}

/*************************************************/
/* html_get()                                    */
/*                                               */
/* Fetches and optionally starts parsing HTML.   */
/*                                               */
/* Parameters: Pointer to URL to fetch;          */
/*             Pointer to extra data for POST    */
/*             etc.;                             */
/*             Pointer to an int into which a    */
/*             handle for this fetch will be     */
/*             placed;                           */
/*             The fetch method, e.g. POST or    */
/*             GET;                              */
/*             Pointer to the user name for      */
/*             MailServ (if in a multiuser       */
/*             environment);                     */
/*             1 to allow parsing, else 0;       */
/*             1 to allow proxying, else 0.      */
/*                                               */
/* Returns:    See parameters list.              */
/*************************************************/

_kernel_oserror * html_get(char * url, char * extradata, int * handle, int method,
                           char * user, int allowparse, int proxy)
{
  _kernel_oserror * e;
  int               ok;
  unsigned int      h;

  #ifdef TRACE
    if (tl & (1u<<6)) Printf("html_get: Called\n");
  #endif

  *handle = 0;

  /* Register the session with the URL module */

  e = url_register(0, &h);

// Sort out the proxying code properly!...

  /* Deal with proxying if necessary */

  if (!e && choices.use_proxy)
  {
    char   method[64];
    char * method_ptr;
    int    method_len;

    /* Extract the fetch method from the proxy address */

    method_ptr = strstr(lookup_choice("ProxyAddress:http://127.0.0.1/",
                                      0,
                                      0),
                        ":");

    if (method_ptr)
    {
      method_len = (int) method_ptr - (int) tokens + 1;
      if (method_len > sizeof(method) - 1) method_len = sizeof(method) - 1;
      strncpy(method, tokens, method_len);
      method[method_len] = 0;
    }
    else strncpy(method, "http:", sizeof(method));

    e = url_set_proxy(0, h, tokens, method, 0);
  }

  if (!e)
  {
    urlstat * up = NULL;

    #ifdef TRACE
      if (tl & (1u<<6)) Printf("html_get: Session registered, ID is %d\n",h);
    #endif

    /* Allocate memory for the fetch */

    #ifdef TRACE
      if (tl & (1u<<12)) Printf("html_get: malloc %d for 'urlstat' structure\n",sizeof(urlstat));
    #endif

    up = malloc(sizeof(urlstat));

    /* If the allocation failed, report the problem  */

    if (!up)
    {
      url_deregister(0,h);
      make_no_fetch_memory_error(1);
      return &erb;
    }

    #ifdef TRACE
      malloccount += sizeof(urlstat);
      if (tl & (1u<<13)) Printf("** malloccount: %d\n",malloccount);
    #endif

    /* The allocation succeeded; fill the claimed memory with zeros */
    /* and initialise various other parts of the block              */

    memset(up, 0, sizeof(urlstat));

    up->session    = * handle = (int) h; /* The fetch's session handle                      */
    up->type       = TYPE_HTMLFILE;      /* Type of file - state it is an HTML file for now */
    up->fetching   = 1;                  /* We are still fetching                           */
    up->next       = fetch_list;         /* Point to the top of the list in the Next entry  */
    up->method     = method;             /* Current fetch method                            */
    up->extradata  = NULL;               /* Filled in later, if there is extra data         */
    up->allowparse = allowparse;         /* Do we parse the data?                           */

    fetch_list = up;

    /* If there is any extra data for POST or whatever, deal with it. */
    /* The POST request entries must come first in the extra header   */
    /* info, so that the browser can make the assumption that         */
    /* everything from Content-Type forwards may be stripped in the   */
    /* event of a redirection when the current fetch method is POST.  */

    if (extradata)
    {
      int len;

      len = strlen(extradata);

      /* Allocate space for the extra data, the anchor stored in up->extradata */

      #ifdef TRACE
        if (tl & (1u<<12)) Printf("html_get: flex_alloc %d for 'extradata' store\n",len + 3);
      #endif

      if (!flex_alloc((flex_ptr) &up->extradata, len + 3))
      {
        url_deregister(0,h);
        make_no_fetch_memory_error(2);
        return &erb;
      }
      else
      {
        char head[50];

        #ifdef TRACE
          flexcount += (len + 3);
          if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
        #endif

        /* CR+LF into the top of the new block of memory */

        up->extradata[0] = '\r';
        up->extradata[1] = '\n';

        /* Copy the extra data under the CR+LF */

        strcpy(up->extradata + 2, extradata);

        /* Header entry for the extra data - again, the removal routines in the  */
        /* fetcher's redirection code assume that this comes before Content-Type */
        /* and the actual body content to make life easy there.                  */

        sprintf(head, "Content-Length: %d", len);

        /* Insert the header entries above the extra data already in the block. */

        ok = html_insert_header(head, (flex_ptr) &up->extradata);

        /* (html_insert_header() returns 1 for success, 0 for memory claim failure) */

        if (!ok)
        {
          url_deregister(0, h);
          make_no_fetch_memory_error(3);
          return &erb;
        }

        StrNCpy0(head, "Content-Type: application/x-www-form-urlencoded");
        ok = html_insert_header(head, (flex_ptr) &up->extradata);

        if (!ok)
        {
          url_deregister(0, h);
          make_no_fetch_memory_error(4);
          return &erb;
        }
      }
    }

    /* If user details are given, insert the appopriate header entry */

    if (user)
    {
      char head[50];

      sprintf(head, "Mailserv-User: %s", user);
      ok = html_insert_header(head, (flex_ptr) &up->extradata);

      if (!ok)
      {
        url_deregister(0, h);
        make_no_fetch_memory_error(5);
        return &erb;
      }
    }

    /* If we aren't to use a proxy, say so in the header */

    if (!proxy)
    {
      ok = html_insert_header("X-NoProxy:", (flex_ptr) &up->extradata);

      if (!ok)
      {
        url_deregister(0, h);
        make_no_fetch_memory_error(6);
        return &erb;
      }
    }

    /* Last but not least, do, er, something... */

    {
      char   c = 0;
      char * p = NULL;

      /* If non-zero on exit, p will point to the position of a hash */
      /* in the URL (i.e., this finds out if an anchor is specified) */

      p = fetch_find_name_tag(url);

      /* If there is a hash, turn it into a zero for now so the string */
      /* contains just the URL and not the anchor.                     */

      if (p) c = *p, *p = 0;

      e = url_get_url(0,             /* Flags - must be 0, currently */
                      h,             /* Session handle               */
                      method,        /* Fetch method                 */
                      url,           /* URL to get                   */
                      up->extradata, /* Any extra data for POST etc. */
                      NULL,          /* (Would be a status word)     */
                      2);            /* Mode; 2 = header and data    */

      /* Put the hash back if was removed earlier. */

      if (p) *p = c;
    }
  }

  #ifdef TRACE
    if (tl & (1u<<6))
    {
      if (!e) Printf("html_get: Successful\n");
      else Printf("html_get: Exitting with an error\n");
    }
  #endif

  return e;
}

/*************************************************/
/* html_insert_header()                          */
/*                                               */
/* Inserts a string into the header for an HTML  */
/* fetch (for POST). Puts it at the top.         */
/*                                               */
/* Parameters: Pointer to the null terminated    */
/*             string to insert (this ends up    */
/*             CR+LF terminated in the header);  */
/*             Pointer to a flex anchor, which   */
/*             points to existing header data or */
/*             is NULL if there is no header at  */
/*             the time of the function call.    */
/*                                               */
/* Returns:    1 if successful, or 0; you must   */
/*             externally generate an error      */
/*             appropriate to the memory claim   */
/*             having failed.                    */
/*************************************************/

int html_insert_header(char * header, flex_ptr data)
{
  if (header)
  {
    int ok, s, len;

    len = strlen(header) + 2;

    /* 'data' points to an anchor; if this isn't null, find the */
    /* size of the block the anchor points to                   */

    if (*data) s = flex_size(data);
    else s = 0;

    /* If the block is > 0 bytes, extend it to a block big      */
    /* enough to hold the extra header data, else allocate a    */
    /* new block to hold it. Note that s will be zero if a new  */
    /* block was allocated, else it holds the old block size.   */

    #ifdef TRACE
      if (tl & (1u<<12))
      {
        if (s) Printf("html_insert_header: flex_extend to %d for header store\n",len + s);
        else   Printf("html_insert_header: flex_alloc %d for header store\n",len + 1);
      }
    #endif

    if (s) ok = flex_extend(data, len + s);
    else   ok = flex_alloc(data, len + 1); /* Note len *plus 1*. */

    if (!ok) return 0;

    #ifdef TRACE
      if (s) flexcount += len;
      else   flexcount += (len + 1);
      if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
    #endif

    /* Shuffle the header data down to make room for the new    */
    /* stuff at the top, if there was any data there to move.   */

    #ifdef TRACE
      if (tl & (1u<<18)) Printf("\0213html_insert_header: memove from %p to %p for %d bytes\0217\n",((int) (*data)) + len, *data, s);
    #endif

    if (s) memmove((void *) (((int) (*data)) + len), *data, s);

    /* Copy the new data into the top of the header. Don't want */
    /* to overflow so use strncpy for extra caution...          */

    strncpy(*data, header, len - 2);

    /* Terminate the string with CR+LF                          */

    ((char *) (*data))[len - 2] = '\r';
    ((char *) (*data))[len - 1] = '\n';

    /* If s is zero, i.e. a new block was created here, make    */
    /* sure it ends in zero (so C will think the string has     */
    /* ended properly if a string is read from the buffer). We  */
    /* can reference (array)[len] as the block allocation was   */
    /* done to len plus 1 bytes (see above).                    */

    if (!s) ((char *) (*data))[len] = 0;
  }

  return 1;
}

/*************************************************/
/* html_close()                                  */
/*                                               */
/* Closes the specified handle, aborting any     */
/* fetch and freeing up memory relating to it.   */
/*                                               */
/* Parameters: A fetch handle (usually from the  */
/*             browser_data->fetch_handle        */
/*             field).                           */
/*************************************************/

_kernel_oserror * html_close(unsigned int handle)
{
  urlstat  * up;
  urlstat ** pup;

  #ifdef TRACE
    if (tl & (1u<<6)) Printf("html_close: Called\n");
  #endif

  url_deregister(0, handle);

  /* set 'pup' to point to the pointer to the head of the urlstat structure linked list */

  pup = &fetch_list;

  /* set 'up' to point to the head of the urlstat structure linked list */

  up = *pup;

  /* It's another linked list traversal... As long as we aren't at the */
  /* end of the list, and we haven't reached the item relating to this */
  /* fetch, keep looking.                                              */

  while ((up) && (up->session != handle))
  {
    pup = &(up->next);
    up = up->next;
  }

  /* After the above loop, 'up' points to the structure for this */
  /* fetch or is null; in the latter case, give an error.        */

  if (!up)
  {
    erb.errnum = Utils_Error_Custom_Fatal;

    StrNCpy0(erb.errmess,
             lookup_token("StrNotFd:Internal error: Can't find structure in %0.",
                          0,
                          "html_close()"));

    #ifdef TRACE
      if (tl & (1u<<6)) Printf("html_close: Exiting with error\n");
    #endif

    return &erb;
  }

  /* Make the pointer pointing to this structure point to the next one */

  *pup = up->next;

  /* Free blocks associated with the urlstat structure */

  #ifdef TRACE
    if (up->stream)
    {
      if (tl & (1u<<12))   Printf("html_close: Calling HtmlStreamFree on %p\n",up->stream);
      if (tl & (1u<<18)) Printf("\0212Closing stream %p\0217\n",up->stream);
    }
  #endif

  if (up->stream)
  {
    unsigned int   context = HtmlReturnContext(up->stream);
    browser_data * browser = last_browser;

    /* Should Never Happen...! */

    if (!context)
    {
      erb.errnum = Utils_Error_Custom_Fatal;

      StrNCpy0(erb.errmess,
               lookup_token("NoContxt:Serious internal error - Block is already free or was not HtmlAlloc'd in html_close(); must exit immediately.",
                            0,
                            0));

      return &erb;
    }

    /* Ensure that any HStream pointers inside any current browser_data */
    /* structures are not part of this stream - if so, clear them.      */

    while (browser)
    {
      /* For now, just a few selected items */

      if (browser->selected && HtmlReturnContext(browser->selected) == context)
      {
        browser_clear_selection(browser, 0);
        browser->selected = NULL;
      }

      if (browser->highlight && HtmlReturnContext(browser->highlight) == context)
      {
        browser_clear_highlight(browser, 0);
        browser->highlight = NULL;
      }

      if (browser->pointer_over && HtmlReturnContext(browser->pointer_over) == context) browser->pointer_over = NULL;

      browser = browser->previous;
    }

    HtmlStreamFree(up->stream);
  }

  if (up->context)
  {
    #ifdef TRACE
      if (tl & (1u<<12)) Printf("html_close: free block %p for 'context' field of 'urlstat' structure\n",up->context);
    #endif

    free(up->context);
    up->context = NULL;
  }

  #ifdef TRACE
    if (up->extradata)
    {
      if (tl & (1u<<12)) Printf("html_close: flex_free block %p for 'extradata' field of 'urlstat' structure\n",&up->extradata);
      flexcount -= flex_size((flex_ptr) &up->extradata);
      if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
    }
  #endif

  if (up->extradata) flex_free((flex_ptr) &up->extradata);

  /* Finally, get rid of the structure itself */

  #ifdef TRACE
    if (tl & (1u<<12)) Printf("html_close: free block %p holding 'urlstat' structure\n",up);
    malloccount -= sizeof(urlstat);
    if (tl & (1u<<13)) Printf("** malloccount: %d\n",malloccount);
  #endif

  free(up);

  #ifdef TRACE
    if (tl & (1u<<6)) Printf("html_close: Successful\n");
  #endif

  return NULL;
}

/*************************************************/
/* html_get_next_token()                         */
/*                                               */
/* Gets a chunk of document source from a given  */
/* fetch handle, and may generate new HStream    */
/* structures as the document is passed over to  */
/* the HTML library parser.                      */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the fetch or NULL;    */
/*                                               */
/*             The fetch handle;                 */
/*                                               */
/*             Pointer to int into which the     */
/*             number of bytes still to be       */
/*             fetched is played;                */
/*                                               */
/*             Pointer to int into which the     */
/*             number of bytes fetched so far is */
/*             placed;                           */
/*                                               */
/*             Pointer to an HStream *, into     */
/*             which the address of the base of  */
/*             the token list is written, or     */
/*             NULL to signal 'not ready';       */
/*                                               */
/*             Pointer to an int, into which a   */
/*             reason code is placed:            */
/*                                               */
/*             0: Token has been received OK,    */
/*             1: We are waiting for something,  */
/*             2: A redirect has been detected   */
/*                (in this case, *remaining will */
/*                point at the new URL),         */
/*             3: This data is not parseable (in */
/*                this case, *remaining holds a  */
/*                filetype);                     */
/*                                               */
/*             Pointer to pointer to the store   */
/*             for the whole of the data fetched */
/*             so far (if any), be it an HTML    */
/*             document, image, or whatever;     */
/*                                               */
/*             Pointer to string holding the URL */
/*             that is being fetched;            */
/*                                               */
/*             1 if this is an image fetch, else */
/*             0 for HTML or unknown.            */
/*                                               */
/* Returns:    See parameters list.              */
/*                                               */
/* Assumes:    That if the browser_data struct   */
/*             pointer is NULL, the fetch is not */
/*             for an internal URL. The other    */
/*             pointers must NOT be NULL unless  */
/*             it is specifically stated that    */
/*             they may be in the parameters     */
/*             list.                             */
/*************************************************/

_kernel_oserror * html_get_next_token(browser_data * b, unsigned int handle, int * remaining, int * size,
                                      HStream ** token, int * waiting, flex_ptr source, char * url, int image)
{
  _kernel_oserror * e = NULL;
  int               r = 0;
  char              ref_url[2048];
  urlstat         * up;

  #ifdef TRACE
    if (tl & (1u<<6)) Printf("html_get_next_token: Called\n");
  #endif

  /* Start in the default state of having no HStream to pass back */
  /* through *token.                                              */

  if (token) *token = NULL;

  /* Until we know better, signal that we're waiting */

  *waiting = 1;

  /* Ensure a fetch buffer is allocated */

  if (!fetch_buffer)
  {
    fetch_buffer = malloc(FetchBufferSize); /* See top of this file */

    if (!fetch_buffer)
    {
      make_no_cont_memory_error(8);

      #ifdef TRACE
        if (tl & (1u<<6)) Printf("html_get_next_token: Exiting with error\n");
      #endif

      return &erb;
    }
  }

  /* up points to the first of the urlstat structure linked list; */
  /* get a pointer to the one that the fetch handle refers to.    */

  up = fetch_list;

  while((up) && (up->session != handle)) up = up->next;

  if (!up)
  {
    erb.errnum = Utils_Error_Custom_Fatal;

    StrNCpy0(erb.errmess,
             lookup_token("StrNotFd:Internal error: Can't find structure in %0.",
                          0,
                          "html_get_next_token()"));

    #ifdef TRACE
      if (tl & (1u<<6)) Printf("html_get_next_token: Exiting with error\n");
    #endif

    return &erb;
  }

  /* Only look for an anchor and use url_read_data for URLs which */
  /* are not internal.                                              */

  StrNCpy0(ref_url, url);

  if (image || b->displayed == Display_Fetched_Page)
  {
    int status = 0;

    /* Want to make sure we work on a URL which doesn't */
    /* have an anchor in it, so copy over the url to    */
    /* a local buffer and if there's a '#' marking an   */
    /* anchor, replace it with a string terminator.     */

    char * p = fetch_find_name_tag(ref_url);

    if (p) * p = 0;

    /* If there isn't an authorisation request in progress, and the  */
    /* fetch is apparently in progress, and the authorisation status */
    /* isn't '1' (which means 'doing'), get some data from the URL   */
    /* module. The url_read_data call puts the number of bytes read  */
    /* into r.                                                       */

    if (!authorising && up->fetching && (up->authorised != 1))
    {
      e = url_read_data(0,               /* Flags - must be 0 at present */
                        handle,          /* Session handle               */
                        fetch_buffer,    /* Buffer to receive data       */
                        FetchBufferSize, /* The buffer's size            */

                        &status,         /* Protocol status              */
                        &r,              /* Number of bytes read         */
                        remaining);      /* Number of bytes left to get  */

      /* Deal with cookies */

      if (!e && (status & (1u<<16))) e = cookies_process_cookie(b);
    }
  }
  else
  {
    /* This is an internal URL, so treat specially */

    int    ok;
    char * extra = "";
    char * tail  = "";
    int    len, exoff, toff;

    if (*source) flex_free(source);

    /* Look up the token embedded in the URL */

    lookup_token(url + Int_URL_Len, 1, 0);

    /* Find a ':' separating extra information and point just past it */

    exoff = urlutils_internal_extra(url);
    if (exoff) extra = url + exoff;

    /* Work out the length that the HTML file we're about to generate will be; */
    /* this will be at least as long as the looked up token, plus a display    */
    /* type dependent extra amount.                                            */

    len = strlen(tokens) + 1;

    switch (b->displayed)
    {
      case Display_External_Image:
      {
        FILE * null;

        /* For external images, use of sprintf() precludes the use of */
        /* general calculations to work out the string length. So,    */
        /* need to fprintf to NULL, find out how many bytes were      */
        /* written, and use this value instead...                     */


        if (*extra)
        {
          toff = urlutils_internal_tail(url);
          if (toff) tail = url + toff;
        }

        null = fopen("Null:", "wb");

        if (null)
        {
          len = fprintf(null, tokens, extra, extra, tail);
          fclose(null);
        }
        else len = -1;

        if (len < 0)
        {
          /* If the above fails, do our best to calculate the length. */
          /* This will always overestimate the size (safer to do this */
          /* than underestimate!).                                    */

          len = strlen(tokens) + 1;

          /* For external images, need to fit the extra data in twice, and */
          /* try to find a filename separator for a picture caption (put   */
          /* this in 'tail').                                              */

          if (*extra) len += strlen(extra) * 2 + strlen(tail) + 2;
        }
      }
      break;
    }

    /* Claim memory for the page; complain if this fails */

    ok = flex_alloc(source, len);

    if (!ok)
    {
      make_no_cont_memory_error(1);

      #ifdef TRACE
        if (tl & (1u<<6)) Printf("html_get_next_token: Exiting with error\n");
      #endif

      return &erb;
    }

    /* Construct the page in the claimed block */

    switch (b->displayed)
    {
      case Display_External_Image:
      {
        memset(*source, 0, len);
        sprintf(*source, tokens, extra, extra, tail);
      }
      break;
    }

    /* Set up fetch flags to say that a fetch has been completed; since */
    /* we've filled in the document source store here, say that zero    */
    /* bytes have been fetched (otherwise code below will try to copy   */
    /* data out of the fetch_buffer block).                             */

    r              = 0;
    remaining      = 0;
    up->identified = 1;
    up->allowparse = 1;
    up->fetched    = 1;
    up->fetching   = 0;
  }

  /* If there isn't an error, and more than zero bytes have been read, */
  /* deal with the data (if any) returned from the above call.         */

  if (r && !e)
  {
    int ok, oldsize;

    /* 'fetched' is a flag which if set indicates at least 1 byte has been */
    /* got so far. If fetched is zero, and there is data in the source     */
    /* store (i.e. 'source' is not NULL) then free up the store as it does */
    /* not hold any valid data (must be from an old fetch).                */

    if (!up->fetched && *source)
    {
      #ifdef TRACE
        if (tl & (1u<<12)) Printf("html_get_next_token: (1) flex_free block %p which held page source\n",source);
        flexcount -= flex_size(source);
        if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
      #endif

      flex_free(source);
      *source = NULL;
    }

    /* Signal that there's definitely data fetched now. */

    up->fetched = 1;

    /* If there's store allocated at this point, it holds valid source; extend */
    /* it by the number of bytes read from the url_read_data call. Else, alloc */
    /* a new buffer to hold the data.                                          */

    #ifdef TRACE
      if (tl & (1u<<12))
      {
        if (*source) Printf("html_get_next_token: flex_extend by %d to %d for page source store\n",r,flex_size(source) + r);
        else         Printf("html_get_next_token: flex_alloc %d for page source store\n",r);
      }
    #endif

    if (*source)
    {
      oldsize = flex_size(source);
      ok = flex_extend(source, oldsize + r);
    }
    else
    {
      oldsize = 0;
      ok = flex_alloc(source, r);
    }

    #ifdef TRACE
      flexcount += r;
      if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
    #endif

    if (size) *size = oldsize + r;

    /* Report an error if the allocation failed */

    if (!ok)
    {
      make_no_cont_memory_error(1);

      #ifdef TRACE
        if (tl & (1u<<6)) Printf("html_get_next_token: Exiting with error\n");
      #endif

      return &erb;
    }

    /* The data block has been created/extended successfully, so copy the */
    /* data from the url_read_data call into it.                          */

    #ifdef TRACE
      if (tl & (1u<<18)) Printf("\0216html_get_next_token: memcpy from %p to %p for %d bytes\0217\n",((char *) (*source)) + oldsize, fetch_buffer, r);
    #endif

    memcpy(((char *) (*source)) + oldsize, fetch_buffer, r);
  }

  /* If we're not authorising the transfer and data has been fetched... */

  if (!authorising && up->fetched)
  {
    unsigned int   hf  = 0;
    HStream      * new = NULL;

    /* If the stream has been identified as HTML... */

    if (up->identified)
    {
      /* If there's no parsing context, get one by calling HtmlParse() */
      /* - this initialises the HTML parser, getting it ready to parse */
      /* a document (though it need not be present at this stage).     */
      /*                                                               */
      /* First time round, this won't be called as the stream hasn't   */
      /* been identified with HtmlIdentify() yet.                      */

      if (up->context == NULL)
      {
        up->context = HtmlParse(ref_url,    /* Full page URL, so parser can handle relative links */
                                0,          /* Length of document - zero at present (not known)   */
                                up->type,   /* Return type from the HtmlIdentify call             */

                                #ifdef FRAMES_SUPPORT
                                  1);
                                #else
                                  0);
                                #endif

        r = *source ? flex_size(source) : 0;
      }

      /* If there is new data in the source store (size = r) and no error at */
      /* present, attempt to parse the chunk of data with HtmlGetStream.     */

      if (r && !e)
      {
        new = HtmlGetStream(up->context,        /* Parser context, from HtmlParse()          */
                            (char **) source,   /* Pointer to start of the complete document */
                            r,                  /* Size of the chunk that has been added     */
                            &hf);               /* Flags from HTMLLib, e.g. 'have more data' */

        up->stream = new;

        #ifdef TRACE
          if (tl & (1u<<18)) Printf("\0211(New stream for %p, %p)\0217\n", b, up->stream);
        #endif
      }

      if (!new)
      {
        /* There are no new HTML library structures */

        if (up->lasttoken)
        {
          /* There is no new data, but lasttoken indicates there are more tokens */
          /* left in the token stream from earlier calls that haven't been dealt */
          /* with. So move to the next one.                                      */

          up->lasttoken     = up->lasttoken->next;
          if (token) *token = up->lasttoken;
        }
      }
      else
      {
        /* There are some new HTML library structures. */

        if (!(hf & HTML_GOT_MORE_IN_A_TABLE))
        {
          /* The flag is unset, so the structures were added to the main token */
          /* stream and not to part of a table structure.                      */

          if (up->lasttoken)
          {
            /* Even though there are new structures, we still have older ones */
            /* that are not dealt with, so move to the next one (the remote   */
            /* server is sending us data than we're processing it - yay!).    */

            up->lasttoken     = up->lasttoken->next;
            if (token) *token = up->lasttoken;
          }
          else
          {
            /* There are no earlier structures left to deal with, so start on */
            /* the first of the new batch.                                    */

            up->lasttoken     = new;
            if (token) *token = up->lasttoken;
          }
        }
        else
        {
          /* The HTML_GOT_MORE_IN_A_TABLE flag is set, so structures were added to */
          /* a table arrangement, as well as (possibly) the main stream after it.  */

          if (!up->lasttoken)
          {
            /* We weren't waiting to process anything from an earlier call, so */
            /* start on this new table structure.                              */

            up->lasttoken     = new;
            if (token) *token = up->lasttoken;
          }
          else
          {
            /* We have undealt with structures from a previous fetch. Now, if */
            /* we are already on the same table structure as returned by the  */
            /* HtmlGetStream call, then stay there (i.e. process the new data */
            /* inside the table). Otherwise, move on.                         */

            if (up->lasttoken != new) up->lasttoken = up->lasttoken->next;
            if (token) *token = up->lasttoken;
          }
        }
      }

      /* If we've moved on to, or were already on no token, then whether or */
      /* not the fetch is still in progress determines whether or not we're */
      /* waiting. Otherwise, we aren't waiting for anything.                */

      if (!up->lasttoken) *waiting = !!up->fetching;
      else                *waiting = 0;
    }
    else if (up->authorised != 1)
    {
      /* The stream hasn't been identified as HTML, text or whatever, */
      /* but there isn't an authorisation in progress.                */

      int    s, o = 0;
      char * redirect;
      int    code;
      int    type;
      int    parseable;

      /* Get the fetch status */

      if (image || b->displayed == Display_Fetched_Page)
      {
        e = url_status(0, handle, &s, NULL, NULL);
        if (e) return e;
      }
      else s = URL_Status_Done;

      redirect  = NULL;
      type      = TYPE_UNKNOWN;
      parseable = 0;

      /* HttpStripHeaders, when passed a pointer to some document data, */
      /* and an offest into that stream, returns the offset into the    */
      /* stream at which it starts assuming HTTP style headers (if      */
      /* there is such a point).                                        */

      o = *source ? HttpStripHeaders((char *) *source, flex_size(source)) : 0;

      /* If o is 0, there were no HTTP headers. If o is -1, there wasn't */
      /* enough data to tell. Else, there were headers, and o is the     */
      /* offset into the stream of the data that follows those headers.  */

      if (o > 0)
      {
        #ifdef DUMP_HEADERS
          {
            FILE * file;
            int    byte;

            file = fopen("<Wimp$ScrapDir>.Headers", "ab");

            if (!image) fprintf(file, "For URL '%s', received header:\r\n\r\n", url);
            else        fprintf(file, "For image '%s', received header:\r\n\r\n", url);

            for (byte = 0; byte < o; byte++)
            {
              fputc((int) (*((char *) (((int) *source) + byte))), file);
            }

            fclose(file);
          }
        #endif

        /* There are HTTP style headers in the data */
        /* stream; try to identify that stream.     */

        code = HtmlIdentify(ref_url,                     /* Allow relative redirections to work */
                            (char *) *source,            /* Pointer to head of data stream      */
                            flex_size(source),           /* Amount of data in the stream        */
                            (s & URL_Status_Done) != 0,  /* Is it a complete stream? 1 = yes    */
                            &redirect,                   /* Will point to a URL if code = 302   */
                            &type,                       /* Will hold a filetype                */
                            &parseable);                 /* Will say if the data is parseable   */

        /* Discard the stuff before the HTTP style headers by moving the data over them */

        if (o != flex_size(source))
        {
          #ifdef TRACE
            if (tl & (1u<<18)) Printf("\0213html_get_next_token: memove from %p to %p for %d bytes\0217\n",
                                    *source,
                                    (char*) (((int) *source) + o),
                                    flex_size(source) - o);
          #endif

          memmove(*source,
                  (char*) (((int) *source) + o),
                  flex_size(source) - o);

          /* Set o to the size of the data stream that is now in use, */
          /* and shrink the source store to this size.                */

          #ifdef TRACE
            if (tl & (1u<<12)) Printf("html_get_next_token: flex_extend to shrink source code store by %d to %d\n",o,flex_size(source) - o);
            flexcount -= o;
            if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
          #endif
        }

        o = flex_size(source) - o;

        /* If the size of the store minus o is less than 0, HttpStripHeaders */
        /* has failed completely and we must get out before everything else  */
        /* comes down...!                                                    */

        if (o < 0)
        {
          erb.errnum = Utils_Error_Custom_Fatal;

          StrNCpy0(erb.errmess,
                   lookup_token("HSHOvrrn:Serious internal error - HtmlStripHeaders has failed; must exit immediately.",
                                0,
                                0));

          show_error_cont(&erb); /* This will cause exit(EXIT_FAILURE) eventually. */
        }

        flex_extend(source, o); /* (Which shrinks the source store) */

        /* Interpret the codes returned by HtmlIdentify(). */

        switch (code)
        {
          /* Redirect; 'redirect' is a pointer to a new URL. */

          case 302:
          {
            /* Stop the current fetch and free the source store,   */
            /* remembering to invalidate the anchor pointing to it */

            url_stop(0, handle);

            #ifdef TRACE
              if (tl & (1u<<12)) Printf("html_get_next_token: (2) flex_free block %p which held page source\n",source);
              flexcount -= flex_size(source);
              if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
            #endif

            flex_free(source);
            *source = NULL;

            /* Ensure POST requests are now cleared (we shouldn't continue */
            /* POSTing to redirected URLs)                                 */

            if (up->method == URL_Method_http_POST)
            {
              /* When the headers are build, the POST data (starting with a Content-Length */
              /* entry) are put in first and everything else is inserted above it. As the  */
              /* comments on this code say, this is organised so we can simplify things    */
              /* here and just chop off everything at Content-Length and below, rather     */
              /* than having to carefully remove the appropriate header lines and body.�   */

              if (up->extradata)
              {
                char * strip = strstr(up->extradata, "Content-Length");

                if (strip)
                {
                  int len;

                  /* How much do we want to keep? */

                  len = strstr(up->extradata, "Content-Length") - up->extradata; /* Don't use 'strip' in case flex shifted when 'len' was stacked */

                  #ifdef TRACE
                    {
                      int rmv = flex_size((flex_ptr) &up->extradata) - len;
                      flexcount -= rmv;
                      if (tl & (1u<<13)) Printf("**   flexcount: %d\n",flexcount);
                    }
                  #endif

                  /* Resize the block */

                  flex_extend((flex_ptr) &up->extradata, len);
                }
              }

              /* Change the fetch method to GET */

              up->method = URL_Method_http_GET;
            }

            /* Customer specific */

            #ifdef CUSTOMER_SPECIAL

              if (
                   !strcmp(redirect,"http://www.customer.com/login.html")  ||
                   !strcmp(redirect,"http://www.customer.com/index.html")
                 )
              {
                // Send out cookie...

                redirect = (char *) "http://www.customer.com/simple.html";
              }

            #endif

            /* Set the fetch's urlstat structure to say that */
            /* no data has been fetched                      */

            up->fetched = 0;

            /* Start a fetch on the new URL */

            e = url_get_url(0,             /* Flags (must be 0)                   */
                            handle,        /* Session handle                      */
                            up->method,    /* Fetch method                        */
                            redirect,      /* URL to get                          */
                            up->extradata, /* Extra data for POST etc.            */
                            NULL,          /* We're ignoring the returned status  */
                            2);            /* Mode 2 = fetch both header and data */

            /* Return any errors that url_get_url generated */

            if (e) return e;

            /* This function returns the address of the new URL in   */
            /* 'remaining', flagging this with a waiting status of 2 */
            /* - and yes, this is quite odd.                         */

            *waiting   = 2;
            *remaining = (int) redirect; /* Not redirect_to, as it may be freed now */
          }
          break;

          /* Authorise; the server requested authorisation before it */
          /* would deliver the page.                                 */

          case 401:
          {
            char * realm;
            char   host[128];
            char   username[MaxAuthUser];
            char   password[MaxAuthUser];
            int    po;

            /* Try to find the host and realm */

            urlutils_host_name_from_url(ref_url, host, sizeof(host));

            /* (The realm will lie in the string pointed to by */
            /* 'redirect', between two double quotes).         */

            realm = authorise_read_realm(redirect);

            /* Ditch any document data got so far, we don't need it now */
            /* (it only contains e.g. header information).              */

            if (source)
            {
              flex_free((flex_ptr) source);
              *source = NULL;
            }

            up->fetched = 0;

            /* If we've already tried this, then the authorisation failed, */
            /* so give an appropriate error.                               */

            if (up->authorised >= 2)
            {
              authorise_forget(host, realm);

              erb.errnum = Utils_Error_Custom_Message;

              StrNCpy0(erb.errmess,
                       lookup_token("BadAuthor:Authorisation failed; you must use a valid user name and password.",
                                    0,
                                    0));

              return &erb;
            }

            /* Stop the URL module trying to get anything else, and set */
            /* the flag to say we're authorising this fetch.            */

            url_stop(0, handle);
            up->authorised = 1;

            /* If there is a user name and / or password available already, */
            /* use that and authenticate immediately.                       */

            username[0] = password[0] = 0;

            po = authorise_find_user_name(host, realm);

            if (po >= 0)
            {
              StrNCpy0(username, authorise + po);

              po = authorise_find_password(host, realm);

              if (po >= 0)
              {
                StrNCpy0(password, authorise + po);
              }

              fetch_authorisation_proceed(b, up, realm, url);
            }

            /* Otherwise, get this information from a dialogue box and */
            /* allow the authentication to happen later, when the user */
            /* has done relevant things with the dialogue.             */

            else
            {
              char     prompt[MaxAuthDisp];
              int      f;
              ObjectId dbox;

              /* Ensure the authorisation dialogue is created and event handlers */
              /* are registered for it.                                          */

              e = authorise_create_dialogue((void *) b, &dbox);
              if (e) return e;

              /* -4 corrects for %s being replaced by host / realm strings, plus */
              /* a terminator at the end of the whole lot.                       */

              f = strlen(realm) + strlen(host); /* (But no terminators needed for these, so no '+ 1's) */
              lookup_token("Authorise:Please enter a user name and a password for %%s at %%s",0,0);
              f += ((signed int) strlen(tokens)) - 4 + 1; /* Yuck... */

              /* If the string is too big for the prompt or null, put a */
              /* simple version in instead.                             */

              if (f < 0 || f > sizeof(prompt))
              {
                lookup_token("AuthorShr:Please enter a user name and a password.",0,0);
                e = button_set_value(0, dbox, AuthButton, tokens);
              }
              else
              {
                sprintf(prompt, tokens, realm, host);
                e = button_set_value(0, dbox, AuthButton, prompt);
              }

              if (e) return e;

              /* Empty the user name and password writables */

              e = writablefield_set_value(0, dbox, AuthUserWrit, "");
              if (e) return e;

              e = writablefield_set_value(0, dbox, AuthPassWrit, "");
              if (e) return e;

              /* Show the dialogue and set the authorising flag */

              e = toolbox_show_object(Toolbox_ShowObject_AsMenu,
                                      dbox,
                                      Toolbox_ShowObject_Centre,
                                      NULL,
                                      b->self_id,
                                      -1);
              if (e) return e;
              else menusrc = Menu_Authorise, authorising = 1;
            }
          }
          break;

          /* Catch anything else 'just in case'. Guess that the data */
          /* is parseable and let this drop through to the ordinary  */
          /* URL handling code.                                      */

          default: parseable = 1; /* ...so no 'break' */

          /* An ordinary URL. */

          case 200:
          {
            if (!up->allowparse) parseable = 0;

            /* If the data is apparently parseable, flag the status as 'waiting' */
            /* else flag it as 'not parseable'.                                  */

            switch (parseable)
            {
              default:
              case TYPE_UNKNOWN:
              case TYPE_IMAGEFILE: *waiting = 3;
              break;

              case TYPE_TEXTFILE:
              case TYPE_HTMLFILE:  *waiting = 1;
              break;
            }

            if (parseable == TYPE_IMAGEFILE)
            {
              /* For images, stop the current fetch and 'redirect' to */
              /* an internal page which will fetch the image inline.  */
              /* This is inefficient as you start to fetch the image  */
              /* twice; on slow servers, something of a killer...     */
              /* Unfortunately, pressure of time (yet *again*)        */
              /* precludes a more elegant solution for the moment.    */

              url_stop(0, handle);

              #ifdef TRACE
                if (tl & (1u<<12)) Printf("html_get_next_token: (2) flex_free block %p which held page source\n",source);
                flexcount -= flex_size(source);
                if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
              #endif

              flex_free(source);
              *source = NULL;

              /* Set the fetch's urlstat structure to say that */
              /* no data has been fetched                      */

              up->fetched = 0;

              redirect = Internal_URL "PExtImage";

              /* Start a fetch on the new URL */

              e = url_get_url(0,             /* Flags (must be 0)                   */
                              handle,        /* Session handle                      */
                              up->method,    /* Fetch method                        */
                              redirect,      /* URL to get                          */
                              up->extradata, /* Extra data for POST etc.            */
                              NULL,          /* We're ignoring the returned status  */
                              2);            /* Mode 2 = fetch both header and data */

              if (e) return e;

              b->displayed = Display_External_Image;

              /* This function returns the address of the new URL in   */
              /* 'remaining', flagging this with a waiting status of 2 */
              /* - and yes, this is quite odd.                         */

              *waiting   = 2;
              *remaining = (int) redirect;
            }
            else
            {
              *remaining = type;

              /* Set the type according to the parseable flag */

              up->type = parseable;

              /* Flag that we've identified the stream */

              up->identified = 1;
            }
          }
          break;

          /* If 0, haven't identified the stream yet */

          case 0:
          break;

        /* Closure of 'switch' statement checking the return code */
        /* from the HtmlIdentify() function call                  */
        }

      /* Closure of 'if' statement that checked there was recognised data */
      /* following HTTP style headers in the data stream                  */
      }

    /* Closure of 'if' statement that checked the urlstat structure  */
    /* to see if authorisation was in progress (and only proceeded   */
    /* if it was not)                                                */
    }

  /* Closure of 'if' statement that followed the url_read_data    */
  /* function call and associated memory allocation procedures,   */
  /* and only proceeded if data had been fetched and authoristion */
  /* was not flagged as being in progress.                        */
  }

  /* If no data has been fetched from the url_read_data call   */
  /* from earlier on, and there hasn't been an error flagged   */
  /* so far, and both the general 'authorisation taking place' */
  /* and urlstat-based 'authorisation in progress' flags are   */
  /* clear, ask the URL module for its current status. If it   */
  /* says it has finished (which would explain this set of     */
  /* circumstances - basically, having no data from the        */
  /* url_read_data call but nothing else is wrong), set the    */
  /* urlstat structure flag to say fetching is no longer in    */
  /* progress.                                                 */

  if (!r && !e && up->authorised != 1 && !authorising)
  {
    int s;

    if (image || b->displayed == Display_Fetched_Page)
    {
      e = url_status(0, handle, &s, NULL, NULL);
      if (e) return e;
    }
    else s = URL_Status_Done; /* Internal URLs 'fetch' immediately */

    if (s & URL_Status_Done) up->fetching = 0;
  }

  /* If we've been passed somewhere to put the size of the store, */
  /* and if the store is present, return the size of it.          */

  if (size) *size = (*source) ? flex_size(source) : 0;

  #ifdef TRACE
    if (tl & (1u<<6))
    {
      if (!e) Printf("html_get_next_token: Successful\n");
      else Printf("html_get_next_token: Exitting with an error\n");
    }
  #endif

  /* Exit, passing on any error if there is one */

  return (e);
}

/*************************************************/
/* html_get_next_chunk()                         */
/*                                               */
/* Gets a chunk of data from a stream, assuming  */
/* that it is not HTML.                          */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the fetch;            */
/*                                               */
/*             The fetch handle;                 */
/*                                               */
/*             Pointer to buffer into which the  */
/*             fetched data will be placed (as   */
/*             a char *);                        */
/*                                               */
/*             Size of the buffer;               */
/*                                               */
/*             Pointer to an int into which 1 is */
/*             placed if the fetch is complete,  */
/*             else 0 is returned (this pointer  */
/*             may be NULL);                     */
/*                                               */
/*             Pointer to an int into which the  */
/*             number of bytes fetched is placed */
/*             (which may also be NULL).         */
/*                                               */
/* Returns:    See parameters list.              */
/*                                               */
/* Assumes:    That if the browser_data struct   */
/*             pointer is NULL, the fetch is not */
/*             for an internal URL.              */
/*************************************************/

_kernel_oserror * html_get_next_chunk(browser_data * b, unsigned int handle, char * buffer,
                                      int size, int * done, int * bytes)
{
  _kernel_oserror * e;
  urlstat         * up;
  int               s, t;

//
// This function does not know about internal URLs yet (so parameter 'b' is currently unused)...
// BEWARE when using this, as of course this URL fetch may not be for page data under an
// internal URL.
//

  /* Find the urlstat structure for the fetch handle */

  up = fetch_list;

  while (up && up->session != handle) up = up->next;

  if (!up)
  {
    erb.errnum = Utils_Error_Custom_Fatal;

    StrNCpy0(erb.errmess,
             lookup_token("StrNotFd:Internal error: Can't find structure in %0.",
                          0,
                          "html_get_next_chunk()"));
    return &erb;
  }

  /* Read some data */

  // Printf("handle, buffer, size, bytes: %p, %p, %d, %d\n",handle,buffer,size,bytes);

  e = url_read_data(0,
                    handle,
                    buffer,
                    size,
                    NULL,
                    bytes,
                    &t);

  if (e) return e;

  /* Get the fetch status */

  e = url_status(0,
                 handle,
                 &s,
                 NULL,
                 NULL);

  if (e) return e;

  /* Fill in 'done' as appropriate to the fetch status and exit */

  if (done) *done = (s & URL_Status_Done) ? 1 : 0;

  return NULL;
}

/*************************************************/
/* url_register()                                */
/*                                               */
/* Registers a requirement to fetch a URL with   */
/* the URL module.                               */
/*                                               */
/* Parameters: Flags (must be 0 at present);     */
/*             Pointer to int into which the     */
/*             session handle is placed. May be  */
/*             NULL.                             */
/*                                               */
/* Returns:    See parameters list.              */
/*************************************************/

_kernel_oserror * url_register(unsigned int flags, unsigned int * handle)
{
  _kernel_oserror * e;
  unsigned int      h;

  /* If there's a pointer to put the handle into, set it to zero initially */

  if (handle) *handle = 0;

  e = _swix(URL_Register,
            _IN(0) | _OUT(1),

            flags,

            &h);

  /* If the call didn't return an error, store the session handle */

  if ((!e) && (handle)) *handle = h;

  #ifdef TRACE
    if (tl & (1u<<6))
    {
      if (!e) Printf("url_register: Registered ID %d\n",*handle);
      else Printf("url_register: Exitting with error\n");
    }
  #endif

  return e;
}

/*************************************************/
/* url_deregister()                              */
/*                                               */
/* Deregisters a requirement to fetch a URL with */
/* the URL module.                               */
/*                                               */
/* Parameters: Flags (must be 0 at present);     */
/*             The session handle.               */
/*************************************************/

_kernel_oserror * url_deregister(unsigned int flags, unsigned int handle)
{
  #ifdef TRACE
    if (tl & (1u<<6)) Printf("url_deregister: Deregistering ID %d\n",handle);
  #endif

  /* Abort any current action */

  url_stop(flags,handle);

  /* Deregister the session */

  return _swix(URL_Deregister,
               _INR(0,1),

               flags,
               handle);
}

/*************************************************/
/* url_stop()                                    */
/*                                               */
/* Interrupts a fetch if one is going on.        */
/*                                               */
/* Parameters: Flags (must be 0 at present);     */
/*             The session handle.               */
/*************************************************/

_kernel_oserror * url_stop(unsigned int flags, unsigned int handle)
{
  #ifdef TRACE
    if (tl & (1u<<6)) Printf("url_stop: Stop with ID %d\n",handle);
  #endif

  return _swix(URL_Stop,
               _INR(0,1),

               flags,
               handle);
}

/*************************************************/
/* url_get_url()                                 */
/*                                               */
/* Starts fetching data from a URL.              */
/*                                               */
/* Parameters: Flags (must be 0 at present);     */
/*             The session handle;               */
/*             The fetch method, as in the       */
/*             html_get() function above;        */
/*             Pointer to URL to fetch;          */
/*             Pointer to any extra data to send */
/*             for POST etc.;                    */
/*             Pointer to an int into which a    */
/*             status flag is placed (this may   */
/*             be NULL);                         */
/*             The fetch mode                    */
/*                                               */
/*             0: Get data only,                 */
/*             1: Get header only,               */
/*             2: Get both.                      */
/*                                               */
/* Returns:    See parameters list.              */
/*************************************************/

_kernel_oserror * url_get_url(unsigned int flags, unsigned int handle, int method, char * url,
                              char * extradata, unsigned int * status, int mode)
{
  _kernel_oserror * e = NULL;
  int               s;

  #ifdef TRACE
    if (tl & (1u<<6)) Printf("url_get_url: Called with ID %d\n",handle);
  #endif

  /* If a pointer to the int in which status information can be written */
  /* is not NULL, set the current status to 0                           */

  if (status) *status = 0;

  if (url && !strncmp(url, Internal_URL, strlen(Internal_URL)))
  {
    /* For an internal URL, don't try to actually use the URL module... */

    if (status) *status = 0;
  }
  else
  {
    #ifdef DUMP_HEADERS
      {
        FILE * file;

        file = fopen("<Wimp$ScrapDir>.Headers", "ab");

        if (!extradata || (extradata && extradata[0] == 0)) fprintf(file, "Fetch URL '%s'; sending standard header\r\n\r\n", url);
        else
        {
          fprintf(file, "Fetch URL '%s'; sending standard header plus:\r\n\r\n%s\r\n", url, extradata);
          if (extradata[strlen(extradata) - 1] != '\n') fprintf(file, "\r\n");
        }

        fclose(file);
      }
    #endif

    _swix(Hourglass_Start, _IN(0), 50);

    e = _swix(URL_GetURL,
              _INR(0,5) | _OUT(0),

              flags,
              handle,
              method,
              url,
              extradata,
              mode,

              &s);

    _swix(Hourglass_Off, 0);

    /* If the GetURL call didn't return an error, write the new status */

    if (status && !e) *status = s;
  }

  #ifdef TRACE
    if (tl & (1u<<6))
    {
      if (!e) Printf("url_get_url: Successful\n");
      else Printf("url_get_url: Exitting with error\n");
    }
  #endif

  return e;
}

/*************************************************/
/* url_read_data()                               */
/*                                               */
/* Asks the URL module to copy some of the data  */
/* it has fetched over into a buffer.            */
/*                                               */
/* Parameters: Flags (must be 0 at present);     */
/*             The session handle;               */
/*             Pointer to buffer into which the  */
/*             data is transferred, or NULL to   */
/*             just get a Pending state;         */
/*             The size of the buffer;           */
/*             Pointer to an int, into which a   */
/*             status word is placed;            */
/*             Pointer to int, into which the    */
/*             number of bytes read (and put in  */
/*             the bufrer) is placed;            */
/*             Pointer to int, into which the    */
/*             number of bytes that are still to */
/*             be fetched s placed.             */
/*                                               */
/* Returns:    See parameters list.              */
/*************************************************/

_kernel_oserror * url_read_data(unsigned int flags, unsigned int handle, void * buffer,
                                int size, int * status, int * read, int * pending)
{

  _kernel_oserror * e;
  int               s, r, p = 0;

  #ifdef TRACE
    if (tl & (1u<<6)) Printf("url_read_data: Called with ID %d\n",handle);
  #endif

  /* Ensure all returned data is set to a sensible default to start with */

  if (status)  * status  = 0;
  if (read)    * read    = 0;
  if (pending) * pending = 0;

  /* Call the URL module */

  e = _swix(URL_ReadData,
            _INR(0,3) | _OUT(0) | _OUTR(4,5),

            flags,
            handle,
            buffer,
            size,

            &s,
            &r,
            &p);

  #ifdef TRACE
    if (tl & (1u<<6)) Printf("url_read_data: Status %p, error %p returned\n",s,e);
  #endif

  /* In the absence of any errors, fill in the relevant returned data */

  if (!e)
  {
    if (status)  *status  = s;
    if (read)    *read    = r;
    if (pending) *pending = p;
  }

  #ifdef TRACE
    if (tl & (1u<<6))
    {
      if (!e) Printf("url_read_data: Successful\n");
      else Printf("url_read_data: Exitting with error\n");
    }
  #endif

  return e;
}

/*************************************************/
/* url_status()                                  */
/*                                               */
/* Returns the status of a fetch.                */
/*                                               */
/* Parameters: Flags (must be 0 at present);     */
/*             The session handle;               */
/*             Pointer to an int, into which a   */
/*             status word is placed;            */
/*             Pointer to an int, into which the */
/*             server's response is placed;      */
/*             Pointer to an int, into which the */
/*             number of bytes transferred so    */
/*             far is placed.                    */
/*                                               */
/* Returns:    See parameters list.              */
/*************************************************/

_kernel_oserror * url_status(unsigned int flags, unsigned int handle,
                             int * status, int * response, int * bytes)
{
  _kernel_oserror * e;
  int               s, r, b;

  #ifdef TRACE
    if (tl & (1u<<6)) Printf("url_status: Called with ID %d\n",handle);
  #endif

  /* Set returned data to zero to begin with */

  if (status)   *status   = 0;
  if (response) *response = 0;
  if (bytes)    *bytes    = 0;

  e = _swix(URL_Status,
            _INR(0,1) | _OUT(0) | _OUTR(2,3),

            flags,
            handle,

            &s,
            &r,
            &b);

  /* In the absence of an error, fill in the returned data */

  if (!e)
  {
    if (status)   *status   = s;
    if (response) *response = r;
    if (bytes)    *bytes    = b;
  }

  #ifdef TRACE
    if (tl & (1u<<6))
    {
      if (!e) Printf("url_status: Successful\n");
      else Printf("url_status: Exitting with error\n");
    }
  #endif

  return e;
}

/*************************************************/
/* url_set_proxy()                               */
/*                                               */
/* Instructs the URL module to fetch through a   */
/* proxy (or not).                               */
/*                                               */
/* Parameters: Flags (must be 0 at present);     */
/*             The session handle;               */
/*             Pointer to the base URL of the    */
/*             proxy server, e.g. for a local    */
/*             proxy, "http://127.0.0.1/";       */
/*             Pointer to a string holding the   */
/*             protocol to use, e.g. "http:" or  */
/*             "ftp:";                           */
/*             1 to disable proxying, 0 to       */
/*             enable it with the above data.    */
/*************************************************/

_kernel_oserror * url_set_proxy(int flags, unsigned int session, char * baseurl,
                                char * protocol, int noproxy)
{
  return _swix(URL_SetProxy,
               _INR(0,4),

               flags,
               session,
               baseurl,
               protocol,
               noproxy);
}

/*************************************************/