netsurf/riscos/plugin.c
James Bursa 11bc5345c5 [project @ 2005-04-13 21:58:28 by bursa]
Add fallback field to struct box for object fallback content. Add some checks for tree consistency to box_dump(). Rename struct plugin_params to object_param. Clean up box_object(), box_embed(), box_iframe(), and box_image(). Implement object fallback to contents if the fetch or conversion fails.

svn path=/import/netsurf/; revision=1627
2005-04-13 21:58:28 +00:00

1390 lines
34 KiB
C

/*
* This file is part of NetSurf, http://netsurf.sourceforge.net/
* Licensed under the GNU General Public License,
* http://www.opensource.org/licenses/gpl-license
* Copyright 2003,4 John M Bell <jmb202@ecs.soton.ac.uk>
*/
/** \file
* Acorn Plugin protocol (implementation)
*
* This file implements the Acorn plugin protocol.
* See http://www.ecs.soton.ac.uk/~jmb202/riscos/acorn/funcspec.html
* for more details.
*
* The are still a number of outstanding issues:
*
* Stream Protocol:
* Fetching data, then streaming it to a plugin is not supported
* Streaming data from a plugin is not supported
*
* Messages:
* Most Plugin_Opening flags not supported
* No support for Plugin_Focus, Plugin_Busy, Plugin_Action
* No support for Plugin_Abort, Plugin_Inform, Plugin_Informed
* Plugin_URL_Access is only part-implemented
*/
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "oslib/mimemap.h"
#include "oslib/os.h"
#include "oslib/osfile.h"
#include "oslib/osfind.h"
#include "oslib/osgbpb.h"
#include "oslib/plugin.h"
#include "oslib/wimp.h"
#include "netsurf/utils/config.h"
#include "netsurf/content/content.h"
#include "netsurf/desktop/browser.h"
#include "netsurf/desktop/gui.h"
#include "netsurf/render/html.h"
#include "netsurf/render/box.h"
#include "netsurf/riscos/gui.h"
#include "netsurf/riscos/options.h"
#include "netsurf/riscos/plugin.h"
#include "netsurf/riscos/theme.h"
#include "netsurf/utils/log.h"
#include "netsurf/utils/messages.h"
#include "netsurf/utils/utils.h"
#ifdef WITH_PLUGIN
static const char * const ALIAS_PREFIX = "Alias$@PlugInType_";
typedef enum {
PLUGIN_PARAMETER_DATA = 1,
PLUGIN_PARAMETER_URL = 2,
PLUGIN_PARAMETER_OBJECT = 3,
PLUGIN_PARAMETER_SPECIAL = 4
} plugin_parameter_type;
struct plugin_param_item {
plugin_parameter_type type;
int rsize;
int nsize;
char *name;
int npad;
int vsize;
char *value;
int vpad;
int msize;
char *mime_type;
int mpad;
struct plugin_param_item *next;
};
static bool plugin_create_sysvar(const char *mime_type, char* sysvar);
static void plugin_create_stream(struct content *c);
static void plugin_write_stream(struct content *c, unsigned int consumed);
static void plugin_stream_write_callback(void *p);
static void plugin_write_stream_as_file(struct content *c);
static void plugin_destroy_stream(struct content *c);
static bool plugin_write_parameters_file(struct content *c,
struct object_params *params, const char *base);
static int plugin_calculate_rsize(const char* name, const char* data,
const char* mime);
static bool plugin_add_item_to_pilist(struct plugin_param_item **pilist,
plugin_parameter_type type, const char* name,
const char* value, const char* mime_type);
static char *plugin_get_string_value(os_string_value string, char *msg);
static bool plugin_active(struct content *c);
/**
* Initialises plugin system in readiness for receiving object data
*
* \param c The content to hold the data
* \param params Parameters associated with the content
* \return true on success, false otherwise
*/
bool plugin_create(struct content *c, const char *params[])
{
LOG(("plugin_create"));
c->data.plugin.bw = 0;
c->data.plugin.page = 0;
c->data.plugin.box = 0;
c->data.plugin.taskname = 0;
c->data.plugin.filename = 0;
c->data.plugin.datafile = 0;
c->data.plugin.opened = false;
c->data.plugin.repeated = 0;
c->data.plugin.browser = 0;
c->data.plugin.plugin = 0;
c->data.plugin.browser_stream = 0;
c->data.plugin.plugin_stream = 0;
c->data.plugin.plugin_task = 0;
c->data.plugin.consumed = 0;
c->data.plugin.reformat_pending = false;
c->data.plugin.width = 0;
c->data.plugin.height = 0;
c->data.plugin.file_stream_waiting = false;
return true;
}
/**
* Convert a plugin ready for display (does nothing)
*
* \param c The content to convert
* \param width Width of available space
* \param height Height of available space
* \return true on success, false otherwise
*/
bool plugin_convert(struct content *c, int width, int height)
{
LOG(("plugin_convert"));
c->width = width;
c->height = height;
/* deal with a plugin waiting for a file stream */
if (c->data.plugin.file_stream_waiting) {
c->data.plugin.file_stream_waiting = false;
plugin_write_stream_as_file(c);
}
c->status = CONTENT_STATUS_DONE;
return true;
}
/**
* Destroy a plugin content
*
* \param c The content to destroy
*/
void plugin_destroy(struct content *c)
{
LOG(("plugin_destroy"));
if (c->data.plugin.taskname)
free(c->data.plugin.taskname);
if (c->data.plugin.filename)
free(c->data.plugin.filename);
if (c->data.plugin.datafile)
free(c->data.plugin.datafile);
}
/**
* Redraw a content
*
* \param c The content to redraw
* \param x Left of content box
* \param y Top of content box
* \param width Width of content box
* \param height Height of content box
* \param clip[xy][01] Clipping rectangle
* \param scale Scale of page (1.0 = 100%)
*/
bool plugin_redraw(struct content *c, int x, int y,
int width, int height,
int clip_x0, int clip_y0, int clip_x1, int clip_y1,
float scale, unsigned long background_colour)
{
/* do nothing */
LOG(("plugin_redraw"));
return true;
}
/**
* Handle a window containing a CONTENT_PLUGIN being opened
*
* \param c The content to open
* \param bw The window to add the content to
* \param page The containing content
* \param box The containing box
* \param params Any parameters associated with the content
* \param state State data pointer
*/
void plugin_open(struct content *c, struct browser_window *bw,
struct content *page, struct box *box,
struct object_params *params)
{
bool standalone = false;
const char *base;
char sysvar[25];
char *varval;
plugin_full_message_open pmo;
wimp_window_state state;
os_error *error;
if (option_no_plugins)
return;
if (!params) {
/* this is a standalone plugin, so fudge the parameters */
params = calloc(1, sizeof(struct object_params));
if (!params) {
warn_user("NoMemory", 0);
goto error;
}
params->data = strdup(c->url);
if (!params->data) {
warn_user("NoMemory", 0);
goto error;
}
params->type = strdup(c->mime_type);
if (!params->type) {
warn_user("NoMemory", 0);
goto error;
}
standalone = true;
}
/* we only do this here cos the box is needed by
* write_parameters_file. Ideally it would be at the
* end of this function with the other writes to c->data.plugin
*/
c->data.plugin.box = box;
if (params->codebase)
base = params->codebase;
else if (page)
base = page->data.html.base_url;
else
base = c->url;
LOG(("writing parameters file"));
if (!plugin_write_parameters_file(c, params, base))
goto error;
LOG(("creating sysvar"));
/* get contents of Alias$@PlugInType_xxx variable */
if (!plugin_create_sysvar(c->mime_type, sysvar))
goto error;
LOG(("getenv"));
varval = getenv(sysvar);
LOG(("%s: '%s'", sysvar, varval));
if(!varval) {
goto error;
}
/* The browser instance handle is the content struct pointer */
c->data.plugin.browser = (unsigned int)c;
pmo.size = 60;
pmo.your_ref = 0;
pmo.action = message_PLUG_IN_OPEN;
/* open as helper, if standalone */
pmo.flags = /*standalone ? plugin_OPEN_AS_HELPER :*/ 0;
pmo.reserved = 0;
pmo.browser = (plugin_b)c->data.plugin.browser;
pmo.parent_window = bw->window->window;
/* initial position/dimensions */
if (standalone) {
/* if standalone, try to fill the browser window */
state.w = bw->window->window;
error = xwimp_get_window_state(&state);
if (error)
goto error;
pmo.bbox.x0 = 10;
/* avoid toolbar */
pmo.bbox.y1 = -10 - (bw->window->toolbar ?
bw->window->toolbar->height : 0);
pmo.bbox.x1 = (state.visible.x1 - state.visible.x0) - 10;
pmo.bbox.y0 = (state.visible.y0 - state.visible.y1) - 10;
}
else {
pmo.bbox.x0 = -100;
pmo.bbox.x1 = pmo.bbox.y0 = 0;
pmo.bbox.y1 = 100;
}
error = xmimemaptranslate_mime_type_to_filetype(c->mime_type,
&pmo.file_type);
if (error) {
goto error;
}
pmo.filename.pointer = c->data.plugin.filename;
c->data.plugin.repeated = 0;
LOG(("sending message"));
error = xwimp_send_message(wimp_USER_MESSAGE_RECORDED,
(wimp_message *)&pmo, wimp_BROADCAST);
if (error) {
LOG(("xwimp_send_message: 0x%x: %s",
error->errnum, error->errmess));
goto error;
}
c->data.plugin.bw = bw;
c->data.plugin.page = page;
c->data.plugin.taskname = strdup(varval);
error:
/* clean up standalone stuff */
if (standalone) {
free(params->type);
free(params->data);
free(params);
}
LOG(("done"));
}
/**
* Handle a window containing a CONTENT_PLUGIN being closed.
*
* \param c The content to close
*/
void plugin_close(struct content *c)
{
plugin_full_message_close pmc;
os_error *error;
LOG(("plugin_close"));
if (!plugin_active(c) || !c->data.plugin.opened)
return;
pmc.size = 32;
pmc.your_ref = 0;
pmc.action = message_PLUG_IN_CLOSE;
pmc.flags = 0;
pmc.browser = (plugin_b)c->data.plugin.browser;
pmc.plugin = (plugin_p)c->data.plugin.plugin;
LOG(("sending message"));
error = xwimp_send_message(wimp_USER_MESSAGE_RECORDED,
(wimp_message *)&pmc, (wimp_t)c->data.plugin.plugin_task);
if (error) {
return;
}
/* delete the data file used to send the data to the plugin */
if (c->data.plugin.datafile != 0)
xosfile_delete(c->data.plugin.datafile, 0, 0, 0, 0, 0);
}
/**
* Reformat a plugin content on a page
*
* \param c The content to reformat
* \param width New width
* \param height New height
*/
void plugin_reformat(struct content *c, int width, int height)
{
plugin_full_message_reshape pmr;
int x, y;
os_error *error;
LOG(("plugin_reformat"));
if (!plugin_active(c))
return;
/* if the plugin hasn't yet been opened, queue the reformat */
if (!c->data.plugin.opened) {
LOG(("queuing"));
c->data.plugin.reformat_pending = true;
c->data.plugin.width = width;
c->data.plugin.height = height;
return;
}
c->data.plugin.reformat_pending = false;
/* top left of plugin area, relative to top left of browser window */
if (c->data.plugin.box) {
box_coords(c->data.plugin.box, &x, &y);
}
else {
/* standalone */
x = 10 / 2;
/* avoid toolbar */
y = (10 + (c->data.plugin.bw->window->toolbar ?
c->data.plugin.bw->window->toolbar->height : 0)) / 2;
}
pmr.size = 52;
pmr.your_ref = 0;
pmr.action = message_PLUG_IN_RESHAPE;
pmr.flags = 0;
pmr.plugin = (plugin_p)c->data.plugin.plugin;
pmr.browser = (plugin_b)c->data.plugin.browser;
pmr.parent_window = c->data.plugin.bw->window->window;
pmr.bbox.x0 = x * 2;
pmr.bbox.y1 = -y * 2;
if (c->data.plugin.box) {
pmr.bbox.x1 = pmr.bbox.x0 + c->data.plugin.box->width * 2;
pmr.bbox.y0 = pmr.bbox.y1 - c->data.plugin.box->height * 2;
}
else {
/* standalone */
pmr.bbox.x1 = pmr.bbox.x0 + width * 2;
pmr.bbox.y0 = pmr.bbox.y1 - height * 2;
}
LOG(("sending message"));
error = xwimp_send_message(wimp_USER_MESSAGE, (wimp_message *) &pmr,
(wimp_t)c->data.plugin.plugin_task);
if (error) {
return;
}
}
/**
* Creates a system variable from the mimetype
*
* \param mime_type The mime type
* \param sysvar Pointer to buffer into which the string should be written
* \return true on success, false otherwise.
*/
bool plugin_create_sysvar(const char *mime_type, char* sysvar)
{
unsigned int *fv;
os_error *e;
e = xmimemaptranslate_mime_type_to_filetype(mime_type, (bits *) &fv);
if (e) {
return false;
}
sprintf(sysvar, "%s%03x", ALIAS_PREFIX, (unsigned int)fv);
return true;
}
/**
* Determines whether a content is handleable by a plugin
*
* \param mime_type The mime type of the content
* \return true if the content is handleable, false otherwise
*/
bool plugin_handleable(const char *mime_type)
{
char sysvar[25];
if (plugin_create_sysvar(mime_type, sysvar)) {
if (getenv(sysvar) != 0) {
return true;
}
}
return false;
}
/**
* Handle a bounced plugin_open message
*
* \param message The message to handle
*/
void plugin_open_msg(wimp_message *message)
{
struct content *c;
os_error *error;
plugin_message_open *pmo = (plugin_message_open *)&message->data;
/* retrieve our content */
c = (struct content *)pmo->browser;
/* check we expect this message */
if (!c || !plugin_active(c))
return;
LOG(("bounced"));
/* bail if we've already tried twice */
if (c->data.plugin.repeated >= 1)
return;
/* start plugin app */
error = xwimp_start_task((char const*)c->data.plugin.taskname, 0);
if (error) {
return;
}
/* indicate we've already sent this message once */
c->data.plugin.repeated++;
/* and resend the message */
LOG(("resending"));
message->your_ref = 0;
error = xwimp_send_message(wimp_USER_MESSAGE_RECORDED, message,
wimp_BROADCAST);
if (error) {
return;
}
}
/**
* Handle a plugin_opening message
*
* \param message The message to handle
*/
void plugin_opening(wimp_message *message)
{
struct content *c;
plugin_message_opening *pmo =
(plugin_message_opening *)&message->data;
/* retrieve our content */
c = (struct content *)pmo->browser;
/* check we expect this message */
if (!c || !plugin_active(c))
return;
c->data.plugin.repeated = 2; /* make sure open_msg does nothing */
c->data.plugin.plugin = (unsigned int)pmo->plugin;
c->data.plugin.plugin_task = (unsigned int)message->sender;
c->data.plugin.opened = true;
LOG(("opening"));
/* if there's a reformat pending, do so now */
if (c->data.plugin.reformat_pending) {
LOG(("do pending reformat"));
plugin_reformat(c, c->data.plugin.width,
c->data.plugin.height);
}
if (pmo->flags & 0x04) { /* plugin wants the data fetching */
LOG(("wants stream"));
plugin_create_stream(c);
}
if (!(pmo->flags & 0x08)) {
LOG(("we delete file"));
/* we don't care if this fails */
xosfile_delete(c->data.plugin.filename, 0, 0, 0, 0, 0);
}
}
/**
* Handle a bounced plugin_close message
*
* \param message The message to handle
*/
void plugin_close_msg(wimp_message *message)
{
plugin_message_close *pmc = (plugin_message_close *)&message->data;
/* not necessarily true - some plugins don't stop this bouncing */
LOG(("failed to close plugin: %p", pmc->plugin));
}
/**
* Handle a plugin_closed message
*
* \param message The message to handle
*/
void plugin_closed(wimp_message *message)
{
struct content *c;
plugin_message_closed *pmc = (plugin_message_closed *)&message->data;
/* retrieve our content */
c = (struct content*)pmc->browser;
/* check we expect this message */
if (!c || !plugin_active(c))
return;
LOG(("died"));
c->data.plugin.opened = false;
if (pmc->flags & 0x4) {
LOG(("plugin_closed: 0x%x: %s", pmc->error_number,
pmc->error_text));
/* not really important enough to do a warn_user */
gui_window_set_status(c->data.plugin.bw->window,
pmc->error_text);
}
}
/**
* Handles receipt of plugin_reshape_request messages
*
* \param message The message to handle
*/
void plugin_reshape_request(wimp_message *message)
{
struct content *c;
struct box *b;
union content_msg_data data;
plugin_message_reshape_request *pmrr = (plugin_message_reshape_request*)&message->data;
/* retrieve our content */
c = (struct content *)pmrr->browser;
/* check we expect this message */
if (!c || !plugin_active(c))
return;
LOG(("handling reshape request"));
/* we can be called prior to the box content being set up,
* so we set it up here. This is ok as the content won't change
* under us. However, the box may not exist (if we're standalone)
*/
if (c->data.plugin.box)
c->data.plugin.box->object = c;
/* should probably shift by x and y eig values here */
c->width = pmrr->size.x / 2;
c->height = pmrr->size.y / 2;
if (c->data.plugin.box)
/* invalidate parent box widths */
for (b = c->data.plugin.box->parent; b; b = b->parent)
b->max_width = UNKNOWN_MAX_WIDTH;
if (c->data.plugin.page)
/* force a reformat of the parent */
content_reformat(c->data.plugin.page,
c->data.plugin.page->available_width, 0);
/* redraw the window */
content_broadcast(c->data.plugin.bw->current_content,
CONTENT_MSG_REFORMAT, data);
/* reshape the plugin */
plugin_reformat(c, c->width, c->height);
}
/**
* Handles receipt of plugin_status messages
*
* \param message The message to handle
*/
void plugin_status(wimp_message *message)
{
struct content *c;
plugin_message_status *pms = (plugin_message_status*)&message->data;
/* retrieve our content */
c = (struct content *)pms->browser;
/* check we expect this message */
if (!c || !plugin_active(c))
return;
gui_window_set_status(c->data.plugin.bw->window,
(const char*)plugin_get_string_value(pms->message,
(char*)pms));
}
/**
* Handles receipt of plugin_stream_new messages
*
* \param message The message to handle
*/
void plugin_stream_new(wimp_message *message)
{
struct content *c;
int stream_type;
plugin_message_stream_new *pmsn =
(plugin_message_stream_new*)&message->data;
LOG(("plugin_stream_new"));
/* retrieve our content */
c = (struct content *)pmsn->browser;
/* check we expect this message */
if (!c || !plugin_active(c))
return;
/* response to a message we sent */
if (message->my_ref != 0) {
c->data.plugin.browser_stream = (unsigned int)pmsn->browser;
c->data.plugin.plugin_stream = (unsigned int)pmsn->stream;
LOG(("flags: %x", pmsn->flags));
stream_type = pmsn->flags & 0xF; /* bottom four bits */
if (stream_type == 3) {
LOG(("as file"));
/* received all data => go ahead and stream
* we have to check the content's status too, as
* we could be dealing with a stream of unknown
* length (ie c->total_size == 0). If the status
* is CONTENT_STATUS_DONE, we've received all the
* data anyway, regardless of the total size.
*/
if (c->source_size == c->total_size ||
c->status == CONTENT_STATUS_DONE)
plugin_write_stream_as_file(c);
else {
LOG(("waiting for data"));
c->data.plugin.file_stream_waiting = true;
}
}
else if (stream_type < 3) {
LOG(("write stream"));
plugin_write_stream(c, 0);
}
}
/* new stream, initiated by plugin */
else {
/** \todo plugin-initiated streams */
}
}
/**
* Handles receipt of plugin_stream_written messages
*
* \param message The message to handle
*/
void plugin_stream_written(wimp_message *message)
{
struct content *c;
plugin_message_stream_written *pmsw =
(plugin_message_stream_written*)&message->data;
/* retrieve our box */
c = (struct content *)pmsw->browser;
/* check we expect this message */
if (!c || !plugin_active(c))
return;
LOG(("got written"));
plugin_write_stream(c, pmsw->length);
}
/**
* Handles plugin_url_access messages
*
* \param message The message to handle
*/
void plugin_url_access(wimp_message *message)
{
struct content *c;
plugin_full_message_notify pmn;
os_error *error;
plugin_message_url_access *pmua =
(plugin_message_url_access*)&message->data;
bool notify = false, post = false, file = false;
char *url = plugin_get_string_value(pmua->url, (char*)pmua);
char *window;
if (pmua->flags & 0x01) notify = true;
if (pmua->flags & 0x02) post = true;
if (pmua->flags & 0x04) file = true;
/* retrieve our content */
c = (struct content *)pmua->browser;
/* check we expect this message */
if (!c || !plugin_active(c))
return;
/* fetch url to window */
if (pmua->target_window.offset != 0 &&
pmua->target_window.pointer != 0) {
window = plugin_get_string_value(pmua->target_window,
(char*)pmua);
LOG(("flags: %d, url: %s, window: %s", pmua->flags, url, window));
/** \todo proper _parent and _self support (needs frames)
* other window names
*/
if (!post) { /* GET request */
if (strcasecmp(url,
c->data.plugin.bw->current_content->url) &&
(strcasecmp(window, "_self") == 0 ||
strcasecmp(window, "_parent") == 0 ||
strcasecmp(window, "_top") == 0 ||
strcasecmp(window, "") == 0)) {
/* only open in current window if not
* already at the URL requested, else you
* end up in an infinite loop of fetching
* the same page
*/
browser_window_go(c->data.plugin.bw, url, 0);
}
else if (!option_block_popups &&
strcasecmp(window, "_blank") == 0) {
/* don't do this if popups are blocked */
browser_window_create(url, NULL, 0);
}
}
else { /* POST request */
/* fetch URL */
}
}
/* fetch data and stream to plugin */
else {
if (!post) { /* GET request */
/* fetch URL */
}
else { /* POST request */
/* fetch URL */
}
/* stream data to plugin */
}
if (notify) {
/* send message_plugin_notify to plugin task */
pmn.size = 44;
pmn.your_ref = message->my_ref;
pmn.action = message_PLUG_IN_NOTIFY;
pmn.flags = 0;
pmn.plugin = pmua->plugin;
pmn.browser = pmua->browser;
pmn.url.pointer = url;
pmn.reason = (plugin_notify_reason)0;
pmn.notify_data = pmua->notify_data;
error = xwimp_send_message(wimp_USER_MESSAGE,
(wimp_message*)&pmn, message->sender);
if (error) {
return;
}
}
}
/**
* Creates a plugin stream
*
* \param c The content to fetch the data for
*/
void plugin_create_stream(struct content *c)
{
plugin_full_message_stream_new pmsn;
os_error *error;
pmsn.size = 64;
pmsn.your_ref = 0;
pmsn.action = message_PLUG_IN_STREAM_NEW;
pmsn.flags = 0;
pmsn.plugin = (plugin_p)c->data.plugin.plugin;
pmsn.browser = (plugin_b)c->data.plugin.browser;
pmsn.stream = (plugin_s)0;
pmsn.browser_stream = (plugin_bs)c->data.plugin.browser;
pmsn.url.pointer = c->url;
pmsn.end = c->total_size;
pmsn.last_modified_date = 0;
pmsn.notify_data = 0;
pmsn.mime_type.pointer = c->mime_type;
pmsn.target_window.offset = 0;
LOG(("Sending message &4D548"));
error = xwimp_send_message(wimp_USER_MESSAGE_RECORDED,
(wimp_message*)&pmsn, (wimp_t)c->data.plugin.plugin_task);
if (error) {
return;
}
}
/**
* Writes to an open stream
*
* \param c The content to write data to
* \param consumed The amount of data consumed
*/
void plugin_write_stream(struct content *c, unsigned int consumed)
{
plugin_full_message_stream_write pmsw;
os_error *error;
c->data.plugin.consumed += consumed;
pmsw.size = 68;
pmsw.your_ref = 0;
pmsw.action = message_PLUG_IN_STREAM_WRITE;
pmsw.flags = 0;
pmsw.plugin = (plugin_p)c->data.plugin.plugin;
pmsw.browser = (plugin_b)c->data.plugin.browser;
pmsw.stream = (plugin_s)c->data.plugin.plugin_stream;
pmsw.browser_stream = (plugin_bs)c->data.plugin.browser_stream;
pmsw.url.pointer = c->url;
/* end of stream is total_size
* (which is conveniently 0 if unknown)
*/
pmsw.end = c->total_size;
pmsw.last_modified_date = 0;
pmsw.notify_data = 0;
/* offset into data is amount of data consumed by plugin already */
pmsw.offset = c->data.plugin.consumed;
/* length of data available */
pmsw.length = c->source_size - c->data.plugin.consumed;
/* pointer to available data */
/**\todo This pointer can change underneath us (ie between us
* sending the message and the recipient handling it)
*/
pmsw.data = (byte*)(c->source_data + c->data.plugin.consumed);
/* still have data to send */
if (c->data.plugin.consumed < c->source_size) {
LOG(("Sending message &4D54A"));
error = xwimp_send_message(wimp_USER_MESSAGE_RECORDED,
(wimp_message *)&pmsw,
(wimp_t)c->data.plugin.plugin_task);
if (error) {
return;
}
}
else if (c->source_size < c->total_size) {
/* the plugin has consumed all the available data,
* but there's still more to fetch, so we wait for
* 20 cs then try again (note that streams of unknown
* total length won't ever get in here as c->total_size
* will be 0)
*/
schedule(20, plugin_stream_write_callback, c);
}
/* no further data => destroy stream */
else {
plugin_destroy_stream(c);
}
}
/**
* Stream write callback - used to wait for data to download
*
* \param p The appropriate content struct
*/
void plugin_stream_write_callback(void *p)
{
struct content *c = (struct content *)p;
/* remove ourselves from the schedule queue */
schedule_remove(plugin_stream_write_callback, p);
/* continue writing stream */
plugin_write_stream(c, 0);
}
/**
* Writes a stream as a file
*
* \param c The content to write data to
*/
void plugin_write_stream_as_file(struct content *c)
{
plugin_full_message_stream_as_file pmsaf;
unsigned int filetype;
os_error *error;
c->data.plugin.datafile =
calloc(strlen(getenv("Wimp$ScrapDir"))+13+10, sizeof(char));
if (!c->data.plugin.datafile) {
LOG(("malloc failed"));
warn_user("NoMemory", 0);
plugin_destroy_stream(c);
return;
}
/* create filename */
if (c->data.plugin.box) {
sprintf(c->data.plugin.datafile, "%s.WWW.NetSurf.d%x",
getenv("Wimp$ScrapDir"),
(unsigned int)c->data.plugin.box->object_params);
}
else {
sprintf(c->data.plugin.datafile, "%s.WWW.NetSurf.d%x",
getenv("Wimp$ScrapDir"),
(unsigned int)c);
}
pmsaf.size = 60;
pmsaf.your_ref = 0;
pmsaf.action = message_PLUG_IN_STREAM_AS_FILE;
pmsaf.flags = 0;
pmsaf.plugin = (plugin_p)c->data.plugin.plugin;
pmsaf.browser = (plugin_b)c->data.plugin.browser;
pmsaf.stream = (plugin_s)c->data.plugin.plugin_stream;
pmsaf.browser_stream = (plugin_bs)c->data.plugin.browser_stream;
pmsaf.url.pointer = c->url;
pmsaf.end = 0;
pmsaf.last_modified_date = 0;
pmsaf.notify_data = 0;
pmsaf.filename.pointer = c->data.plugin.datafile;
error = xmimemaptranslate_mime_type_to_filetype(c->mime_type,
(bits *) &filetype);
if (error) {
return;
}
error = xosfile_save_stamped((char const*)c->data.plugin.datafile,
filetype, c->source_data,
c->source_data + c->source_size);
if (error) {
return;
}
LOG(("Sending message &4D54C"));
error = xwimp_send_message(wimp_USER_MESSAGE,
(wimp_message *)&pmsaf, (wimp_t)c->data.plugin.plugin_task);
if (error) {
return;
}
}
/**
* Destroys a plugin stream
*
* \param c The content to destroy
*/
void plugin_destroy_stream(struct content *c)
{
plugin_full_message_stream_destroy pmsd;
os_error *error;
/* reset stream */
c->data.plugin.consumed = 0;
pmsd.size = 60;
pmsd.your_ref = 0;
pmsd.action = message_PLUG_IN_STREAM_DESTROY;
pmsd.flags = 0;
pmsd.plugin = (plugin_p)c->data.plugin.plugin;
pmsd.browser = (plugin_b)c->data.plugin.browser;
pmsd.stream = (plugin_s)c->data.plugin.plugin_stream;
pmsd.browser_stream = (plugin_bs)c->data.plugin.browser_stream;
pmsd.url.pointer = c->url;
pmsd.end = c->total_size;
pmsd.last_modified_date = 0;
pmsd.notify_data = 0;
pmsd.reason = plugin_STREAM_DESTROY_FINISHED;
LOG(("Sending message &4D549"));
error = xwimp_send_message(wimp_USER_MESSAGE,
(wimp_message *)&pmsd, (wimp_t)c->data.plugin.plugin_task);
if (error) {
return;
}
}
/**
* Writes the plugin parameters file
*
* \param c Content to write parameters for
* \param params Plugin parameters struct
* \param base base URL for object
* \return true on success, false otherwise
*/
bool plugin_write_parameters_file(struct content *c,
struct object_params *params, const char *base)
{
struct object_param *p;
struct plugin_param_item *ppi;
struct plugin_param_item *pilist = 0;
char bgcolor[10] = {0};
FILE *fp;
/* Create the file */
xosfile_create_dir("<Wimp$ScrapDir>.WWW", 77);
xosfile_create_dir("<Wimp$ScrapDir>.WWW.NetSurf", 77);
/* path + filename + terminating NUL */
c->data.plugin.filename =
calloc(strlen(getenv("Wimp$ScrapDir"))+13+10, sizeof(char));
if (!c->data.plugin.filename) {
LOG(("malloc failed"));
warn_user("NoMemory", 0);
return false;
}
sprintf(c->data.plugin.filename, "%s.WWW.NetSurf.p%x",
getenv("Wimp$ScrapDir"), (unsigned int)params);
LOG(("filename: %s", c->data.plugin.filename));
/* Write object attributes first */
/* classid is checked first */
if (params->classid != 0 && params->codetype != 0) {
if (!plugin_add_item_to_pilist(&pilist,
PLUGIN_PARAMETER_DATA, "CLASSID",
(const char*)params->classid,
(const char*)params->codetype))
goto error;
}
/* otherwise, we check the data attribute */
else if (params->data !=0 && params->type != 0) {
if (!plugin_add_item_to_pilist(&pilist,
PLUGIN_PARAMETER_DATA, "DATA",
(const char *)params->data,
(const char *)params->type))
goto error;
}
/* if codebase is specified, write it as well */
if (params->codebase != 0) {
if (!plugin_add_item_to_pilist(&pilist,
PLUGIN_PARAMETER_DATA, "CODEBASE",
(const char *)params->codebase,
NULL))
goto error;
}
/* Iterate through the parameter list, creating the parameters
* file as we go.
*/
for (p = params->params; p != 0; p = p->next) {
LOG(("name: %s", p->name == 0 ? "not set" : p->name));
LOG(("value: %s", p->value == 0 ? "not set" : p->value));
LOG(("type: %s", p->type == 0 ? "not set" : p->type));
LOG(("valuetype: %s", p->valuetype));
if (strcasecmp(p->valuetype, "data") == 0)
if (!plugin_add_item_to_pilist(&pilist,
PLUGIN_PARAMETER_DATA,
(const char *)p->name,
(const char *)p->value,
(const char *)p->type))
goto error;
if (strcasecmp(p->valuetype, "ref") == 0)
if (!plugin_add_item_to_pilist(&pilist,
PLUGIN_PARAMETER_URL,
(const char *)p->name,
(const char *)p->value,
(const char *)p->type))
goto error;
if (strcasecmp(p->valuetype, "object") == 0)
if (!plugin_add_item_to_pilist(&pilist,
PLUGIN_PARAMETER_OBJECT,
(const char *)p->name,
(const char *)p->value,
(const char *)p->type))
goto error;
}
/* Now write mandatory special parameters */
/* BASEHREF */
if (!plugin_add_item_to_pilist(&pilist, PLUGIN_PARAMETER_SPECIAL,
"BASEHREF", base,
NULL))
goto error;
/* USERAGENT */
if (!plugin_add_item_to_pilist(&pilist, PLUGIN_PARAMETER_SPECIAL,
"USERAGENT", "NetSurf", NULL))
goto error;
/* UAVERSION */
if (!plugin_add_item_to_pilist(&pilist, PLUGIN_PARAMETER_SPECIAL,
"UAVERSION", "0.01", NULL))
goto error;
/* APIVERSION */
if (!plugin_add_item_to_pilist(&pilist, PLUGIN_PARAMETER_SPECIAL,
"APIVERSION", "1.10", NULL))
goto error;
/* BGCOLOR */
if (c->data.plugin.box && c->data.plugin.box->style &&
c->data.plugin.box->style->background_color
<= 0xFFFFFF)
sprintf(bgcolor, "%X00",
(unsigned int)c->data.plugin.box->style->background_color);
else
sprintf(bgcolor, "FFFFFF");
if (!plugin_add_item_to_pilist(&pilist, PLUGIN_PARAMETER_SPECIAL,
"BGCOLOR",
(const char *)bgcolor,
NULL))
goto error;
/* Write file */
fp = fopen(c->data.plugin.filename, "wb+");
while (pilist != 0) {
fwrite(&pilist->type, (unsigned int)sizeof(int), 1, fp);
fwrite(&pilist->rsize, (unsigned int)sizeof(int), 1, fp);
fwrite(&pilist->nsize, (unsigned int)sizeof(int), 1, fp);
fwrite(pilist->name, (unsigned int)strlen(pilist->name), 1, fp);
for (; pilist->npad != 0; pilist->npad--)
fputc('\0', fp);
fwrite(&pilist->vsize, (unsigned int)sizeof(int), 1, fp);
fwrite(pilist->value, (unsigned int)strlen(pilist->value), 1, fp);
for(; pilist->vpad != 0; pilist->vpad--)
fputc('\0', fp);
fwrite(&pilist->msize, (unsigned int)sizeof(int), 1, fp);
if (pilist->msize > 0) {
fwrite(pilist->mime_type,
(unsigned int)strlen(pilist->mime_type), 1, fp);
for (; pilist->mpad != 0; pilist->mpad--)
fputc('\0', fp);
}
ppi = pilist;
pilist = pilist->next;
free(ppi->name);
free(ppi->value);
free(ppi->mime_type);
ppi->name = ppi->value = ppi->mime_type = 0;
free(ppi);
ppi = 0;
}
fwrite("\0", sizeof(char), 4, fp);
fclose(fp);
return true;
error:
while (pilist != 0) {
ppi = pilist;
pilist = pilist->next;
free(ppi->name);
free(ppi->value);
free(ppi->mime_type);
ppi->name = ppi->value = ppi->mime_type = 0;
free(ppi);
ppi = 0;
}
free(c->data.plugin.filename);
c->data.plugin.filename = 0;
return false;
}
/**
* Calculates the size of a parameter file record
*
* \param name Record name
* \param data Record data
* \param mime Record mime type
* \return length of record
*/
int plugin_calculate_rsize(const char* name, const char* data,
const char* mime)
{
int ret = 0;
ret += (4 + strlen(name) + 3) / 4 * 4; /* name */
ret += (4 + strlen(data) + 3) / 4 * 4; /* data */
if (mime != NULL)
ret += (4 + strlen(mime) + 3) / 4 * 4; /* mime type */
else
ret += 4;
return ret;
}
/**
* Adds an item to the list of parameter file records
*
* \param pilist Pointer to list of parameters
* \param type Type of record to add
* \param name Name of record
* \param value Value of record
* \param mime_type Mime type of record
* \return true on success, false otherwise
*/
bool plugin_add_item_to_pilist(struct plugin_param_item **pilist,
plugin_parameter_type type, const char* name,
const char* value, const char* mime_type)
{
struct plugin_param_item *ppi = calloc(1, sizeof(*ppi));
if (!ppi)
return false;
/* initialise struct */
ppi->type = 0;
ppi->rsize = 0;
ppi->nsize = 0;
ppi->name = 0;
ppi->npad = 0;
ppi->vsize = 0;
ppi->value = 0;
ppi->vpad = 0;
ppi->msize = 0;
ppi->mime_type = 0;
ppi->mpad = 0;
ppi->type = type;
ppi->rsize = plugin_calculate_rsize(name, value, mime_type);
ppi->nsize = strlen(name);
ppi->name = strdup(name);
if (!ppi->name) {
free(ppi);
return false;
}
ppi->npad = 4 - (ppi->nsize%4 == 0 ? 4 : ppi->nsize%4);
ppi->vsize = strlen(value);
ppi->value = strdup(value);
if (!ppi->value) {
free(ppi->name);
free(ppi);
return false;
}
ppi->vpad = 4 - (ppi->vsize%4 == 0 ? 4 : ppi->vsize%4);
if(mime_type != 0) {
ppi->msize = strlen(mime_type);
ppi->mime_type = strdup(mime_type);
if (!ppi->mime_type) {
free(ppi->name);
free(ppi->value);
free(ppi);
return false;
}
ppi->mpad = 4 - (ppi->msize%4 == 0 ? 4 : ppi->msize%4);
}
ppi->next = (*pilist);
(*pilist) = ppi;
return true;
}
/**
* Utility function to grab string data from plugin message blocks
*
* \param string Containing structure
* \param msg Containing message
* \return the string data
*/
char *plugin_get_string_value(os_string_value string, char *msg)
{
if(string.offset == 0 || string.offset > 256) {
return string.pointer;
}
return &msg[string.offset];
}
/**
* Determines whether a content is still active
*
* \param c The content to examine
* \return true if active, false otherwise
*/
bool plugin_active(struct content *c)
{
struct content *d;
if (c->user_list == 0)
return false;
for (d = content_list; d; d = d->next) {
if (d == c)
return true;
}
return false;
}
#endif