/**
 * URL launching / receiving code
 *
 * Implements:
 *
 *  + the Acorn URI protocol, as defined in <http://www.acorn.com/browser/uri/funcspec.html>
 *  + the ANT URL protocol, as defined in <http://www.ant.co.uk/support/tech/notes/url.html>
 *
 * NB. Does not support sending URLs over 240ish character using the ANT protocol - this
 *     would require claiming RMA.
 *
 * (C) Nettle developers 2000-2003
 *
 * $Id: url,v 1.5 2003/10/16 19:36:40 archifishal Exp $
 */


#include "generic.h"
#include "globals.h"

#include "misc.h"
#include "url.h"
#include "wimp.h"
#include "wimputil.h"


#define Wimp_EUserMessage             17
#define Wimp_EUserMessageRecorded     18
#define Wimp_EUserMessageAcknowledge  19



/* steps in a url broadcast:
 * 1) Try ANT broadcast
 * 2) If that bounces, try Acorn URI dispatch [broadcast + load]
 * 3) If that succeeds, what for succes or failure wimp message
 * 4) If either fails, try ANT Url_open
 * 5) If that fails, give error message
 */



#define WORDALIGN(x) ( (x+3) & ~3 )



static void url_failed(void)
{
  generror(lookup_inbuffer("NoAppForURL"), false);
}



static int url_antbroadcast(const char *url)
{
  url_message urlblock;
  _kernel_oserror *error;
  int strlen_url=(strlen(url)>235) ? 235 : strlen(url);

  urlblock.hdr.size = WORDALIGN( sizeof(urlblock.hdr) + strlen_url + 1 );
  urlblock.hdr.your_ref = 0;
  urlblock.hdr.action_code = Wimp_MOpenUrl;

  *urlblock.data.url = 0;
  strncat(urlblock.data.url, url, sizeof(urlblock.data.url) - 1);
  error = _swix(Wimp_SendMessage, _INR(0,2), Wimp_EUserMessageRecorded,
                &urlblock, 0);
  if (error != NULL)
  {
    return -1;
  }

  return 0;
}



static int url_antload(const char *url)
{
  char buf[512];
  char *off;

  off = strchr(url, ':');

  /* our url request wasn't answered :-( */
  /* try an Alias$URLOpen, instead */
  if (off == NULL)
    return -1;  /* no ':' */

  strcpy(buf, "Alias$URLOpen_");
  strncat(buf, url, off - url);
  if (getenv(buf) == NULL)
    return -1;

  strcat(buf, " ");
  strncat(buf, url, sizeof(buf) - strlen(buf) - 1 );

  if (_swix(Wimp_StartTask, _IN(0), buf + sizeof("Alias$") - 1) != NULL)
    return -1;


  return 0;
}



void url_launch(const char *url)
{
  if (url_antbroadcast(url) == -1)
  {
    url_failed();
  }
}



static int url_acornlaunch(const char *url)
{
  int taskhan = 0;
  int success = 1;
  int flags;

  if (_swix( Wimp_ReadSysInfo, _IN(0) | _OUT(0), 5, &taskhan) != NULL)
    success = 0;

  if (success)
  {
    if (_swix(URI_Dispatch, _INR(0,2) | _OUT(0), URI_inform_caller, url, taskhan, &flags ) != NULL || flags & 1)
    {
      success = 0;
    }
  }

  if (!success)
    url_antload(url);

  return 0;
}



void url_bounce(user_message *mess)
{
  char              buf[512];
  _kernel_oserror  *error;

  if ( mess->message_code == URI_MReturnResult )
  {
    uri_returnresultmessage *msg = (uri_returnresultmessage *) mess;

    if ( (msg->flags & 0x01) == 0 )
      return; /* url was claimed */

    /* failed AcornURI. Try ANT launch */
    error = _swix(URI_RequestURI, _INR(0, 3), 0, buf, sizeof buf,
                  msg->uri_handle);
    if (error)
    {
      _swi(Wimp_ReportError, _INR(0,2), error, 0, application_name);
      return;
    }

    if (url_antload(buf) == -1)
      url_failed();

    /* we haven't acknowledged the returnresult message, so the URI
     * handler task will automatically free the URI */

    return;
  }
  else if ( mess->message_code != Wimp_MOpenUrl )
  {
    return;
  }

  /* otherwise, it was an ANT url broadcast that failed - try Acorn broadcast/launch */

  url_acornlaunch(mess->contents.bytes);
}



/* Called when wantACK wimp message with type Wimp_MOpenUrl or URI_MProcess is received.
 *
 * NB. Modifies your_ref & other fields in mess->hdr
 */
void url_recvbroadcast(user_message *mess, url_handler_func handler)
{
  url_message *msg = (url_message *) mess;
  char *url;

  if ( mess->message_code == URI_MProcess )
  {
    uri_processmessage *msg_process = (uri_processmessage *) mess;
    int checkonly = msg_process->flags & 0x01;

    if ( (*handler)(msg_process->uri, checkonly) )
    {
      /* url handled (or can be handled */
      mess->your_ref    = mess->my_ref;
      mess->message_code = URI_MProcessAck;
      _swi(Wimp_SendMessage, _INR(0,2), Wimp_EUserMessageAcknowledge, mess,
           mess->sender_ref);
    }
    return;
  }
  else if ( mess->message_code != Wimp_MOpenUrl )
    return;

  if (msg->data.indirect.tag == 0)
  {
    unsigned int offset = msg->data.indirect.url.offset;
    /* indirect message */
    if ( offset == 0 )
      return; /* NULL url */
    else if ( offset < 236 )
      /* offset inside message block */
      url = (char *)&msg->data + offset;
    else if ( offset >= 0x01800000 )
      /* pointer into shared memory */
      url = msg->data.indirect.url.ptr;
    else
      return; /* reserved value. */
  }
  else
  {
    /* url must be inside body of message */
    url = msg->data.url;
  }

  if ( (*handler)( url, 0 ) )
  {
    /* url handled, ack message */
    mess->your_ref = mess->my_ref;
    _swi(Wimp_SendMessage, _INR(0,2), Wimp_EUserMessageAcknowledge, mess,
         mess->sender_ref);
  }
}
