mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-18 10:13:24 +03:00
6c9e4bea8e
svn path=/trunk/netsurf/; revision=10300
1780 lines
44 KiB
C
1780 lines
44 KiB
C
/*
|
|
* Copyright 2003,4 John M Bell <jmb202@ecs.soton.ac.uk>
|
|
*
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
*
|
|
* NetSurf is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* NetSurf is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/** \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:
|
|
* 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 ignores POST requests.
|
|
*
|
|
* Helpers are not supported (system variable detection is #if 0ed out)
|
|
*/
|
|
|
|
#include "utils/config.h"
|
|
#ifdef WITH_PLUGIN
|
|
|
|
#include <assert.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 "utils/config.h"
|
|
#include "content/content.h"
|
|
#include "content/fetch.h"
|
|
#include "content/fetchcache.h"
|
|
#include "desktop/browser.h"
|
|
#include "desktop/gui.h"
|
|
#include "render/html.h"
|
|
#include "render/box.h"
|
|
#include "riscos/gui.h"
|
|
#include "riscos/options.h"
|
|
#include "riscos/plugin.h"
|
|
#include "riscos/theme.h"
|
|
#include "utils/log.h"
|
|
#include "utils/messages.h"
|
|
#include "utils/url.h"
|
|
#include "utils/utils.h"
|
|
|
|
|
|
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;
|
|
};
|
|
|
|
struct plugin_stream {
|
|
struct plugin_stream *next; /* next in list */
|
|
|
|
struct content *plugin; /* the plugin content */
|
|
struct content *c; /* the content being fetched for
|
|
* this stream (may be the same as
|
|
* plugin iff we've been asked to
|
|
* fetch the data resource for the
|
|
* plugin task) */
|
|
|
|
/* browser stream handle is the address of this struct in memory */
|
|
plugin_s pluginh; /* plugin stream handle */
|
|
|
|
/* We only support stream types 0 and 3 (Normal and As file only)
|
|
* Type 1 (Seek only) streams are treated as type 0.
|
|
* Type 2 (As file) streams are treated as type 3.
|
|
* Streams are never seekable.
|
|
*/
|
|
enum { NORMAL = plugin_STREAM_NEW_TYPE_NORMAL,
|
|
AS_FILE = plugin_STREAM_NEW_TYPE_AS_FILE_ONLY } type;
|
|
union {
|
|
struct {
|
|
char *datafile; /* filename of filestreamed file */
|
|
|
|
/* we need this flag as we should only send stream
|
|
* destroy once. This struct may still persist after
|
|
* the stream has ended in the case where it's a
|
|
* file only stream, as we've still got to destroy
|
|
* the temporary file. We can only do this when
|
|
* we're certain the plugin's no longer using it
|
|
* (i.e. after we've sent the plugin close message)
|
|
*/
|
|
bool destroyed; /* have we destroyed this stream? */
|
|
bool waiting; /* waiting for data to arrive */
|
|
} file;
|
|
struct {
|
|
unsigned int consumed; /* size of data consumed by plugin */
|
|
/* The following is nasty, but necessary to prevent
|
|
* a race condition between the plugin application
|
|
* handling the stream write message and our fetch
|
|
* code reallocing the data buffer (and potentially
|
|
* relocating it)
|
|
*/
|
|
#define PLUGIN_STREAM_BUFFER_SIZE (32*1024)
|
|
char buffer[PLUGIN_STREAM_BUFFER_SIZE]; /* buffer for data chunk */
|
|
} normal;
|
|
} stream;
|
|
};
|
|
|
|
#define PLUGIN_SCHEDULE_WAIT (40) /* time (in cs) to wait between processing data chunks */
|
|
|
|
#define PLUGIN_PREFIX "Alias$@PlugInType_"
|
|
#define HELPER_PREFIX "Alias$@HelperType_"
|
|
#define SYSVAR_BUF_SIZE (25) /* size of buffer to hold system variable */
|
|
|
|
static bool plugin_create_sysvar(const char *mime_type, char* sysvar,
|
|
bool helper);
|
|
static void plugin_create_stream(struct content *plugin, struct content *c,
|
|
const char *url);
|
|
static bool plugin_send_stream_new(struct plugin_stream *p);
|
|
static void plugin_write_stream(struct plugin_stream *p,
|
|
unsigned int consumed);
|
|
static void plugin_stream_write_callback(void *p);
|
|
static void plugin_stream_as_file_callback(void *p);
|
|
static void plugin_write_stream_as_file(struct plugin_stream *p);
|
|
static void plugin_destroy_stream(struct plugin_stream *p,
|
|
plugin_stream_destroy_reason reason);
|
|
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);
|
|
static void plugin_stream_free(struct plugin_stream *p);
|
|
static bool plugin_start_fetch(struct plugin_stream *p, const char *url);
|
|
static void plugin_stream_callback(content_msg msg, struct content *c,
|
|
intptr_t p1, intptr_t p2, union content_msg_data data);
|
|
static void plugin_fetch_callback(fetch_msg msg, void *p, const void *data,
|
|
unsigned long size);
|
|
|
|
/**
|
|
* 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, struct content *parent,
|
|
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.opened = false;
|
|
c->data.plugin.repeated = 0;
|
|
c->data.plugin.browser = 0;
|
|
c->data.plugin.plugin = 0;
|
|
c->data.plugin.plugin_task = 0;
|
|
c->data.plugin.reformat_pending = false;
|
|
c->data.plugin.width = 0;
|
|
c->data.plugin.height = 0;
|
|
c->data.plugin.streams = 0;
|
|
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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, colour background_colour)
|
|
{
|
|
/* do nothing */
|
|
LOG(("plugin_redraw"));
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle a window containing a CONTENT_PLUGIN being opened.
|
|
*
|
|
* \param c content that has been opened
|
|
* \param bw browser window containing the content
|
|
* \param page content of type CONTENT_HTML containing c, or 0 if not an
|
|
* object within a page
|
|
* \param index index in page->data.html.object, or 0 if not an object
|
|
* \param box box containing c, or 0 if not an object
|
|
* \param params object parameters, or 0 if not an object
|
|
*/
|
|
void plugin_open(struct content *c, struct browser_window *bw,
|
|
struct content *page, unsigned int index, struct box *box,
|
|
struct object_params *params)
|
|
{
|
|
bool standalone = false, helper = false;
|
|
const char *base;
|
|
char sysvar[SYSVAR_BUF_SIZE];
|
|
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 because 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;
|
|
|
|
/* get contents of Alias$@PlugInType_xxx variable */
|
|
if (!plugin_create_sysvar(c->mime_type, sysvar, false))
|
|
goto error;
|
|
|
|
varval = getenv(sysvar);
|
|
LOG(("%s: '%s'", sysvar, varval));
|
|
if(!varval) {
|
|
#if 0
|
|
if (!plugin_create_sysvar(c->mime_type, sysvar, true))
|
|
goto error;
|
|
varval = getenv(sysvar);
|
|
if (!varval)
|
|
goto error;
|
|
helper = true;
|
|
#else
|
|
goto error;
|
|
#endif
|
|
}
|
|
|
|
/* 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;
|
|
pmo.flags = helper ? 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 {
|
|
/* open off the left hand edge of the work area */
|
|
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)
|
|
{
|
|
struct plugin_stream *p, *q;
|
|
plugin_full_message_close pmc;
|
|
os_error *error;
|
|
|
|
LOG(("plugin_close"));
|
|
|
|
if (!plugin_active(c) || !c->data.plugin.opened)
|
|
return;
|
|
|
|
/* destroy all active streams */
|
|
for (p = c->data.plugin.streams; p; p = q) {
|
|
q = p->next;
|
|
|
|
plugin_destroy_stream(p, plugin_STREAM_DESTROY_USER_REQUEST);
|
|
}
|
|
|
|
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 any temporary files */
|
|
for (p = c->data.plugin.streams; p; p = q) {
|
|
q = p->next;
|
|
|
|
assert(p->type == AS_FILE);
|
|
|
|
/* delete the data file used to send the
|
|
* data to the plugin */
|
|
xosfile_delete(p->stream.file.datafile, 0, 0, 0, 0, 0);
|
|
|
|
/* and destroy the struct */
|
|
free(p->stream.file.datafile);
|
|
free(p);
|
|
}
|
|
|
|
/* paranoia */
|
|
c->data.plugin.streams = 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 of length >= SYSVAR_BUF_SIZE into
|
|
* which the string should be written
|
|
* \param helper Whether we're interested in the helper variable
|
|
* \return true on success, false otherwise.
|
|
*/
|
|
bool plugin_create_sysvar(const char *mime_type, char* sysvar, bool helper)
|
|
{
|
|
unsigned int *fv;
|
|
os_error *e;
|
|
|
|
e = xmimemaptranslate_mime_type_to_filetype(mime_type, (bits *) &fv);
|
|
if (e) {
|
|
return false;
|
|
}
|
|
|
|
snprintf(sysvar, SYSVAR_BUF_SIZE, "%s%03x",
|
|
helper ? HELPER_PREFIX : PLUGIN_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[SYSVAR_BUF_SIZE];
|
|
|
|
/* Look for Alias$@PluginType_xxx */
|
|
if (plugin_create_sysvar(mime_type, sysvar, false)) {
|
|
if (getenv(sysvar) != 0) {
|
|
return true;
|
|
}
|
|
}
|
|
#if 0
|
|
/* Look for Alias$@HelperType_xxx */
|
|
if (plugin_create_sysvar(mime_type, sysvar, true)) {
|
|
if (getenv(sysvar) != 0) {
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
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 & plugin_OPENING_WANTS_DATA_FETCHING) {
|
|
LOG(("wants stream"));
|
|
plugin_create_stream(c, c, NULL);
|
|
}
|
|
|
|
if (!(pmo->flags & plugin_OPENING_WILL_DELETE_PARAMETERS)) {
|
|
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 & plugin_CLOSED_WITH_ERROR) {
|
|
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 plugin_stream *p;
|
|
int stream_type;
|
|
plugin_message_stream_new *pmsn =
|
|
(plugin_message_stream_new*)&message->data;
|
|
|
|
LOG(("plugin_stream_new"));
|
|
|
|
p = (struct plugin_stream *)pmsn->browser_stream;
|
|
|
|
/* check we expect this message */
|
|
if (!p || !p->plugin || !plugin_active(p->plugin))
|
|
return;
|
|
|
|
/* response to a message we sent */
|
|
if (message->my_ref != 0) {
|
|
p->pluginh = pmsn->stream;
|
|
|
|
LOG(("flags: %x", pmsn->flags));
|
|
|
|
/* extract the stream type */
|
|
stream_type = pmsn->flags & plugin_STREAM_NEW_TYPE;
|
|
|
|
if (stream_type == plugin_STREAM_NEW_TYPE_AS_FILE_ONLY ||
|
|
stream_type ==
|
|
plugin_STREAM_NEW_TYPE_AS_FILE) {
|
|
LOG(("as file"));
|
|
|
|
p->type = 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 (p->c->source_size == p->c->total_size ||
|
|
p->c->status == CONTENT_STATUS_DONE)
|
|
plugin_write_stream_as_file(p);
|
|
else {
|
|
LOG(("waiting for data"));
|
|
p->stream.file.waiting = true;
|
|
/* schedule a callback */
|
|
schedule(PLUGIN_SCHEDULE_WAIT,
|
|
plugin_stream_as_file_callback, p);
|
|
}
|
|
}
|
|
else if (stream_type == plugin_STREAM_NEW_TYPE_SEEK_ONLY ||
|
|
stream_type ==
|
|
plugin_STREAM_NEW_TYPE_NORMAL) {
|
|
LOG(("write stream"));
|
|
plugin_write_stream(p, 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 plugin_stream *p;
|
|
plugin_message_stream_written *pmsw =
|
|
(plugin_message_stream_written*)&message->data;
|
|
|
|
/* retrieve our stream context */
|
|
p = (struct plugin_stream *)pmsw->browser_stream;
|
|
|
|
/* check we expect this message */
|
|
if (!p || !p->plugin || !plugin_active(p->plugin))
|
|
return;
|
|
|
|
LOG(("got written"));
|
|
|
|
plugin_write_stream(p, 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;
|
|
|
|
notify = (pmua->flags & plugin_URL_ACCESS_NOTIFY_COMPLETION);
|
|
post = (pmua->flags & plugin_URL_ACCESS_USE_POST);
|
|
file = (pmua->flags & plugin_URL_ACCESS_POST_FILE);
|
|
|
|
/* 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, true);
|
|
}
|
|
else if (!option_block_popups &&
|
|
strcasecmp(window, "_blank") == 0) {
|
|
/* don't do this if popups are blocked */
|
|
browser_window_create(url, NULL, 0, true,
|
|
false);
|
|
}
|
|
}
|
|
else { /* POST request */
|
|
/* fetch URL */
|
|
}
|
|
}
|
|
/* fetch data and stream to plugin */
|
|
else {
|
|
if (!post) { /* GET request */
|
|
/* stream to plugin */
|
|
plugin_create_stream(c, NULL, url);
|
|
}
|
|
else { /* POST request */
|
|
/* fetch URL */
|
|
}
|
|
}
|
|
|
|
/* this may be a little early to send this, but tough. */
|
|
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 plugin The content to fetch the data for
|
|
* \param c The content being fetched, or NULL.
|
|
* \param url The url of the resource to fetch, or NULL if content provided.
|
|
*/
|
|
void plugin_create_stream(struct content *plugin, struct content *c,
|
|
const char *url)
|
|
{
|
|
struct plugin_stream *p;
|
|
|
|
assert(plugin && plugin->type == CONTENT_PLUGIN &&
|
|
((c && !url) || (!c && url)));
|
|
|
|
p = malloc(sizeof(struct plugin_stream));
|
|
if (!p)
|
|
return;
|
|
|
|
if (url) {
|
|
if (!plugin_start_fetch(p, url)) {
|
|
free(p);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
p->c = c;
|
|
|
|
p->plugin = plugin;
|
|
p->pluginh = 0;
|
|
p->type = NORMAL;
|
|
p->stream.normal.consumed = 0;
|
|
|
|
/* add to head of list */
|
|
p->next = plugin->data.plugin.streams;
|
|
plugin->data.plugin.streams = p;
|
|
|
|
if (url)
|
|
/* we'll send this later, once some data is arriving */
|
|
return;
|
|
|
|
plugin_send_stream_new(p);
|
|
}
|
|
|
|
/**
|
|
* Send a plugin stream new message
|
|
*
|
|
* \param p The stream context
|
|
* \return true on success, false otherwise
|
|
*/
|
|
bool plugin_send_stream_new(struct plugin_stream *p)
|
|
{
|
|
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)p->plugin->data.plugin.plugin;
|
|
pmsn.browser = (plugin_b)p->plugin->data.plugin.browser;
|
|
pmsn.stream = (plugin_s)0;
|
|
pmsn.browser_stream = (plugin_bs)p;
|
|
pmsn.url.pointer = p->c->url;
|
|
pmsn.end = p->c->total_size;
|
|
pmsn.last_modified_date = 0;
|
|
pmsn.notify_data = 0;
|
|
pmsn.mime_type.pointer = p->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)p->plugin->data.plugin.plugin_task);
|
|
if (error) {
|
|
plugin_stream_free(p);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Writes to an open stream
|
|
*
|
|
* \param c The stream context
|
|
* \param consumed The amount of data consumed
|
|
*/
|
|
void plugin_write_stream(struct plugin_stream *p, unsigned int consumed)
|
|
{
|
|
plugin_full_message_stream_write pmsw;
|
|
os_error *error;
|
|
|
|
assert(p->type == NORMAL);
|
|
|
|
p->stream.normal.consumed += consumed;
|
|
|
|
pmsw.size = 68;
|
|
pmsw.your_ref = 0;
|
|
pmsw.action = message_PLUG_IN_STREAM_WRITE;
|
|
pmsw.flags = 0;
|
|
pmsw.plugin = (plugin_p)p->plugin->data.plugin.plugin;
|
|
pmsw.browser = (plugin_b)p->plugin->data.plugin.browser;
|
|
pmsw.stream = (plugin_s)p->pluginh;
|
|
pmsw.browser_stream = (plugin_bs)p;
|
|
pmsw.url.pointer = p->c->url;
|
|
/* end of stream is p->c->total_size
|
|
* (which is conveniently 0 if unknown)
|
|
*/
|
|
pmsw.end = p->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 = p->stream.normal.consumed;
|
|
/* length of data available is <= sizeof fixed buffer */
|
|
pmsw.length = p->c->source_size - p->stream.normal.consumed;
|
|
if (pmsw.length >= PLUGIN_STREAM_BUFFER_SIZE)
|
|
pmsw.length = PLUGIN_STREAM_BUFFER_SIZE;
|
|
|
|
/* copy data into buffer */
|
|
memcpy(p->stream.normal.buffer,
|
|
p->c->source_data + p->stream.normal.consumed, pmsw.length);
|
|
|
|
/* pointer to available data */
|
|
pmsw.data = (byte*)(p->stream.normal.buffer);
|
|
|
|
/* still have data to send */
|
|
if (p->stream.normal.consumed < p->c->source_size) {
|
|
LOG(("Sending message &4D54A"));
|
|
error = xwimp_send_message(wimp_USER_MESSAGE_RECORDED,
|
|
(wimp_message *)&pmsw,
|
|
(wimp_t)p->plugin->data.plugin.plugin_task);
|
|
if (error) {
|
|
plugin_destroy_stream(p,
|
|
plugin_STREAM_DESTROY_ERROR);
|
|
return;
|
|
}
|
|
}
|
|
else if (p->c->source_size < p->c->total_size) {
|
|
/* the plugin has consumed all the available data,
|
|
* but there's still more to fetch, so we wait for
|
|
* 40 cs then try again (note that streams of unknown
|
|
* total length won't ever get in here as
|
|
* p->c->total_size will be 0)
|
|
*/
|
|
schedule(PLUGIN_SCHEDULE_WAIT,
|
|
plugin_stream_write_callback, p);
|
|
}
|
|
/* no further data => destroy stream */
|
|
else {
|
|
plugin_destroy_stream(p, plugin_STREAM_DESTROY_FINISHED);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stream write callback - used to wait for data to download
|
|
*
|
|
* \param p The stream context
|
|
*/
|
|
void plugin_stream_write_callback(void *p)
|
|
{
|
|
/* remove ourselves from the schedule queue */
|
|
schedule_remove(plugin_stream_write_callback, p);
|
|
|
|
/* continue writing stream */
|
|
plugin_write_stream((struct plugin_stream *)p, 0);
|
|
}
|
|
|
|
/**
|
|
* Stream as file callback - used to wait for data to download
|
|
*
|
|
* \param p The stream context
|
|
*/
|
|
void plugin_stream_as_file_callback(void *p)
|
|
{
|
|
struct plugin_stream *s = (struct plugin_stream *)p;
|
|
|
|
/* remove ourselves from the schedule queue */
|
|
schedule_remove(plugin_stream_as_file_callback, p);
|
|
|
|
if (s->c->source_size < s->c->total_size ||
|
|
s->c->status != CONTENT_STATUS_DONE) {
|
|
/* not got all the data so wait some more */
|
|
schedule(PLUGIN_SCHEDULE_WAIT,
|
|
plugin_stream_as_file_callback, p);
|
|
return;
|
|
}
|
|
|
|
/* deal with a plugin waiting for a file stream */
|
|
if (s->stream.file.waiting) {
|
|
s->stream.file.waiting = false;
|
|
plugin_write_stream_as_file(s);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes a stream as a file
|
|
*
|
|
* \param c The stream context
|
|
*/
|
|
void plugin_write_stream_as_file(struct plugin_stream *p)
|
|
{
|
|
plugin_full_message_stream_as_file pmsaf;
|
|
unsigned int filetype;
|
|
os_error *error;
|
|
|
|
assert(p->type == AS_FILE);
|
|
|
|
p->stream.file.datafile =
|
|
calloc(strlen(getenv("Wimp$ScrapDir"))+13+10, sizeof(char));
|
|
|
|
if (!p->stream.file.datafile) {
|
|
LOG(("malloc failed"));
|
|
warn_user("NoMemory", 0);
|
|
plugin_destroy_stream(p, plugin_STREAM_DESTROY_ERROR);
|
|
return;
|
|
}
|
|
|
|
/* create filename */
|
|
sprintf(p->stream.file.datafile, "%s.WWW.NetSurf.d%x",
|
|
getenv("Wimp$ScrapDir"), (unsigned int)p);
|
|
|
|
pmsaf.size = 60;
|
|
pmsaf.your_ref = 0;
|
|
pmsaf.action = message_PLUG_IN_STREAM_AS_FILE;
|
|
pmsaf.flags = 0;
|
|
pmsaf.plugin = (plugin_p)p->plugin->data.plugin.plugin;
|
|
pmsaf.browser = (plugin_b)p->plugin->data.plugin.browser;
|
|
pmsaf.stream = (plugin_s)p->pluginh;
|
|
pmsaf.browser_stream = (plugin_bs)p;
|
|
pmsaf.url.pointer = p->c->url;
|
|
pmsaf.end = p->c->total_size;
|
|
pmsaf.last_modified_date = 0;
|
|
pmsaf.notify_data = 0;
|
|
pmsaf.filename.pointer = p->stream.file.datafile;
|
|
|
|
error = xmimemaptranslate_mime_type_to_filetype(p->c->mime_type,
|
|
(bits *) &filetype);
|
|
if (error) {
|
|
plugin_destroy_stream(p, plugin_STREAM_DESTROY_ERROR);
|
|
return;
|
|
}
|
|
|
|
error = xosfile_save_stamped((char const*)p->stream.file.datafile,
|
|
filetype, p->c->source_data,
|
|
p->c->source_data + p->c->source_size);
|
|
if (error) {
|
|
plugin_destroy_stream(p, plugin_STREAM_DESTROY_ERROR);
|
|
return;
|
|
}
|
|
|
|
LOG(("Sending message &4D54C"));
|
|
error = xwimp_send_message(wimp_USER_MESSAGE,
|
|
(wimp_message *)&pmsaf,
|
|
(wimp_t)p->plugin->data.plugin.plugin_task);
|
|
if (error) {
|
|
plugin_destroy_stream(p, plugin_STREAM_DESTROY_ERROR);
|
|
return;
|
|
}
|
|
|
|
plugin_destroy_stream(p, plugin_STREAM_DESTROY_FINISHED);
|
|
}
|
|
|
|
/**
|
|
* Destroys a plugin stream
|
|
*
|
|
* \param c The stream context to destroy
|
|
* \param reason The reason for the destruction
|
|
*/
|
|
void plugin_destroy_stream(struct plugin_stream *p,
|
|
plugin_stream_destroy_reason reason)
|
|
{
|
|
plugin_full_message_stream_destroy pmsd;
|
|
os_error *error;
|
|
|
|
if (p->type == AS_FILE && p->stream.file.destroyed)
|
|
/* we've already destroyed this stream */
|
|
return;
|
|
|
|
/* stop any scheduled callbacks */
|
|
if (p->type == NORMAL)
|
|
schedule_remove(plugin_stream_write_callback, p);
|
|
else
|
|
schedule_remove(plugin_stream_as_file_callback, p);
|
|
|
|
pmsd.size = 60;
|
|
pmsd.your_ref = 0;
|
|
pmsd.action = message_PLUG_IN_STREAM_DESTROY;
|
|
pmsd.flags = 0;
|
|
pmsd.plugin = (plugin_p)p->plugin->data.plugin.plugin;
|
|
pmsd.browser = (plugin_b)p->plugin->data.plugin.browser;
|
|
pmsd.stream = (plugin_s)p->pluginh;
|
|
pmsd.browser_stream = (plugin_bs)p;
|
|
pmsd.url.pointer = p->c->url;
|
|
pmsd.end = p->c->total_size;
|
|
pmsd.last_modified_date = 0;
|
|
pmsd.notify_data = 0;
|
|
pmsd.reason = reason;
|
|
|
|
LOG(("Sending message &4D549"));
|
|
error = xwimp_send_message(wimp_USER_MESSAGE,
|
|
(wimp_message *)&pmsd,
|
|
(wimp_t)p->plugin->data.plugin.plugin_task);
|
|
if (error) {
|
|
LOG(("0x%x %s", error->errnum, error->errmess));
|
|
}
|
|
|
|
plugin_stream_free(p);
|
|
}
|
|
|
|
/**
|
|
* 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, sizeof(int), 1, fp);
|
|
fwrite(&pilist->rsize, sizeof(int), 1, fp);
|
|
|
|
fwrite(&pilist->nsize, sizeof(int), 1, fp);
|
|
fwrite(pilist->name, strlen(pilist->name), 1, fp);
|
|
for (; pilist->npad != 0; pilist->npad--)
|
|
fputc('\0', fp);
|
|
|
|
fwrite(&pilist->vsize, sizeof(int), 1, fp);
|
|
fwrite(pilist->value, strlen(pilist->value), 1, fp);
|
|
for(; pilist->vpad != 0; pilist->vpad--)
|
|
fputc('\0', fp);
|
|
|
|
fwrite(&pilist->msize, sizeof(int), 1, fp);
|
|
if (pilist->msize > 0) {
|
|
fwrite(pilist->mime_type,
|
|
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("", 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;
|
|
}
|
|
|
|
/**
|
|
* Free a plugin_stream struct and unlink it from the list
|
|
*/
|
|
void plugin_stream_free(struct plugin_stream *p)
|
|
{
|
|
if (p->c != p->plugin) {
|
|
if (p->c->fetch) {
|
|
/* abort fetch, if active */
|
|
fetch_abort(p->c->fetch);
|
|
p->c->fetch = 0;
|
|
p->c->status = CONTENT_STATUS_DONE;
|
|
}
|
|
content_remove_user(p->c, plugin_stream_callback,
|
|
(intptr_t)p, 0);
|
|
}
|
|
|
|
/* free normal stream context. file streams get freed later */
|
|
if (p->type == NORMAL) {
|
|
struct plugin_stream *q;
|
|
for (q = p->plugin->data.plugin.streams; q && q->next != p;
|
|
q = q->next)
|
|
/* do nothing */;
|
|
assert(q || p == p->plugin->data.plugin.streams);
|
|
if (q)
|
|
q->next = p->next;
|
|
else
|
|
p->plugin->data.plugin.streams = p->next;
|
|
|
|
free(p);
|
|
}
|
|
else
|
|
p->stream.file.destroyed = true;
|
|
}
|
|
|
|
/**
|
|
* Initialise a fetch for a plugin
|
|
*
|
|
* \param p The stream context to fetch for
|
|
* \param url The URL to fetch
|
|
* \return true on successful fetch initiation and p->c filled in, false
|
|
* otherwise.
|
|
*/
|
|
bool plugin_start_fetch(struct plugin_stream *p, const char *url)
|
|
{
|
|
char *url2;
|
|
struct content *c;
|
|
url_func_result res;
|
|
|
|
assert(p && url);
|
|
|
|
res = url_normalize(url, &url2);
|
|
if (res != URL_FUNC_OK) {
|
|
return false;
|
|
}
|
|
|
|
if (!fetch_can_fetch(url2)) {
|
|
free(url2);
|
|
return false;
|
|
}
|
|
|
|
c = fetchcache(url2, plugin_stream_callback, (intptr_t)p, 0,
|
|
100, 100, true, 0, 0, false, true);
|
|
free(url2);
|
|
if (!c) {
|
|
return false;
|
|
}
|
|
|
|
p->c = c;
|
|
fetchcache_go(c, 0, plugin_stream_callback, (intptr_t)p, 0,
|
|
100, 100, 0, 0, false, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Callback for fetchcache() for plugin stream fetches.
|
|
*/
|
|
void plugin_stream_callback(content_msg msg, struct content *c,
|
|
intptr_t p1, intptr_t p2, union content_msg_data data)
|
|
{
|
|
struct plugin_stream *p = (struct plugin_stream *)p1;
|
|
|
|
switch (msg) {
|
|
case CONTENT_MSG_LOADING:
|
|
assert(p->c == c);
|
|
assert(c->type == CONTENT_OTHER);
|
|
fetch_change_callback(c->fetch,
|
|
plugin_fetch_callback, p);
|
|
/* and kickstart the stream protocol */
|
|
plugin_send_stream_new(p);
|
|
break;
|
|
|
|
case CONTENT_MSG_LAUNCH:
|
|
/* Fall through */
|
|
case CONTENT_MSG_ERROR:
|
|
/* The plugin we were fetching may have been
|
|
* redirected, in that case, the object pointers
|
|
* will differ, so ensure that the object that's
|
|
* in error is still in use by us before destroying
|
|
* the stream */
|
|
if (p->c == c)
|
|
plugin_destroy_stream(p,
|
|
plugin_STREAM_DESTROY_ERROR);
|
|
break;
|
|
|
|
case CONTENT_MSG_NEWPTR:
|
|
p->c = c;
|
|
break;
|
|
|
|
case CONTENT_MSG_AUTH:
|
|
/**\todo handle authentication */
|
|
plugin_destroy_stream(p, plugin_STREAM_DESTROY_ERROR);
|
|
break;
|
|
|
|
case CONTENT_MSG_STATUS:
|
|
/* ignore this */
|
|
break;
|
|
|
|
case CONTENT_MSG_SSL:
|
|
plugin_destroy_stream(p, plugin_STREAM_DESTROY_ERROR);
|
|
break;
|
|
|
|
case CONTENT_MSG_READY:
|
|
case CONTENT_MSG_DONE:
|
|
case CONTENT_MSG_REFORMAT:
|
|
case CONTENT_MSG_REDRAW:
|
|
default:
|
|
/* not possible */
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback for plugin fetch
|
|
*/
|
|
void plugin_fetch_callback(fetch_msg msg, void *p, const void *data,
|
|
unsigned long size)
|
|
{
|
|
struct plugin_stream *s = p;
|
|
union content_msg_data msg_data;
|
|
|
|
switch (msg) {
|
|
case FETCH_PROGRESS:
|
|
break;
|
|
|
|
case FETCH_DATA:
|
|
if (!content_process_data(s->c, data, size)) {
|
|
fetch_abort(s->c->fetch);
|
|
s->c->fetch = 0;
|
|
}
|
|
break;
|
|
|
|
case FETCH_FINISHED:
|
|
s->c->fetch = 0;
|
|
s->c->status = CONTENT_STATUS_DONE;
|
|
break;
|
|
|
|
case FETCH_ERROR:
|
|
s->c->fetch = 0;
|
|
s->c->status = CONTENT_STATUS_ERROR;
|
|
msg_data.error = data;
|
|
content_broadcast(s->c, CONTENT_MSG_ERROR, msg_data);
|
|
break;
|
|
|
|
case FETCH_NOTMODIFIED:
|
|
case FETCH_AUTH:
|
|
case FETCH_CERT_ERR:
|
|
default:
|
|
/* not possible */
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif
|