mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-26 22:09:43 +03:00
23fb72ea6b
Do not change the locale globally, else things will break in weird and wonderful ways. Introduce utils/locale.[ch], which provide locale-specific wrappers for various functions (currently just the <ctype.h> ones). Fix up the few places I can see that actually require that the underlying locale is paid attention to. Some notes: 1) The GTK frontend code has not been touched. It is possible that reading of numeric values (e.g. from the preferences dialogue) may break with this change, particularly in locales that use something other than '.' as their decimal separator. 2) The search code is left unchanged (i.e. assuming a locale of "C"). This may break case insensitive matching of non-ASCII characters. I doubt that ever actually worked, anyway. In future, it should use Unicode case conversion to achieve the same effect. 3) The text input handling in the core makes use of isspace() to detect word boundaries. This is fine for western languages (even in the C locale, which it's currently assuming). It will, however, break for CJK et. al. (this has always been the case, rather than being a new issue) 4) text-transform uses locale-specific variants of to{lower,upper}. In future this should probably be performing Unicode case conversion. This is the only part of the core code that makes use of locale information. In future, if you require locale-specific behaviour, do the following: setlocale(LC_<whatever>, ""); <your operation(s) here> setlocale(LC_<whatever>, "C"); The first setlocale will change the current locale to the native environment. The second setlocale will reset the current locale to "C". Any value other than "" or "C" is probably a bug, unless there's a really good reason for it. In the long term, it is expected that all locale-dependent code will reside in platform frontends -- the core being wholly locale agnostic (though assuming "C" for things like decimal separators). svn path=/trunk/netsurf/; revision=4153
1784 lines
44 KiB
C
1784 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 <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"
|
|
|
|
|
|
#ifdef WITH_PLUGIN
|
|
|
|
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, 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, unsigned long 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);
|
|
}
|
|
}
|
|
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, (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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
#ifdef WITH_SSL
|
|
case CONTENT_MSG_SSL:
|
|
plugin_destroy_stream(p, plugin_STREAM_DESTROY_ERROR);
|
|
break;
|
|
#endif
|
|
|
|
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_TYPE:
|
|
case FETCH_NOTMODIFIED:
|
|
case FETCH_AUTH:
|
|
#ifdef WITH_SSL
|
|
case FETCH_CERT_ERR:
|
|
#endif
|
|
default:
|
|
/* not possible */
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif
|