home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.0 (the "NPL"); you may not use this file except in
- * compliance with the NPL. You may obtain a copy of the NPL at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the NPL is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
- * for the specific language governing rights and limitations under the
- * NPL.
- *
- * The Initial Developer of this code under the NPL is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All Rights
- * Reserved.
- */
-
- /* Please leave at the top for windows precompiled headers */
- #include "mkutils.h"
-
- #include <stddef.h>
- #include <memory.h>
- #include <time.h>
- #include "net.h"
- #include "xp.h"
- #include "libmocha.h"
- #include "mkgeturl.h"
- #include "mkmocha.h"
- #include "libevent.h"
- #include "fe_proto.h"
- #include "pa_tags.h" /* included via -I../libparse */
- #include "libi18n.h" /* unicode */
- #include "layout.h" /* for lo_ContextToCell only */
-
- extern char lm_unknown_origin_str[];
-
- extern int MK_OUT_OF_MEMORY;
- extern int MK_MALFORMED_URL_ERROR;
-
- typedef struct {
- char * buffer;
- size_t offset;
- size_t length;
- MWContext * context;
- char * content_type;
- int16 char_set;
- } MochaStream;
-
- typedef struct {
- int32 ref_count;
- ActiveEntry * active_entry;
- PRPackedBool is_valid;
- PRPackedBool eval_what;
- PRPackedBool single_shot;
- PRPackedBool polling;
- char * str;
- size_t len;
- MWContext * context;
- int status;
- char * wysiwyg_url;
- char * base_href;
- NET_StreamClass * stream;
- } MochaConData;
-
- #define HOLD_CON_DATA(con_data) ((con_data)->ref_count++)
- #define DROP_CON_DATA(con_data) { \
- if (--(con_data)->ref_count == 0) \
- free_con_data(con_data); \
- }
-
- static void
- free_con_data(MochaConData * con_data)
- {
- XP_FREEIF(con_data->str);
- XP_FREEIF(con_data->wysiwyg_url);
- XP_FREEIF(con_data->base_href);
- XP_FREE(con_data);
- }
-
- #define START_POLLING(ae, con_data) { \
- (con_data)->polling = TRUE; \
- NET_SetCallNetlibAllTheTime((ae)->window_id, "mkmocha"); \
- }
-
- #define STOP_POLLING(ae, con_data) { \
- NET_ClearCallNetlibAllTheTime((ae)->window_id, "mkmocha"); \
- (con_data)->polling = FALSE; \
- }
-
- /* forward decl */
- PRIVATE int32 net_ProcessMocha(ActiveEntry * ae);
-
- /*
- * Add the new bits to our buffer
- */
- PRIVATE int
- mocha_process(NET_StreamClass *stream, const char *str, int32 len)
- {
- MochaStream * mocha_stream = (MochaStream *) stream->data_object;
-
- mocha_stream->length += len;
- if (!mocha_stream->buffer) {
- mocha_stream->buffer = (char *)XP_ALLOC(mocha_stream->length);
- }
- else {
- mocha_stream->buffer = (char *)XP_REALLOC(mocha_stream->buffer,
- mocha_stream->length);
- }
- if (!mocha_stream->buffer) {
- return MK_OUT_OF_MEMORY;
- }
- memcpy(mocha_stream->buffer + mocha_stream->offset, str, len);
- mocha_stream->offset += len;
- return len;
- }
-
- PRIVATE unsigned int
- mocha_ready(NET_StreamClass *stream)
- {
- return MAX_WRITE_READY; /* always ready for writing */
- }
-
-
- /*
- * All of the processing for this needs to be done in the mocha thread
- */
- PRIVATE void
- mocha_complete(NET_StreamClass *stream)
- {
- void * data;
- JSBool isUnicode = JS_FALSE;
-
- MochaStream * mocha_stream = (MochaStream *) stream->data_object;
-
- if (mocha_stream->char_set != CS_DEFAULT) {
- uint32 len;
- INTL_Unicode * unicode;
-
- /* find out how many unicode characters we'll end up with */
- len = INTL_TextToUnicodeLen(mocha_stream->char_set,
- (unsigned char *) mocha_stream->buffer,
- mocha_stream->length);
- unicode = XP_ALLOC(sizeof(INTL_Unicode) * len);
- if (!unicode)
- return;
-
- /* do the conversion */
- mocha_stream->length = INTL_TextToUnicode(mocha_stream->char_set,
- (unsigned char *) mocha_stream->buffer,
- mocha_stream->length,
- unicode,
- len);
-
- data = unicode;
- isUnicode = JS_TRUE;
-
- XP_FREE(mocha_stream->buffer);
- mocha_stream->buffer = NULL;
- }
- else {
- data = mocha_stream->buffer;
- }
-
- /* this will grab ownership of data */
- ET_MochaStreamComplete(mocha_stream->context, data,
- mocha_stream->length,
- mocha_stream->content_type,
- isUnicode);
-
- XP_FREEIF(mocha_stream->content_type);
- XP_FREE(mocha_stream);
-
- }
-
- PRIVATE void
- mocha_abort(NET_StreamClass *stream, int status)
- {
- MochaStream * mocha_stream = (MochaStream *) stream->data_object;
-
- ET_MochaStreamAbort(mocha_stream->context, status);
- XP_FREE(mocha_stream->buffer);
- XP_FREEIF(mocha_stream->content_type);
- XP_FREE(mocha_stream);
- }
-
- int16
- net_check_for_charset(URL_Struct *url_struct)
- {
- int i, max;
- char *key, *value;
- static char charset[] = "charset=";
- int len = XP_STRLEN(charset);
-
- max = url_struct->all_headers.empty_index;
-
- for (i = 0; i < max; i++) {
- key = url_struct->all_headers.key[i];
-
- /* keep looking until we find the content type one */
- if (XP_STRCASECMP(key, "Content-type"))
- continue;
-
- value = url_struct->all_headers.value[i];
-
- /* don't bother unless this is a JS file to begin with */
- if (!strcasestr(value, APPLICATION_JAVASCRIPT))
- return CS_DEFAULT;
-
- value = XP_STRTOK(value, ";");
- while (value) {
- value = XP_StripLine(value);
-
- if (!strncasecomp(value, charset, len)) {
- value += len;
- value = XP_StripLine(value);
- return (INTL_CharSetNameToID(value));
- }
-
- /* move to next arg */
- value = XP_STRTOK(NULL, ";");
-
- }
-
- /* found content-type but no charset */
- return CS_DEFAULT;
-
- }
-
- /* didn't find content-type */
- return CS_DEFAULT;
- }
-
- static char *
- getOriginFromURLStruct(MWContext *context, URL_Struct *url_struct)
- {
- char *referer;
- /*
- * Look in url_struct->origin_url for this javascript: URL's
- * original referrer.
- *
- * In the basis case, a javascript: or *.js URL targets a context
- * from a (the same, or not) context loaded with a non-javascript URL.
- * The referring document's URL is in url_struct->referer and we
- * duplicate it as origin_url. If we find a non-null origin_url
- * later, we know by induction where it came from.
- */
- referer = url_struct->origin_url;
- if (referer == NULL) {
- /*
- * url_struct->referer (sic) tells the URL of the page in
- * which a javascript: or *.js link or form was clicked or submitted.
- * If it's non-null, but the context is a frame cell that's
- * being (re-)created for this load, don't use referer as the
- * decoder's source URL for the evaluation, because the FE has
- * arbitrarily set it to the top frameset's URL. Instead, use
- * the immediate parent frameset context's wysiwyg URL to get
- * the origin of the generator (or the parent's address URL if
- * not wysiwyg).
- *
- * If referer is null, the user typed this javascript: URL or
- * its frameset's URL directly into the Location toolbar.
- */
- referer = url_struct->referer;
- if (referer) {
- lo_GridRec *grid = NULL;
-
- if (context->is_grid_cell &&
- !lo_ContextToCell(context, FALSE, &grid)) {
- /*
- * Context is a frame being (re)created: veto referer.
- * The javascript: or *.js URL can't be a LAYER SRC= URL in a
- * frame because the frame's cell must point to its
- * context by the time the first tag (even LAYER) is
- * processed by layout.
- */
- referer = NULL;
- }
- }
- if (!referer) {
- History_entry *he;
-
- if (context->grid_parent) {
- /*
- * If grid parent, use its history entry to get the
- * wysiwyg or real URL, which tells the subject origin
- * of (any code in it that may have generated) this
- * javascript: or *.js URL open. If no grid parent, this must
- * be a javascript: or *.js URL that was typed directly into
- * Location.
- */
- context = context->grid_parent;
- }
- he = SHIST_GetCurrent(&context->hist);
- if (!he) {
- referer = lm_unknown_origin_str;
- } else {
- referer = he->wysiwyg_url;
- if (!referer)
- referer = he->address;
- }
- }
- }
-
- XP_ASSERT(referer);
- referer = XP_STRDUP(referer);
- if (!referer) {
- return NULL;
- }
- url_struct->origin_url = referer;
- return referer;
- }
-
- NET_StreamClass *
- NET_CreateMochaConverter(FO_Present_Types format_out,
- void *data_object,
- URL_Struct *url_struct,
- MWContext *context)
- {
- MochaStream * mocha_stream;
- NET_StreamClass *stream;
- char *origin;
-
- mocha_stream = (MochaStream *) XP_NEW_ZAP(MochaStream);
- if (!mocha_stream)
- return NULL;
-
- mocha_stream->context = context;
- mocha_stream->content_type = XP_STRDUP(url_struct->content_type);
- mocha_stream->char_set = net_check_for_charset(url_struct);
-
- /* Get the origin from the URL struct. We don't have to free origin
- * here because url_struct->origin_url owns it.
- */
- origin = getOriginFromURLStruct(context, url_struct);
- if (origin == NULL)
- return NULL;
-
- if (NET_URL_Type(url_struct->address) == FILE_TYPE_URL &&
- NET_URL_Type(origin) != FILE_TYPE_URL)
- {
- /*
- * Don't allow loading a file: URL from a non-file URL to
- * prevent privacy attacks against the local machine from
- * web pages.
- */
- return NULL;
- }
-
- stream = NET_NewStream("mocha", mocha_process, mocha_complete,
- mocha_abort, mocha_ready, mocha_stream,
- context);
- return stream;
- }
-
- PRIVATE void
- mk_mocha_eval_exit_fn(void * data, char * str, size_t len, char * wysiwyg_url,
- char * base_href, Bool valid)
- {
- MochaConData * con_data = (MochaConData *) data;
-
- if (!valid) {
- con_data->status = MK_MALFORMED_URL_ERROR;
- con_data->is_valid = TRUE;
- return;
- }
- if (str == NULL) {
- con_data->status = MK_DATA_LOADED;
- con_data->is_valid = TRUE;
- return;
- }
-
- con_data->base_href = base_href;
- con_data->wysiwyg_url = wysiwyg_url;
- con_data->str = str;
- con_data->len = len;
- con_data->is_valid = TRUE;
-
- }
-
- /*
- * Handle both 'mocha:<stuff>' urls and the mocha type-in widget
- */
- MODULE_PRIVATE int32
- net_MochaLoad(ActiveEntry *ae)
- {
- MWContext *context;
- URL_Struct *url_struct;
- char *what;
- Bool eval_what, single_shot;
- MochaConData * con_data;
-
- context = ae->window_id;
- url_struct = ae->URL_s;
- what = XP_STRCHR(url_struct->address, ':');
- XP_ASSERT(what);
- what++;
- eval_what = FALSE;
- single_shot = (*what != '?');
-
- if (single_shot) {
- /* Don't use an existing Mocha output window's stream. */
- if (*what == '\0') {
- /* Generate two grid cells, one for output and one for input. */
- what = PR_smprintf("<frameset rows=\"75%%,25%%\">\n"
- "<frame name=MochaOutput src=about:blank>\n"
- "<frame name=MochaInput src=%.*s#input>\n"
- "</frameset>",
- what - url_struct->address,
- url_struct->address);
- } else if (!XP_STRCMP(what, "#input")) {
- /* The input cell contains a form with one magic isindex field. */
- what = PR_smprintf("<b>%.*s typein</b>\n"
- "<form action=%.*s target=MochaOutput"
- " onsubmit='this.isindex.focus()'>\n"
- "<input name=isindex size=60>\n"
- "</form>",
- what - url_struct->address - 1,
- url_struct->address,
- what - url_struct->address,
- url_struct->address);
- url_struct->internal_url = TRUE;
- } else {
- eval_what = TRUE;
- }
- } else {
- /* Use the Mocha output window if it exists. */
- url_struct->auto_scroll = 1000;
-
- /* Skip the leading question-mark and clean up the string. */
- what++;
- NET_PlusToSpace(what);
- NET_UnEscape(what);
- eval_what = TRUE;
- }
-
- /* something got hosed. bail */
- if (!what) {
- ae->status = MK_OUT_OF_MEMORY;
- return -1;
- }
-
- /* make space for the connection data */
- con_data = XP_NEW_ZAP(MochaConData);
- if (!con_data) {
- ae->status = MK_OUT_OF_MEMORY;
- return -1;
- }
-
- /* remember for next time */
- con_data->ref_count = 1;
- con_data->active_entry = ae;
- ae->con_data = con_data;
-
- /* set up some state so we remember where we are */
- con_data->eval_what = eval_what;
- con_data->single_shot = single_shot;
- con_data->context = context;
-
- /* fake out netlib so we don't select on the socket id */
- ae->socket = NULL;
- ae->local_file = TRUE;
-
- if (eval_what) {
- char *referer;
- JSPrincipals *principals;
- ETEvalStuff *stuff;
-
- referer = getOriginFromURLStruct(context, url_struct);
- if (!referer) {
- ae->status = MK_OUT_OF_MEMORY;
- return -1;
- }
-
- principals = LM_NewJSPrincipals(NULL, NULL, referer);
- if (!principals) {
- ae->status = MK_OUT_OF_MEMORY;
- return -1;
- }
-
- /*
- * send the buffer off to be evaluated
- */
- stuff = (ETEvalStuff *) XP_NEW_ZAP(ETEvalStuff);
- if (!stuff) {
- ae->status = MK_OUT_OF_MEMORY;
- return -1;
- }
-
- stuff->len = XP_STRLEN(what);
- stuff->line_no = 0;
- stuff->scope_to = NULL;
- stuff->want_result = JS_TRUE;
- stuff->data = con_data;
- stuff->version = JSVERSION_UNKNOWN;
- stuff->principals = principals;
-
- ET_EvaluateScript(context, what, stuff, mk_mocha_eval_exit_fn);
-
- }
- else {
-
- /* allocated above, don't need to free */
- con_data->str = what;
- con_data->len = XP_STRLEN(what);
- con_data->is_valid = TRUE;
-
- }
-
- /* make sure netlib gets called so we know when our data gets back */
- START_POLLING(ae, con_data);
-
- /* ya'll come back now, ya'hear? */
- return net_ProcessMocha(ae);
-
- }
-
- PRIVATE int
- net_process_mocha(MochaConData * con_data)
- {
- int32 ref_count;
-
- ref_count = con_data->ref_count;
- DROP_CON_DATA(con_data);
- if (ref_count == 1 || !con_data->active_entry)
- return -1;
- return net_ProcessMocha(con_data->active_entry);
- }
-
- static char nscp_open_tag[] = "<" PT_NSCP_OPEN ">";
-
- /*
- * If the mocha has finished evaluation shove the result
- * down the stream and continue else just wait
- */
- PRIVATE int32
- net_ProcessMocha(ActiveEntry * ae)
- {
- FO_Present_Types output_format;
- Bool to_layout;
- Bool first_time;
- NET_StreamClass *stream = NULL;
- MochaConData * con_data = (MochaConData *) ae->con_data;
- MWContext * context;
- int status;
-
- /* if we haven't gotten our data yet just return and wait */
- if (!con_data || !con_data->is_valid)
- return 0;
-
- context = ae->window_id;
-
- /*
- * Race with the mocha thread until we can grab the JSLock
- */
- if (!con_data->single_shot) {
- MochaDecoder * decoder;
-
- HOLD_CON_DATA(con_data);
- if (!LM_AttemptLockJS((JSLockReleaseFunc)net_process_mocha, con_data))
- return 0;
- DROP_CON_DATA(con_data);
- decoder = LM_GetMochaDecoder(context);
- if (!decoder) {
- LM_UnlockJS();
- ae->status = MK_OUT_OF_MEMORY;
- goto done;
- }
- stream = decoder->stream;
- LM_PutMochaDecoder(decoder);
- LM_UnlockJS();
- }
- else {
- stream = con_data->stream;
- }
-
- /* we've gotten valid data, clear the callme all the time flag */
- STOP_POLLING(ae, con_data);
-
- /* see if there were any problems */
- if (con_data->status < 0 || con_data->str == NULL) {
- ET_SendLoadEvent(context, EVENT_LOAD, NULL, NULL,
- LO_DOCUMENT_LAYER_ID, FALSE);
- ae->status = con_data->status;
- goto done;
- }
-
- /*
- * If we don't already have a stream to take our output create one now
- */
- first_time = !stream;
- if (first_time) {
- StrAllocCopy(ae->URL_s->content_type, TEXT_HTML);
- ae->format_out = CLEAR_CACHE_BIT(ae->format_out);
-
- stream = NET_StreamBuilder(ae->format_out, ae->URL_s, ae->window_id);
- if (!stream) {
- ae->status = MK_UNABLE_TO_CONVERT;
- goto done;
- }
- }
-
- /*
- * If the stream we just created isn't ready for writing just
- * hold onto the stream and try again later
- */
- if (!stream->is_write_ready(stream)) {
- con_data->stream = stream;
- START_POLLING(ae, con_data);
- return 0;
- }
-
- /* XXX this condition is fairly bogus */
- output_format = CLEAR_CACHE_BIT(ae->format_out);
- to_layout = (output_format != FO_INTERNAL_IMAGE &&
- output_format != FO_MULTIPART_IMAGE &&
- output_format != FO_EMBED &&
- output_format != FO_PLUGIN);
-
- if (to_layout) {
- /* The string must end in a newline so the parser will flush it. */
- if (con_data->len != 0 && con_data->str[con_data->len-1] != '\n') {
- size_t new_len = con_data->len + 1;
- char * new_str = XP_ALLOC((new_len + 1) * sizeof(char));
-
- if (!new_str) {
- ae->status = MK_OUT_OF_MEMORY;
- goto done;
- }
- XP_MEMCPY(new_str, con_data->str, con_data->len);
- new_str[new_len-1] = '\n';
- new_str[new_len] = '\0';
- con_data->str = new_str;
- con_data->len = new_len;
- }
- }
-
-
- /*
- * If this is the first time do some initial setup. We'll set
- * the window title and maybe shove out a <BASE> tag
- */
- status = 0;
- if (to_layout && first_time && con_data->eval_what) {
-
- /* Feed layout an internal tag so it will create a new top state. */
- stream->put_block(stream, nscp_open_tag,
- sizeof nscp_open_tag - 1);
-
- (void) LM_WysiwygCacheConverter(context, ae->URL_s,
- con_data->wysiwyg_url,
- con_data->base_href);
-
- if (*con_data->str != '<') {
- char * prefix = NULL;
- StrAllocCopy(prefix, "<TITLE>");
- StrAllocCat(prefix, ae->URL_s->address);
- StrAllocCat(prefix, "</TITLE><PLAINTEXT>");
- if (!prefix) {
- ae->status = MK_OUT_OF_MEMORY;
- goto done;
- }
- status = (*stream->put_block)(stream, prefix,
- XP_STRLEN(prefix));
- XP_FREE(prefix);
- }
- else {
- if (con_data->base_href) {
- status = (*stream->put_block)(stream,
- con_data->base_href,
- XP_STRLEN(con_data->base_href));
- }
- }
- }
-
- if (status >= 0) {
- status = (*stream->put_block)(stream, con_data->str,
- (int32)con_data->len);
- }
-
- if (con_data->single_shot && status >= 0)
- (*stream->complete)(stream);
-
- if (!con_data->single_shot && status >= 0) {
- if (first_time) {
- ET_SetDecoderStream(context, stream, ae->URL_s, JS_TRUE);
- goto done;
- }
- } else {
- if (status < 0)
- (*stream->abort)(stream, status);
- if (first_time)
- XP_DELETE(stream);
- }
-
- ae->status = MK_DATA_LOADED;
-
- done:
- ae->con_data = NULL;
- con_data->active_entry = NULL;
- DROP_CON_DATA(con_data);
- return -1;
-
- }
-
- PRIVATE int32
- net_InterruptMocha(ActiveEntry *ae)
- {
- MochaConData * con_data = (MochaConData *) ae->con_data;
-
- if (!con_data)
- return MK_INTERRUPTED;
-
- /* do we need to decrement the callme all the time flag? */
- if (con_data->polling) {
- STOP_POLLING(ae, con_data);
- }
-
- /* ae is about to go away, break its connection with con_data */
- ae->con_data = NULL;
- con_data->active_entry = NULL;
-
- /* ae is about to go away, better get it off the JS lock waiters list */
- if (LM_ClearAttemptLockJS((JSLockReleaseFunc)net_process_mocha, con_data))
- DROP_CON_DATA(con_data);
-
- return ae->status = MK_INTERRUPTED;
- }
-
- PRIVATE void
- net_CleanupMocha(void)
- {
- /* nothing so far needs freeing */
- return;
- }
-
- MODULE_PRIVATE void
- NET_InitMochaProtocol(void)
- {
- static NET_ProtoImpl mocha_proto_impl;
-
- mocha_proto_impl.init = net_MochaLoad;
- mocha_proto_impl.process = net_ProcessMocha;
- mocha_proto_impl.interrupt = net_InterruptMocha;
- mocha_proto_impl.cleanup = net_CleanupMocha;
-
- NET_RegisterProtocolImplementation(&mocha_proto_impl, MOCHA_TYPE_URL);
- }
-
-