/* 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   : PlugIn.c                               */
/*                                                 */
/* Purpose: Supporting the generic RISC OS browser */
/*          Plug-In interface.                     */
/*                                                 */
/* Author : A.D.Hodgkinson                         */
/*                                                 */
/* History: 05-Oct-97: 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 "toolbox.h"

#include "svcprint.h"
#include "Global.h"
#include "FromROSLib.h"
#include "Utils.h"

#include "Browser.h"
#include "Fetch.h" /* (Which itself includes URLstat.h) */
#include "FetchHTML.h"
#include "FetchPage.h"
#include "Filetypes.h"
#include "Frames.h"
#include "Handlers.h"
#include "Images.h"
#include "MimeMap.h"
#include "MiscDefs.h"
#include "Object.h"
#include "Protocols.h"
#include "Redraw.h"
#include "Reformat.h"
#include "RMA.h"
#include "TokenUtils.h"
#include "URLveneer.h"
#include "Windows.h"

#include "PlugIn.h"

/* Statics */

static plugin_queue * plugin_queue_head = NULL;

static int            plugin_open_reference  = 0;
static int            plugin_open_tried_once = 0;
static char         * plugin_open_filename   = NULL;
static HStream      * plugin_open_token      = NULL;

static int            plugin_close_reference = 0;

/* Static function prototypes */

static _kernel_oserror * plugin_write_params_word     (int fh, int word);
static _kernel_oserror * plugin_write_params_string   (int fh, const char * string);
static _kernel_oserror * plugin_write_params_entry    (int fh, int type, const char * name, const char * data, const char * mime);

static _kernel_oserror * plugin_broadcast_plugin_open (void);

static _kernel_oserror * plugin_remove_head_item      (void);

/*************************************************/
/* plugin_return_string()                        */
/*                                               */
/* Takes a message block and string_value from   */
/* within it, and returns the string it points   */
/* to.                                           */
/*                                               */
/* Parameters: Pointer to a WimpMessage struct   */
/*             holding the string_value field;   */
/*                                               */
/*             Pointer to the string_value field */
/*             inside that message.              */
/*                                               */
/* Returns:    Pointer to the string the         */
/*             string_value references, or NULL  */
/*             if the value seems invalid.       */
/*************************************************/

const char * plugin_return_string(WimpMessage * m, string_value * sv)
{
  int offset = ((char *) sv) - ((char *) m);

  if (!m || !sv || !sv->offset) return NULL;

  /* Is the offset of the string_value field within the message */
  /* block out of range (i.e. the string_value does not appear  */
  /* to lie in the given message block)?                        */

  if (offset >= m->hdr.size || offset < sizeof(m->hdr)) return NULL;

  /* Now read the string_value field itself */

  if (sv->offset < 256)
  {
    if (sizeof(m->hdr) + offset >= m->hdr.size) return NULL;

    else return (const char *) (((char *) m) + sizeof(m->hdr) + sv->offset);
  }
  else return (const char *) sv->offset;
}

/*************************************************/
/* plugin_write_params_word()                    */
/*                                               */
/* Writes a word to a given parameters file,     */
/* in little-endian order.                       */
/*                                               */
/* Parameters: RISC OS file handle of the file;  */
/*                                               */
/*             Word to write.                    */
/*************************************************/

static _kernel_oserror * plugin_write_params_word(int fh, int word)
{
  _kernel_oserror * e;
  char              integer[4];

  /* #define Brain_Off... */

  integer[0] = (word & 0xff);
  integer[1] = ((word & 0xff00) >> 8);
  integer[2] = ((word & 0xff0000) >> 16);
  integer[3] = ((word & 0xff000000) >> 24);

  RetError(_swix(OS_BPut,
                 _INR(0,1),

                 integer[0],
                 fh));

  RetError(_swix(OS_BPut,
                 _INR(0,1),

                 integer[1],
                 fh));

  RetError(_swix(OS_BPut,
                 _INR(0,1),

                 integer[2],
                 fh));

  RetError(_swix(OS_BPut,
                 _INR(0,1),

                 integer[3],
                 fh));

  return NULL;
}

/*************************************************/
/* plugin_write_params_string()                  */
/*                                               */
/* Writes a string to a given parameters file,   */
/* padding with zeros to a length which is a     */
/* multiple of 4. If the string length, not      */
/* including terminator, is already a multiple   */
/* of four, no extra bytes are written.          */
/*                                               */
/* Parameters: RISC OS file handle of the file;  */
/*                                               */
/*             Pointer to the string.            */
/*************************************************/

static _kernel_oserror * plugin_write_params_string(int fh, const char * string)
{
  _kernel_oserror * e;
  const char      * s;
  int               i = 0;
  int               pad;

  /* Write the main string */

  s = string ? string : "";

  while (s[i])
  {
    RetError(_swix(OS_BPut,
                   _INR(0,1),

                   s[i],
                   fh));

    i++;
  }

  /* Pad if necessary */

  pad = 4 - (strlen(s) % 4);

  if (pad < 4)
  {
    for (i = 0; i < pad; i ++)
    {
      RetError(_swix(OS_BPut,
                     _INR(0,1),

                     '\0',
                     fh));
    }
  }

  return NULL;
}

/*************************************************/
/* plugin_write_params_entry()                   */
/*                                               */
/* Writes an entry to the given parameters file. */
/*                                               */
/* Parameters: RISC OS file handle of the file;  */
/*                                               */
/*             Type of the entry (see            */
/*             PlugIn.h);                        */
/*                                               */
/*             Pointer to the name string;       */
/*                                               */
/*             Pointer to the data string;       */
/*                                               */
/*             Pointer to the Mime string.       */
/*                                               */
/* Assumes:    Any of the pointers may be NULL   */
/*             or a zero length string to write  */
/*             a zero length field for that      */
/*             item.                             */
/*************************************************/

static _kernel_oserror * plugin_write_params_entry(int fh, int type, const char * name, const char * data, const char * mime)
{
  _kernel_oserror * e;
  int               length;
  const char      * n;
  const char      * d;
  const char      * m;

  /* Make life easier */

  n = name ? name : "";
  d = data ? data : "";
  m = mime ? mime : "";

  /* Write the type */

  RetError(plugin_write_params_word(fh, type));

  /* Calculate and write the entry length */

  length = 4 + strlen(n);
  length = (int) WordAlign(length);

  length += 4 + strlen(d);
  length = (int) WordAlign(length);

  length += 4 + strlen(m);
  length = (int) WordAlign(length);

  RetError(plugin_write_params_word(fh, length));

  /* The name field */

  RetError(plugin_write_params_word(fh, strlen(n)));
  RetError(plugin_write_params_string(fh, n));

  /* The data field */

  RetError(plugin_write_params_word(fh, strlen(d)));
  RetError(plugin_write_params_string(fh, d));

  /* The mime type field */

  RetError(plugin_write_params_word(fh, strlen(m)));
  RetError(plugin_write_params_string(fh, m));

  return NULL;
}

/*************************************************/
/* plugin_write_params()                         */
/*                                               */
/* Writes a parameters file to a unique filename */
/* based on the given HStream.                   */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the HStream;          */
/*                                               */
/*             Pointer to the HStream struct     */
/*             representing the OBJECT, APPLET   */
/*             or EMBED tag that the parameters  */
/*             file is for;                      */
/*                                               */
/*             Buffer for the filename that was  */
/*             used, or if the buffer size given */
/*             is zero, pointer to the filename  */
/*             to use;                           */
/*                                               */
/*             Size of the buffer, or zero if    */
/*             the above parameter is a pointer  */
/*             to the filename to use instead of */
/*             a pointer to a buffer to receive  */
/*             a locally generated system unique */
/*             filename;                         */
/*                                               */
/*             Pointer to a BBox describing the  */
/*             size of the Plug-In window.       */
/*************************************************/

_kernel_oserror * plugin_write_params(browser_data * b, HStream * t, char * buffer, int buffer_size, BBox * position)
{
  _kernel_oserror * e  = NULL;
  int               fh = 0;
  char            * data;
  char              version[5];
  char              number [64];

  if (buffer_size) protocols_util_make_unique_name(buffer, buffer_size);

  RetError(_swix(OS_Find,
                 _INR(0,1) | _OUT(0),

                 0x8c, /* Create empty file with r/w access, return errors */
                 buffer,

                 &fh));

  /* Write the browser special fields */

  data = browser_current_url(b);
  if (!data) data = browser_fetch_url(b);

  e = plugin_write_params_entry(fh,
                                PlugIn_ParamType_BrowserSpecial,
                                "BASEHREF",
                                data,
                                NULL);

  if (e) goto plugin_write_params_exit;

  data = lookup_token("_TaskName", 1, 0);

  e = plugin_write_params_entry(fh,
                                PlugIn_ParamType_BrowserSpecial,
                                "USERAGENT",
                                data,
                                NULL);

  if (e) goto plugin_write_params_exit;

  data = lookup_token("Version", 1, 0);
  StrNCpy0(version, data);

  e = plugin_write_params_entry(fh,
                                PlugIn_ParamType_BrowserSpecial,
                                "UAVERSION",
                                version,
                                NULL);

  if (e) goto plugin_write_params_exit;

  e = plugin_write_params_entry(fh,
                                PlugIn_ParamType_BrowserSpecial,
                                "APIVERSION",
                                "1.10",
                                NULL);

  if (e) goto plugin_write_params_exit;

  /* Now write details based on the Object type */

  if (
       (
         HtmlOBJECTclassid(t)   &&
         *HtmlOBJECTclassid(t)
       )
     )
  {
    /* A code object, e.g. Java applet */

    const char * cis = HtmlOBJECTclassid(t);
    const char * cie;

    /* Get details from the CLASSID attribute, giving just the leafname */

    cie = cis + strlen(cis) - 1;

    while (cie > cis && *cie != '/') cie --;
    if (*cie == '/') cie++;

    e = plugin_write_params_entry(fh,
                                  PlugIn_ParamType_URLFromOBJECT,
                                  "CLASSID",
                                  cie,
                                  HtmlOBJECTcodetype(t));

    if (e) goto plugin_write_params_exit;

    /* CODEBASE, if we have it */

    if (HtmlOBJECTcodebase(t) && *HtmlOBJECTcodebase(t))
    {
      e = plugin_write_params_entry(fh,
                                    PlugIn_ParamType_DataFromOBJECT,
                                    "CODEBASE",
                                    HtmlOBJECTcodebase(t),
                                    NULL);

      if (e) goto plugin_write_params_exit;
    }
  }
  else if (HtmlOBJECTdata(t) && *HtmlOBJECTdata(t))
  {
    /* A data object, e.g. Shockwave movie */

    e = plugin_write_params_entry(fh,
                                  PlugIn_ParamType_URLFromOBJECT,
                                  "DATA",
                                  HtmlOBJECTdata(t),
                                  HtmlOBJECTtype(t));
  }

  /* Some generic bits */

  if (HtmlOBJECTstandby(t) && *HtmlOBJECTstandby(t))
  {
    e = plugin_write_params_entry(fh,
                                  PlugIn_ParamType_DataFromOBJECT,
                                  "STANDBY",
                                  HtmlOBJECTstandby(t),
                                  NULL);

    if (e) goto plugin_write_params_exit;
  }

  /* Width and height */

  sprintf(number, "%d", (position->ymax - position->ymin) / 2);

  e = plugin_write_params_entry(fh,
                                PlugIn_ParamType_DataFromOBJECT,
                                "HEIGHT",
                                number,
                                NULL);

  if (e) goto plugin_write_params_exit;

  sprintf(number, "%d", (position->xmax - position->xmin) / 2);

  e = plugin_write_params_entry(fh,
                                PlugIn_ParamType_DataFromOBJECT,
                                "WIDTH",
                                number,
                                NULL);

  if (e) goto plugin_write_params_exit;

  /* If we have a child token stream, look for PARAM tags */

  if (HtmlOBJECTstream(t))
  {
    HStream * child = HtmlOBJECTstream(t);

    while (child)
    {
      if (child->tagno == TAG_PARAM)
      {
        int type;

        /* Get the entry type from the valuetype field of the tag */

        switch (HtmlPARAMvaluetype(child))
        {
          default:
          case paramtype_DATA:   type = PlugIn_ParamType_DataFromPARAM;   break;
          case paramtype_OBJECT: type = PlugIn_ParamType_ObjectFromPARAM; break;
          case paramtype_REF:    type = PlugIn_ParamType_URLFromPARAM;    break;
        }

        /* Write the entry */

        e = plugin_write_params_entry(fh,
                                      type,
                                      HtmlPARAMname(child),
                                      HtmlPARAMvalue(child),
                                      HtmlPARAMtype(child));

        if (e) goto plugin_write_params_exit;
      }

      child = child->next;
    }
  }

  /* Various actions for different builds, if we don't understand */
  /* the token at all.                                            */

  else
  {
    #ifdef TRACE

      erb.errnum = Utils_Error_Custom_Message;

      strcpy(erb.errmess,
        "OBJECT tag type not understood in plugin_write_params");

      e = &erb;

    #else

      data = data;

    #endif
  }

  /* Write the terminating entry */

  RetError(plugin_write_params_word(fh, PlugIn_ParamType_Terminator));

plugin_write_params_exit:

  /* Exit by closing the file and returning any */
  /* generated error.                           */

  if (fh)
  {
    _swix(OS_Find,
     _INR(0,1),

     0, /* Close file */
     fh);

    /* Set the filetype */

    _swix(OS_File,
     _INR(0,2),

     18,
     buffer,
     FileType_DATA);
  }

  return e;
}

/*************************************************/
/* plugin_broadcast_plugin_open()                */
/*                                               */
/* Where it all starts - broadcast a             */
/* Message_PlugIn_Open for the Plug-In at the    */
/* head of the queue.                            */
/*                                               */
/* External functions should invoke this by      */
/* adding to the Plug-In queue, not by a direct  */
/* call here.                                    */
/*************************************************/

static _kernel_oserror * plugin_broadcast_plugin_open(void)
{
  _kernel_oserror * e;
  char              params[Limits_OS_Pathname];
  const char      * type;
  WimpMessage       m;
  MPlugIn_Open    * open = (MPlugIn_Open *) &m.data;
  browser_data    * b;
  HStream         * t;
  BBox            * position;

  #ifdef TRACE
    if (tl & (1u<<30)) Printf("plugin_broadcast_plugin_open: Called, head item is %p\n", plugin_queue_head);
  #endif

  /* Can't do anything if there are no queue items */

  if (!plugin_queue_head)
  {
    #ifdef TRACE

      erb.errnum  = Utils_Error_Custom_Normal;
      strcpy(erb.errmess,
             "NULL queue head in plugin_broadcast_plugin_open");
      return &erb;

    #endif

    return NULL;
  }

  /* Make life a bit less verbose... */

  b = plugin_queue_head->browser;
  t = plugin_queue_head->token;

  position = &plugin_queue_head->position;

  #ifdef TRACE
    if (tl & (1u<<30))
    {
      char debugbuf[1024];
      sprintf(debugbuf,"plugin_broadcast_plugin_open: Open at BBox %d, %d, %d, %d",position->xmin,position->ymin,position->xmax,position->ymax);
      Printf("%s\n",debugbuf);
    }
  #endif

  /* Write the parameters file */

  RetError(plugin_write_params(b, t, params, sizeof(params), position));

  // For debugging, this may be useful...
  //
  // plugin_write_params(b, t, "ADFS::4.$.DebugParam", 0, position);

  #ifdef TRACE
    if (tl & (1u<<30)) Printf("plugin_broadcast_plugin_open: Params file is at '%s'\n",params);
  #endif

  /* Fill in the header */

  m.hdr.size        = sizeof(MPlugIn_Open) + sizeof(m.hdr);
  m.hdr.your_ref    = 0;
  m.hdr.action_code = Message_PlugIn_Open;

  /* Fill in the body */

  open->flags                   = 0;
  open->reserved                = 0;
  open->browser_instance_handle = (int) b;
  open->parent_window_handle    = b->window_handle;
  open->parent_area             = *position;

  plugin_open_token = t;

  open->file_type = 0;

  /* Get the Mime type */

  type = HtmlOBJECTcodetype(t);
  if (!type || !*type) type = HtmlOBJECTtype(t);

  /* If we've got one, ask the Mime Mapper what RISC OS filetype this is */

  if (type && *type)
  {
    if (mimemap_mime_to_riscos(type, &open->file_type)) open->file_type = FileType_DATA;
  }

  /* Otherwise get it from the filename extension */

  else
  {
    const char * data = HtmlOBJECTclassid(t);
    const char * ext;

    open->file_type = FileType_DATA;

    if (!data || !*data) data = HtmlOBJECTdata(t);

    if (data && * data)
    {
      ext = data + strlen(data) - 1;

      while (ext >= data && *ext != '.') ext--;

      if (*ext == '.')
      {
        if (mimemap_extension_to_riscos(ext, &open->file_type)) open->file_type = FileType_DATA;
      }
    }
  }

  #ifdef TRACE
    if (tl & (1u<<30)) Printf("plugin_broadcast_plugin_open: Filetype to use: 0x%03x\n", open->file_type);
  #endif

  /* If we already have RMA space for the filename, free it */

  if (plugin_open_filename)
  {
    rma_release(NULL, plugin_open_filename);
    plugin_open_filename = NULL;
  }

  /* (Re)claim RMA space for the filename */

  e = rma_claim(NULL, strlen(params) + 1, (void **) &plugin_open_filename);

  if (e) return e;
  if (!plugin_open_filename) return NULL;

  /* Copy it in and fill in the message field */

  strcpy(plugin_open_filename, params);

  open->file_name.ptr = plugin_open_filename;

  /* Broadcast the message */

  RetError(wimp_send_message(Wimp_EUserMessageRecorded, &m, 0, 0, NULL));

  /* Record my_ref in case it bounces */

  plugin_open_reference  = m.hdr.my_ref;
  plugin_open_tried_once = 0; /* We haven't tried once until we get the first bounce */

  #ifdef TRACE
    if (tl & (1u<<30)) Printf("plugin_broadcast_plugin_open: Successful\n",b,t);
  #endif

  return NULL;
}

/*************************************************/
/* plugin_plugin_open_bounced()                  */
/*                                               */
/* Handle a Message_PlugIn_Open bounce.          */
/*                                               */
/* Parameters: Pointer to a Wimp_Message struct  */
/*             holding the bounced message.      */
/*************************************************/

_kernel_oserror * plugin_plugin_open_bounced(WimpMessage * m)
{
  _kernel_oserror * e;
  MPlugIn_Open    * open = (MPlugIn_Open *) &m->data;

  #ifdef TRACE
    if (!plugin_open_tried_once)
    {
      if (tl & (1u<<30)) Printf("plugin_plugin_open_bounced: Called\n");
    }
    else
    {
      if (tl & (1u<<30)) Printf("plugin_plugin_open_bounced: Called again\n");
    }
  #endif

  if (m->hdr.my_ref == plugin_open_reference)
  {
    #ifdef TRACE
      if (tl & (1u<<30)) Printf("plugin_plugin_open_bounced: Message reference %x is known\n", m->hdr.my_ref);
    #endif

    if (plugin_open_tried_once)
    {
      /* Yuk, everything collapsed in a nasty great heap */

      #ifdef TRACE
        if (tl & (1u<<30)) Printf("plugin_plugin_open_bounced: Plug-In launch failed\n");
      #endif

      /* Delete the parameters file */

      remove(plugin_open_filename);

      /* Need to free up the RMA space used for the filename */

      rma_release(NULL, plugin_open_filename);

      plugin_open_filename   = NULL;

      plugin_open_reference  = 0;
      plugin_open_tried_once = 0;
      plugin_open_token      = NULL;

      /* Remove the item from the queue, launching a new one */
      /* if there is anything else there.                    */

      RetError(plugin_remove_head_item());
    }
    else
    {
      char             combuf[16]; /* Size of '@PlugInType_xxx' plus terminator */
      char             sysvar[22]; /* As above, preceeded by 'Alias$'           */
      _kernel_swi_regs r;
      int              required;
      BBox             new;

      /* Try launching the Plug-In through Wimp_StartTask */

      #ifdef TRACE
        if (tl & (1u<<30)) Printf("plugin_plugin_open_bounced: Attempting Plug-In launch\n");
      #endif

      plugin_open_tried_once = 1;

      if (open->file_type >= 0 && open->file_type <= 4095) sprintf(combuf, "@PlugInType_%03x", open->file_type);
      else                                                 sprintf(combuf, "@PlugInType_%03x", FileType_DATA);

      strcpy(sysvar, "Alias$");
      strcat(sysvar, combuf);

      /* Does the system variable exist? */

      r.r[0] = (int) sysvar;
      r.r[1] = (int) NULL;
      r.r[2] = -1;
      r.r[3] = 0;
      r.r[4] = 0;

      /* _swix will not work correctly for this particular SWI if */
      /* requiring the returned R2 value. Something to do with    */
      /* the call relying on generating an error, but _swix spots */
      /* it and pulls out earlier than the call expects. Or some  */
      /* such thing...                                            */

      _kernel_swi(OS_ReadVarVal, &r, &r);

      required = -r.r[2];

      if (required)
      {
        show_error_ret(_swix(Wimp_StartTask,
                             _IN(0),

                             combuf));
      }

      #ifdef TRACE

        else
        {
          if (tl & (1u<<30)) Printf("plugin_plugin_open_bounced: System variable '%s' is not set\n", sysvar);
        }

      #endif

      /* Now rebroadcast the message. Note we can't use the same position, */
      /* as between the two redrawing may have occurred that has changed   */
      /* where the Object now sits.                                        */

      if (
           !object_get_token_object_box((browser_data *) open->browser_instance_handle,
                                        plugin_open_token,
                                        &new)
         )
         open->parent_area = new;

      #ifdef TRACE
        if (tl & (1u<<30))
        {
          char debugbuf[1024];
          sprintf(debugbuf,"plugin_plugin_open_bounced: New BBox %d, %d, %d, %d",new.xmin,new.ymin,new.xmax,new.ymax);
          Printf("%s\n",debugbuf);

          if (tl & (1u<<30)) Printf("plugin_plugin_open_bounced: Rebroadcasting Message_PlugIn_Open\n");
        }
      #endif

      m->hdr.my_ref = m->hdr.your_ref = 0;

      e = wimp_send_message(Wimp_EUserMessageRecorded, m, 0, 0, NULL);

      /* Record my_ref in case it bounces for a second time */

      if (!e) plugin_open_reference = m->hdr.my_ref;
      else return e;
    }
  }

  #ifdef TRACE

    else
    {
      if (tl & (1u<<30)) Printf("plugin_plugin_open_bounced: Message reference %x is not known\n", m->hdr.my_ref);

      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Unknown message reference %x in plugin_broadcast_plugin_open",
              m->hdr.my_ref);

      return &erb;
    }

  #endif

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

  return NULL;
}

/*************************************************/
/* plugin_got_plugin_opening()                   */
/*                                               */
/* Handle reception of a Message_PlugIn_Opening. */
/*                                               */
/* Parameters: Pointer to a Wimp_Message struct  */
/*             relevant to the message.          */
/*************************************************/

_kernel_oserror * plugin_got_plugin_opening(WimpMessage * m)
{
  _kernel_oserror * e       = NULL;
  MPlugIn_Opening * opening = (MPlugIn_Opening *) &m->data;

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

  if (plugin_open_filename)
  {
    if (!(opening->flags & MPlugIn_Opening_WillDeleteParamsItself))
    {
      #ifdef TRACE
        if (tl & (1u<<30)) Printf("plugin_got_plugin_opening: Removing parameters file\n");
      #endif

      remove(plugin_open_filename);
    }

    /* Need to free up the RMA space used for the filename */

    rma_release(NULL, plugin_open_filename);
    plugin_open_filename = NULL;
  }

  /* Tell the Object the PlugIn details */

  object_set_token_object_plugin(opening->browser_instance_handle,
                                 plugin_open_token,
                                 opening->plugin_instance_handle,
                                 m->hdr.sender);

//  /* Deal with cases where the Plug-In wants stuff fetching */
//
//  if (opening->flags & MPlugIn_Opening_WantsDataResourceFetched)
//  {
//    #ifdef TRACE
//      if (tl & (1u<<30)) Printf("plugin_got_plugin_opening: Plug-In wants data resource fetched\n");
//    #endif
//
//    e = plugin_send_original_stream_new((browser_data *) opening->browser_instance_handle,
//                                        plugin_open_token,
//                                        opening->plugin_instance_handle,
//                                        m->hdr.sender);
//  }

  plugin_open_reference  = 0;
  plugin_open_tried_once = 0;
  plugin_open_filename   = NULL;
  plugin_open_token      = NULL;

  if (e) return e;

  /* Remove the item from the queue, launching a new one */
  /* if there is anything else there.                    */

  RetError(plugin_remove_head_item());

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

  return NULL;
}

/*************************************************/
/* plugin_send_plugin_close()                    */
/*                                               */
/* Tell a Plug-In to close down.                 */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the Plug-In (this is  */
/*             used for the browser instance     */
/*             handle in the Close message);     */
/*                                               */
/*             Plug-In instance handle;          */
/*                                               */
/*             Plug-In task handle.              */
/*************************************************/

_kernel_oserror * plugin_send_plugin_close(browser_data * b, unsigned int instance, unsigned int task)
{
  _kernel_oserror * e;
  WimpMessage       m;
  MPlugIn_Close   * close = (MPlugIn_Close *) &m.data;

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

  /* Fill in the header... */

  m.hdr.size        = sizeof(MPlugIn_Close) + sizeof(m.hdr);
  m.hdr.your_ref    = 0;
  m.hdr.action_code = Message_PlugIn_Close;

  /* ...and the body... */

  close->flags                   = 0;
  close->plugin_instance_handle  = instance;
  close->browser_instance_handle = (int) b;

  /* ...then send it. */

  e = wimp_send_message(Wimp_EUserMessageRecorded, &m, task, 0, NULL);

  /* Remember the reference for any replies */

  if (!e) plugin_close_reference = m.hdr.my_ref;

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

  return e;
}

/*************************************************/
/* plugin_send_original_reshape()                */
/*                                               */
/* Tell a Plug-In to reposition or resize itself */
/* - not as part of a Plug-In request to resize. */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the Plug-In (this is  */
/*             used for the browser instance     */
/*             handle in the Close message);     */
/*                                               */
/*             Plug-In instance handle;          */
/*                                               */
/*             Plug-In task handle;              */
/*                                               */
/*             New bounding box for the Plug-In  */
/*             child window (in (parent) window  */
/*             coordinates).                     */
/*************************************************/

_kernel_oserror * plugin_send_original_reshape(browser_data * b, unsigned int instance, unsigned int task, BBox * position)
{
  _kernel_oserror * e;
  WimpMessage       m;
  MPlugIn_Reshape * reshape = (MPlugIn_Reshape *) &m.data;

  #ifdef TRACE
    if (tl & (1u<<30))
    {
      char debugbuf[1024];
      sprintf(debugbuf,"plugin_send_original_reshape: Called for %p to move instance 0x%08x to %d, %d, %d, %d",b,instance,position->xmin,position->ymin,position->xmax,position->ymax);
      Printf("%s\n",debugbuf);
    }
  #endif

  /* Fill in the header... */

  m.hdr.size        = sizeof(MPlugIn_Reshape) + sizeof(m.hdr);
  m.hdr.your_ref    = 0;
  m.hdr.action_code = Message_PlugIn_Reshape;

  /* ...and the body... */

  reshape->flags                   = 0;
  reshape->plugin_instance_handle  = instance;
  reshape->browser_instance_handle = (int) b;
  reshape->parent_window_handle    = b->window_handle;
  reshape->parent_area             = *position;

  /* ...then send it. */

  e = wimp_send_message(Wimp_EUserMessageRecorded, &m, task, 0, NULL);

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

  return e;
}

/*************************************************/
/* plugin_send_original_stream_new()             */
/*                                               */
/* Sent out a Message_PlugIn_StreamNew to start  */
/* a fetch on a Plug-Ins behalf.                 */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the Plug-In;          */
/*                                               */
/*             Pointer to the HStream struct     */
/*             representing the Object that is   */
/*             representing the Plug-In;         */
/*                                               */
/*             Plug-In instance handle;          */
/*                                               */
/*             Plug-In task handle.              */
/*************************************************/

_kernel_oserror * plugin_send_original_stream_new(browser_data * b, HStream * t, unsigned int instance, unsigned int task)
{
  _kernel_oserror   * e;
  WimpMessage         m;
  MPlugIn_StreamNew * stream = (MPlugIn_StreamNew *) &m.data;
  const char        * data   = HtmlOBJECTdata(t);

  #ifdef TRACE
    if (tl & (1u<<30)) Printf("plugin_send_original_stream_new: Called for %p, instance 0x%08x\n",b,instance);
  #endif

  if (!data || !*data)
  {
    #ifdef TRACE
      if (tl & (1u<<30)) Printf("plugin_send_original_stream_new: No data resource - exitting\n");
    #endif

    return NULL;
  }

  #ifdef TRACE
    if (tl & (1u<<30)) Printf("plugin_send_original_stream_new: Initiate fetch for '%s'\n",data);
  #endif

  /* Initiate a fetch for this item */

  RetError(windows_create_browser((char *) data,
                                  NULL,
                                  NULL,
                                  NULL,
                                  1));

//  last_browser->browser_instance       = (unsigned int) b;
//  last_browser->plugin_instance        = instance;
//  last_browser->plugin_stream_instance = NULL;

  /* Fill in the header... */

  m.hdr.size        = sizeof(MPlugIn_StreamNew) + sizeof(m.hdr);
  m.hdr.your_ref    = 0;
  m.hdr.action_code = Message_PlugIn_StreamNew;

  /* ...and the body... */

  stream->flags                          = 0;
  stream->plugin_instance_handle         = instance;
  stream->browser_instance_handle        = (unsigned int) b;
  stream->browser_stream_instance_handle = (unsigned int) last_browser;

  stream->eos                            = 0;
  stream->last_modified                  = 0;
  stream->notify                         = 0;

  /* Then do zippo, sigh, deadlines. */

  // What we need to do here is claim RMA for the string_values,
  // send this out recorded delivery and deal with all return
  // cases.

  return NULL;
}

/*************************************************/
/* plugin_got_url_access()                       */
/*                                               */
/* Handle reception of a                         */
/* Message_PlugIn_URLAccess                      */
/*                                               */
/* Parameters: Pointer to a Wimp_Message struct  */
/*             relevant to the message.          */
/*************************************************/

_kernel_oserror * plugin_got_url_access(WimpMessage * m)
{
  _kernel_oserror   * e;
  MPlugIn_URLAccess * access = (MPlugIn_URLAccess *) &m->data;
  const char        * url;
  const char        * target;
  browser_data      * targetted;
  browser_data      * b = (browser_data *) access->browser_instance_handle;
  browser_data      * ancestor = utils_ancestor(b);

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

  url    = plugin_return_string(m, &access->url);
  target = plugin_return_string(m, &access->target);

  #ifdef TRACE
    if (tl & (1u<<30))
    {
      if (url)    Printf("plugin_got_url_access: URL '%s'\n", url);
      else        Printf("plugin_got_url_access: No URL\n");
      if (target) Printf("plugin_got_url_access: Target '%s'\n", target);
      else        Printf("plugin_got_url_access: No Target\n");
    }
  #endif

  if (url)
  {
    if (target) targetted = frames_find_target(b, target);
    else        targetted = NULL;

    /* Don't want to ever open a new window if configured */
    /* to run full screen.                                */

    if (targetted || choices.full_screen)
    {
      /* If a named target was found, open in that. Otherwise we must */
      /* be running full screen, so can't open a new window; in this  */
      /* case, open in the ancestor.                                  */

      RetError(fetchpage_new(targetted ? targetted : ancestor,
                             url,
                             1,
                             1));
    }
    else
    {
      /* If we've reached here, a named target wasn't found but the */
      /* browser isn't running full screen either, so open a new    */
      /* window with the name specified in the link.                */

      RetError(windows_create_browser((char *) url,
                                      NULL,
                                      NULL,
                                      (char *) target,
                                      0));
    }
  }

  return NULL;
}

/*************************************************/
/* plugin_add_queue_item()                       */
/*                                               */
/* Add details of a new Plug-In to the queue of  */
/* Plug-Ins. If there were none already in the   */
/* queue, then start this new one immediately.   */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the Object;           */
/*                                               */
/*             Pointer to the HStream struct     */
/*             representing the OBJECT, APPLET   */
/*             or EMBED tag (Object) that the    */
/*             message is for;                   */
/*                                               */
/*             Pointer to a BBox describing the  */
/*             where the Plug-In should open, in */
/*             parent window coordinates.        */
/*************************************************/

_kernel_oserror * plugin_add_queue_item(browser_data * b, HStream * t, BBox * position)
{
  plugin_queue * new;
  int            start_now = 0;

  #ifdef TRACE
    if (tl & (1u<<30)) Printf("plugin_add_queue_item: Called for %p, %p\n",b,t);
  #endif

  /* Allocate memory for the item */

  new = malloc(sizeof(plugin_queue));
  if (!new) return make_no_memory_error(21);

  if (plugin_queue_head)
  {
    /* Insert the new item in between the existing first and second items */

    new->next               = plugin_queue_head->next;
    plugin_queue_head->next = new;
  }
  else
  {
    new->next               = NULL;
    plugin_queue_head       = new;

    /* This is the only item in the queue, so try to start a Plug-In */
    /* for it as soon as possible.                                   */

    start_now = 1;
  }

  /* Fill in the rest of the structure */

  new->browser  = b;
  new->token    = t;
  new->position = *position;

  /* If required, try to start the Plug-In */

  if (start_now)
  {
    #ifdef TRACE
      if (tl & (1u<<30)) Printf("plugin_add_queue_item: Exitting through plugin_broadcast_plugin_open\n");
    #endif

    return plugin_broadcast_plugin_open();
  }

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

  return NULL;
}

/*************************************************/
/* plugin_remove_item()                          */
/*                                               */
/* Remove an item from the Plug-In queue.        */
/*                                               */
/* Parameters: Pointer to the plugin_queue       */
/*             struct to remove.                 */
/*************************************************/

_kernel_oserror * plugin_remove_item(plugin_queue * remove)
{
  plugin_queue * prev;

  #ifdef TRACE
    if (tl & (1u<<30)) Printf("plugin_remove_item: Called for %p\n",remove);
  #endif

  /* Can't do anything if there are no items */

  if (!plugin_queue_head || !remove)
  {
    #ifdef TRACE

      erb.errnum  = Utils_Error_Custom_Normal;
      sprintf(erb.errmess,
              "NULL queue head (0x%08x) or remove item (0x%08x)in plugin_remove_item",
              (int) plugin_queue_head,
              (int) remove);
      return &erb;

    #endif

    return NULL;
  }

  /* Find the previous item */

  prev = plugin_queue_head;

  while (prev && prev->next != remove) prev = prev->next;

  /* If we've not found the item we were asked to remove, and */
  /* the remove item isn't at the queue head, 'remove' must   */
  /* be invalid - so exit.                                    */

  if (!prev && remove != plugin_queue_head)
  {
    #ifdef TRACE

      erb.errnum  = Utils_Error_Custom_Normal;
      sprintf(erb.errmess,
              "Can't find item that points to given structure %0x08x in plugin_remove_item",
              (int) remove);
      return &erb;

    #endif

    return NULL;
  }

  /* OK, unlink it */

  if (prev) prev->next   = remove->next;
  else plugin_queue_head = remove->next;

  /* Free the item - finished. */

  free(remove);

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

  return NULL;
}

/*************************************************/
/* plugin_remove_head_item()                     */
/*                                               */
/* Remove the head item from the Plug-In queue,  */
/* launching the next item if there is one.      */
/*************************************************/

static _kernel_oserror * plugin_remove_head_item(void)
{
  _kernel_oserror * e;

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

  RetError(plugin_remove_item(plugin_queue_head));

  if (plugin_queue_head)
  {
    #ifdef TRACE
      if (tl & (1u<<30)) Printf("plugin_remove_head_item: Exitting through plugin_broadcast_plugin_open\n");
    #endif

    return plugin_broadcast_plugin_open();
  }
  else
  {
    #ifdef TRACE
      if (tl & (1u<<30)) Printf("plugin_remove_head_item: Successful\n");
    #endif

    return NULL;
  }
}

/*************************************************/
/* plugin_flush_queue()                          */
/*                                               */
/* Remove selected, or all Plug-Ins in the       */
/* queue.                                        */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the Plug-Ins to       */
/*             remove, or NULL for all items;    */
/*                                               */
/*             If one of the removed items was   */
/*             at the head of the queue, 1 to    */
/*             launch the new head item Plug-In, */
/*             else 0.                           */
/*************************************************/

_kernel_oserror * plugin_flush_queue(browser_data * b, int start_now)
{
  _kernel_oserror * e;
  plugin_queue    * old_head = plugin_queue_head;
  plugin_queue    * current;
  plugin_queue    * next;

  #ifdef TRACE
    if (tl & (1u<<30)) Printf("plugin_flush_queue: Called for %p\n",b);
  #endif

  current = plugin_queue_head;

  /* For each item, remove it only if it belongs to the given */
  /* browser, or the given browser is NULL.                   */

  while (current)
  {
    next = current->next;

    if (!b || (b && next->browser == b)) RetError(plugin_remove_item(current));

    current = next;
  }

  /* If there are still queue items, the head item has changed and */
  /* the start_now parameter passed in says to do so, launch the   */
  /* new head item Plug-In.                                        */

  if (
       plugin_queue_head             &&
       plugin_queue_head != old_head &&
       start_now
     )
  {
    #ifdef TRACE
      if (tl & (1u<<30)) Printf("plugin_flush_queue: Exitting through plugin_broadcast_plugin_open\n");
    #endif

    return plugin_broadcast_plugin_open();
  }
  else
  {
    #ifdef TRACE
      if (tl & (1u<<30)) Printf("plugin_flush_queue: Successful\n");
    #endif

    return NULL;
  }
}

/*************************************************/
/* plugin_shutdown()                             */
/*                                               */
/* Closes down all activities, flushing the      */
/* Plug-In queue and freeing any RMA associated  */
/* with string_value fields for in-transit       */
/* messages.                                     */
/*************************************************/

void plugin_shutdown(void)
{
  /* In transit Message_PlugIn_Open broadcasts */

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

  if (plugin_open_filename)
  {
    rma_release(NULL, plugin_open_filename);
    plugin_open_filename = NULL;
  }

  /* Clear other message fields */

  plugin_open_reference  = 0;
  plugin_open_tried_once = 0;
  plugin_open_token      = NULL;

  /* (For now that's all we have to do for messages). */
  /*                                                  */
  /* Flush the queue and exit.                        */

  plugin_flush_queue(NULL, 0);

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

  return;
}

/*************************************************/
/* plugin_setup_stream()                         */
/*                                               */
/* Sets up a plugin_stream structure in a new    */
/* browser so it may fetch data for a plugin     */
/* owned by another browser.                     */
/*                                               */
/* The all fields are filled in. The browser     */
/* and token details are, and the URL field is   */
/* filled in. The Mime type is also filled in    */
/* on the basis of a filename extension from the */
/* URL, if possible.                             */
/*                                               */
/* All string_value values are allocated in the  */
/* RMA. It is therefore essential to ensure that */
/* these are always completely freed.            */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             for the browser owning the        */
/*             Plug-In;                          */
/*                                               */
/*             Pointer to the HStream struct     */
/*             representing that Plug-In;        */
/*                                               */
/*             Pointer to a browser_data struct  */
/*             for the browser that will fetch   */
/*             the data;                         */
/*                                               */
/*             URL to fetch.                     */
/*************************************************/

_kernel_oserror * plugin_setup_stream(browser_data * owner, HStream * object, browser_data * fetcher, const char * url)
{
  _kernel_oserror * e;
  plugin_stream   * stream;

  /* Allocate memory for the item */

  stream = malloc(sizeof(plugin_stream));
  if (!stream) return make_no_cont_memory_error(11);

  /* Now fill it in */

  fetcher->pstream = stream;

  stream->browser_instance_handle = (int) owner;
  stream->token                   = object;

//  /* Get RMA space for the URL */

  e = NULL;
  return e;
}