home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libnet / mkmocha.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  21.3 KB  |  750 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /* Please leave at the top for windows precompiled headers */
  20. #include "mkutils.h"
  21.  
  22. #include <stddef.h>
  23. #include <memory.h>
  24. #include <time.h>
  25. #include "net.h"
  26. #include "xp.h"
  27. #include "libmocha.h"
  28. #include "mkgeturl.h"
  29. #include "mkmocha.h"
  30. #include "libevent.h"
  31. #include "fe_proto.h"
  32. #include "pa_tags.h"    /* included via -I../libparse */
  33. #include "libi18n.h"    /* unicode */
  34. #include "layout.h"    /* for lo_ContextToCell only */
  35.  
  36. extern char lm_unknown_origin_str[];
  37.  
  38. extern int MK_OUT_OF_MEMORY;
  39. extern int MK_MALFORMED_URL_ERROR;
  40.  
  41. typedef struct {
  42.     char *      buffer;
  43.     size_t      offset;
  44.     size_t        length;
  45.     MWContext    * context;
  46.     char    * content_type;
  47.     int16      char_set;
  48. } MochaStream;
  49.  
  50. typedef struct {
  51.     int32             ref_count;
  52.     ActiveEntry     * active_entry;
  53.     PRPackedBool      is_valid;
  54.     PRPackedBool      eval_what;
  55.     PRPackedBool      single_shot;
  56.     PRPackedBool      polling;
  57.     char            * str;
  58.     size_t            len;
  59.     MWContext       * context;
  60.     int               status;
  61.     char            * wysiwyg_url;
  62.     char        * base_href;
  63.     NET_StreamClass * stream;
  64. } MochaConData;
  65.  
  66. #define HOLD_CON_DATA(con_data) ((con_data)->ref_count++)
  67. #define DROP_CON_DATA(con_data) {                                             \
  68.     if (--(con_data)->ref_count == 0)                                         \
  69.     free_con_data(con_data);                                              \
  70. }
  71.  
  72. static void
  73. free_con_data(MochaConData * con_data)
  74. {
  75.     XP_FREEIF(con_data->str);
  76.     XP_FREEIF(con_data->wysiwyg_url);
  77.     XP_FREEIF(con_data->base_href);
  78.     XP_FREE(con_data);
  79. }
  80.  
  81. #define START_POLLING(ae, con_data) {                                         \
  82.     (con_data)->polling = TRUE;                                               \
  83.     NET_SetCallNetlibAllTheTime((ae)->window_id, "mkmocha");                              \
  84. }
  85.  
  86. #define STOP_POLLING(ae, con_data) {                                          \
  87.     NET_ClearCallNetlibAllTheTime((ae)->window_id, "mkmocha");                        \
  88.     (con_data)->polling = FALSE;                                              \
  89. }
  90.  
  91. /* forward decl */
  92. PRIVATE int32 net_ProcessMocha(ActiveEntry * ae);
  93.  
  94. /*
  95.  * Add the new bits to our buffer
  96.  */
  97. PRIVATE int
  98. mocha_process(NET_StreamClass *stream, const char *str, int32 len)
  99. {
  100.     MochaStream * mocha_stream = (MochaStream *) stream->data_object;
  101.  
  102.     mocha_stream->length += len;
  103.     if (!mocha_stream->buffer) {
  104.         mocha_stream->buffer = (char *)XP_ALLOC(mocha_stream->length);
  105.     } 
  106.     else {
  107.         mocha_stream->buffer = (char *)XP_REALLOC(mocha_stream->buffer, 
  108.                           mocha_stream->length);
  109.     }
  110.     if (!mocha_stream->buffer) {
  111.         return MK_OUT_OF_MEMORY;
  112.     }
  113.     memcpy(mocha_stream->buffer + mocha_stream->offset, str, len);
  114.     mocha_stream->offset += len;
  115.     return len;
  116. }
  117.  
  118. PRIVATE unsigned int
  119. mocha_ready(NET_StreamClass *stream)
  120. {
  121.     return MAX_WRITE_READY;  /* always ready for writing */
  122. }
  123.  
  124.  
  125. /*
  126.  * All of the processing for this needs to be done in the mocha thread
  127.  */
  128. PRIVATE void 
  129. mocha_complete(NET_StreamClass *stream)
  130. {
  131.     void * data;
  132.     JSBool isUnicode = JS_FALSE;
  133.  
  134.     MochaStream * mocha_stream = (MochaStream *) stream->data_object;
  135.  
  136.     if (mocha_stream->char_set != CS_DEFAULT) {
  137.     uint32 len;
  138.     INTL_Unicode * unicode;
  139.  
  140.     /* find out how many unicode characters we'll end up with */
  141.     len = INTL_TextToUnicodeLen(mocha_stream->char_set, 
  142.                     (unsigned char *) mocha_stream->buffer,
  143.                     mocha_stream->length);
  144.     unicode = XP_ALLOC(sizeof(INTL_Unicode) * len);
  145.     if (!unicode)
  146.         return;
  147.  
  148.     /* do the conversion */
  149.     mocha_stream->length = INTL_TextToUnicode(mocha_stream->char_set,
  150.                           (unsigned char *) mocha_stream->buffer,
  151.                           mocha_stream->length,
  152.                           unicode,
  153.                           len);
  154.  
  155.     data = unicode;
  156.     isUnicode = JS_TRUE;
  157.  
  158.     XP_FREE(mocha_stream->buffer);
  159.     mocha_stream->buffer = NULL;
  160.     }
  161.     else {
  162.     data = mocha_stream->buffer;
  163.     }
  164.  
  165.     /* this will grab ownership of data */
  166.     ET_MochaStreamComplete(mocha_stream->context, data, 
  167.                mocha_stream->length, 
  168.                mocha_stream->content_type,
  169.                isUnicode);
  170.  
  171.     XP_FREEIF(mocha_stream->content_type);
  172.     XP_FREE(mocha_stream);
  173.  
  174. }
  175.  
  176. PRIVATE void 
  177. mocha_abort(NET_StreamClass *stream, int status)
  178. {
  179.     MochaStream * mocha_stream = (MochaStream *) stream->data_object;
  180.  
  181.     ET_MochaStreamAbort(mocha_stream->context, status);
  182.     XP_FREE(mocha_stream->buffer);
  183.     XP_FREEIF(mocha_stream->content_type);
  184.     XP_FREE(mocha_stream);
  185. }
  186.  
  187. int16
  188. net_check_for_charset(URL_Struct *url_struct)
  189. {
  190.     int i, max;
  191.     char *key, *value;
  192.     static char charset[] = "charset=";
  193.     int len = XP_STRLEN(charset);
  194.     
  195.     max = url_struct->all_headers.empty_index;
  196.  
  197.     for (i = 0; i < max; i++) {
  198.     key = url_struct->all_headers.key[i];
  199.  
  200.     /* keep looking until we find the content type one */
  201.     if (XP_STRCASECMP(key, "Content-type"))
  202.         continue;
  203.  
  204.     value = url_struct->all_headers.value[i];
  205.  
  206.     /* don't bother unless this is a JS file to begin with */
  207.     if (!strcasestr(value, APPLICATION_JAVASCRIPT))
  208.         return CS_DEFAULT;
  209.  
  210.     value = XP_STRTOK(value, ";");
  211.     while (value) {
  212.         value = XP_StripLine(value);
  213.  
  214.         if (!strncasecomp(value, charset, len)) {
  215.         value += len;
  216.         value = XP_StripLine(value);
  217.         return (INTL_CharSetNameToID(value));
  218.         }
  219.  
  220.         /* move to next arg */
  221.         value = XP_STRTOK(NULL, ";");
  222.  
  223.     }
  224.  
  225.     /* found content-type but no charset */
  226.     return CS_DEFAULT;
  227.  
  228.     }
  229.  
  230.     /* didn't find content-type */
  231.     return CS_DEFAULT;
  232. }
  233.  
  234. static char *
  235. getOriginFromURLStruct(MWContext *context, URL_Struct *url_struct) 
  236. {
  237.     char *referer;
  238.     /*
  239.      * Look in url_struct->origin_url for this javascript: URL's
  240.      * original referrer.
  241.      *
  242.      * In the basis case, a javascript: or *.js URL targets a context
  243.      * from a (the same, or not) context loaded with a non-javascript URL.
  244.      * The referring document's URL is in url_struct->referer and we
  245.      * duplicate it as origin_url.  If we find a non-null origin_url
  246.      * later, we know by induction where it came from.
  247.      */
  248.     referer = url_struct->origin_url;
  249.     if (referer == NULL) {
  250.         /*
  251.          * url_struct->referer (sic) tells the URL of the page in
  252.          * which a javascript: or *.js link or form was clicked or submitted.
  253.          * If it's non-null, but the context is a frame cell that's
  254.          * being (re-)created for this load, don't use referer as the
  255.          * decoder's source URL for the evaluation, because the FE has
  256.          * arbitrarily set it to the top frameset's URL.  Instead, use
  257.          * the immediate parent frameset context's wysiwyg URL to get
  258.          * the origin of the generator (or the parent's address URL if
  259.          * not wysiwyg).
  260.          *
  261.          * If referer is null, the user typed this javascript: URL or
  262.          * its frameset's URL directly into the Location toolbar.
  263.          */
  264.         referer = url_struct->referer;
  265.         if (referer) {
  266.         lo_GridRec *grid = NULL;
  267.  
  268.         if (context->is_grid_cell &&
  269.             !lo_ContextToCell(context, FALSE, &grid)) {
  270.             /*
  271.              * Context is a frame being (re)created: veto referer.
  272.              * The javascript: or *.js URL can't be a LAYER SRC= URL in a
  273.              * frame because the frame's cell must point to its
  274.              * context by the time the first tag (even LAYER) is
  275.              * processed by layout.
  276.              */
  277.             referer = NULL;
  278.         }
  279.         }
  280.         if (!referer) {
  281.         History_entry *he;
  282.  
  283.         if (context->grid_parent) {
  284.             /*
  285.              * If grid parent, use its history entry to get the
  286.              * wysiwyg or real URL, which tells the subject origin
  287.              * of (any code in it that may have generated) this
  288.              * javascript: or *.js URL open.  If no grid parent, this must
  289.              * be a javascript: or *.js URL that was typed directly into
  290.              * Location.
  291.              */
  292.             context = context->grid_parent;
  293.         }
  294.         he = SHIST_GetCurrent(&context->hist);
  295.         if (!he) {
  296.             referer = lm_unknown_origin_str;
  297.         } else {
  298.             referer = he->wysiwyg_url;
  299.             if (!referer)
  300.             referer = he->address;
  301.         }
  302.         }
  303.     }
  304.  
  305.     XP_ASSERT(referer);
  306.     referer = XP_STRDUP(referer);
  307.     if (!referer) {
  308.     return NULL;
  309.     }
  310.     url_struct->origin_url = referer;
  311.     return referer;
  312. }
  313.  
  314. NET_StreamClass *
  315. NET_CreateMochaConverter(FO_Present_Types format_out,
  316.                          void             *data_object,
  317.                          URL_Struct       *url_struct,
  318.                          MWContext        *context)
  319. {
  320.     MochaStream * mocha_stream;
  321.     NET_StreamClass *stream;
  322.     char *origin;
  323.  
  324.     mocha_stream = (MochaStream *) XP_NEW_ZAP(MochaStream);
  325.     if (!mocha_stream)
  326.     return NULL;
  327.  
  328.     mocha_stream->context = context;
  329.     mocha_stream->content_type = XP_STRDUP(url_struct->content_type);
  330.     mocha_stream->char_set = net_check_for_charset(url_struct);
  331.  
  332.     /* Get the origin from the URL struct. We don't have to free origin
  333.      * here because url_struct->origin_url owns it.
  334.      */
  335.     origin = getOriginFromURLStruct(context, url_struct);
  336.     if (origin == NULL)
  337.         return NULL;
  338.  
  339.     if (NET_URL_Type(url_struct->address) == FILE_TYPE_URL &&
  340.         NET_URL_Type(origin) != FILE_TYPE_URL)
  341.     {
  342.         /*
  343.          * Don't allow loading a file: URL from a non-file URL to
  344.          * prevent privacy attacks against the local machine from
  345.          * web pages.
  346.          */
  347.         return NULL;
  348.     }
  349.  
  350.     stream = NET_NewStream("mocha", mocha_process, mocha_complete, 
  351.                mocha_abort, mocha_ready, mocha_stream, 
  352.                context); 
  353.     return stream;
  354. }
  355.  
  356. PRIVATE void
  357. mk_mocha_eval_exit_fn(void * data, char * str, size_t len, char * wysiwyg_url, 
  358.               char * base_href, Bool valid)
  359. {
  360.     MochaConData * con_data = (MochaConData *) data;
  361.  
  362.     if (!valid) {
  363.         con_data->status = MK_MALFORMED_URL_ERROR;
  364.         con_data->is_valid = TRUE;
  365.         return;
  366.     }
  367.     if (str == NULL) {
  368.         con_data->status = MK_DATA_LOADED;
  369.         con_data->is_valid = TRUE;
  370.         return;
  371.     }
  372.  
  373.     con_data->base_href = base_href;
  374.     con_data->wysiwyg_url = wysiwyg_url;
  375.     con_data->str = str;
  376.     con_data->len = len;
  377.     con_data->is_valid = TRUE;
  378.  
  379. }
  380.  
  381. /*
  382.  * Handle both 'mocha:<stuff>' urls and the mocha type-in widget
  383.  */
  384. MODULE_PRIVATE int32
  385. net_MochaLoad(ActiveEntry *ae)
  386. {
  387.     MWContext *context;
  388.     URL_Struct *url_struct;
  389.     char *what;
  390.     Bool eval_what, single_shot;
  391.     MochaConData * con_data;
  392.  
  393.     context = ae->window_id;
  394.     url_struct = ae->URL_s;
  395.     what = XP_STRCHR(url_struct->address, ':');
  396.     XP_ASSERT(what);
  397.     what++;
  398.     eval_what = FALSE;
  399.     single_shot = (*what != '?');
  400.  
  401.     if (single_shot) {
  402.         /* Don't use an existing Mocha output window's stream. */
  403.         if (*what == '\0') {
  404.             /* Generate two grid cells, one for output and one for input. */
  405.             what = PR_smprintf("<frameset rows=\"75%%,25%%\">\n"
  406.                                "<frame name=MochaOutput src=about:blank>\n"
  407.                                "<frame name=MochaInput src=%.*s#input>\n"
  408.                                "</frameset>",
  409.                                what - url_struct->address,
  410.                                url_struct->address);
  411.         } else if (!XP_STRCMP(what, "#input")) {
  412.             /* The input cell contains a form with one magic isindex field. */
  413.             what = PR_smprintf("<b>%.*s typein</b>\n"
  414.                                "<form action=%.*s target=MochaOutput"
  415.                                " onsubmit='this.isindex.focus()'>\n"
  416.                                "<input name=isindex size=60>\n"
  417.                                "</form>",
  418.                                what - url_struct->address - 1,
  419.                                url_struct->address,
  420.                                what - url_struct->address,
  421.                                url_struct->address);
  422.             url_struct->internal_url = TRUE;
  423.         } else {
  424.             eval_what = TRUE;
  425.         }
  426.     } else {
  427.         /* Use the Mocha output window if it exists. */
  428.         url_struct->auto_scroll = 1000;
  429.  
  430.         /* Skip the leading question-mark and clean up the string. */
  431.         what++;
  432.         NET_PlusToSpace(what);
  433.         NET_UnEscape(what);
  434.         eval_what = TRUE;
  435.     }
  436.  
  437.     /* something got hosed.  bail */
  438.     if (!what) {
  439.         ae->status = MK_OUT_OF_MEMORY;
  440.     return -1;
  441.     }
  442.  
  443.     /* make space for the connection data */
  444.     con_data = XP_NEW_ZAP(MochaConData);
  445.     if (!con_data) {
  446.         ae->status = MK_OUT_OF_MEMORY;
  447.         return -1;
  448.     }
  449.  
  450.     /* remember for next time */
  451.     con_data->ref_count = 1;
  452.     con_data->active_entry = ae;
  453.     ae->con_data = con_data;
  454.  
  455.     /* set up some state so we remember where we are */
  456.     con_data->eval_what = eval_what;
  457.     con_data->single_shot = single_shot;
  458.     con_data->context = context;
  459.  
  460.     /* fake out netlib so we don't select on the socket id */
  461.     ae->socket = NULL;
  462.     ae->local_file = TRUE;
  463.  
  464.     if (eval_what) {
  465.     char *referer;
  466.         JSPrincipals *principals;
  467.     ETEvalStuff *stuff;
  468.  
  469.         referer = getOriginFromURLStruct(context, url_struct);
  470.     if (!referer) {
  471.         ae->status = MK_OUT_OF_MEMORY;
  472.         return -1;
  473.     }
  474.  
  475.     principals = LM_NewJSPrincipals(NULL, NULL, referer);
  476.     if (!principals) {
  477.         ae->status = MK_OUT_OF_MEMORY;
  478.         return -1;
  479.     }
  480.  
  481.         /* 
  482.          * send the buffer off to be evaluated 
  483.          */
  484.     stuff = (ETEvalStuff *) XP_NEW_ZAP(ETEvalStuff);
  485.     if (!stuff) {
  486.         ae->status = MK_OUT_OF_MEMORY;
  487.         return -1;
  488.     }
  489.  
  490.     stuff->len = XP_STRLEN(what);
  491.     stuff->line_no = 0;
  492.     stuff->scope_to = NULL;
  493.     stuff->want_result = JS_TRUE;
  494.     stuff->data = con_data;
  495.     stuff->version = JSVERSION_UNKNOWN;
  496.     stuff->principals = principals;
  497.  
  498.         ET_EvaluateScript(context, what, stuff, mk_mocha_eval_exit_fn);
  499.     
  500.     }
  501.     else {
  502.  
  503.         /* allocated above, don't need to free */
  504.         con_data->str = what;
  505.     con_data->len = XP_STRLEN(what);
  506.         con_data->is_valid = TRUE;
  507.  
  508.     }
  509.  
  510.     /* make sure netlib gets called so we know when our data gets back */
  511.     START_POLLING(ae, con_data);
  512.  
  513.     /* ya'll come back now, ya'hear? */
  514.     return net_ProcessMocha(ae);
  515.  
  516. }
  517.  
  518. PRIVATE int
  519. net_process_mocha(MochaConData * con_data)
  520. {
  521.     int32 ref_count;
  522.  
  523.     ref_count = con_data->ref_count;
  524.     DROP_CON_DATA(con_data);
  525.     if (ref_count == 1 || !con_data->active_entry)
  526.     return -1;
  527.     return net_ProcessMocha(con_data->active_entry);
  528. }
  529.  
  530. static char nscp_open_tag[] = "<" PT_NSCP_OPEN ">";
  531.  
  532. /*
  533.  * If the mocha has finished evaluation shove the result
  534.  *   down the stream and continue else just wait
  535.  */
  536. PRIVATE int32
  537. net_ProcessMocha(ActiveEntry * ae)
  538. {
  539.     FO_Present_Types output_format;
  540.     Bool to_layout;
  541.     Bool first_time;
  542.     NET_StreamClass *stream = NULL;
  543.     MochaConData * con_data = (MochaConData *) ae->con_data;
  544.     MWContext * context;
  545.     int status;
  546.  
  547.     /* if we haven't gotten our data yet just return and wait */
  548.     if (!con_data || !con_data->is_valid)
  549.         return 0;
  550.  
  551.     context = ae->window_id;
  552.  
  553.     /*
  554.      * Race with the mocha thread until we can grab the JSLock
  555.      */
  556.     if (!con_data->single_shot) {
  557.     MochaDecoder * decoder;
  558.  
  559.     HOLD_CON_DATA(con_data);
  560.     if (!LM_AttemptLockJS((JSLockReleaseFunc)net_process_mocha, con_data))
  561.         return 0;
  562.     DROP_CON_DATA(con_data);
  563.     decoder = LM_GetMochaDecoder(context);
  564.     if (!decoder) {
  565.         LM_UnlockJS();
  566.         ae->status = MK_OUT_OF_MEMORY;
  567.         goto done;
  568.     }
  569.         stream = decoder->stream;
  570.         LM_PutMochaDecoder(decoder);
  571.     LM_UnlockJS();
  572.     }
  573.     else {
  574.     stream = con_data->stream;
  575.     }
  576.  
  577.     /* we've gotten valid data, clear the callme all the time flag */
  578.     STOP_POLLING(ae, con_data);
  579.  
  580.     /* see if there were any problems */
  581.     if (con_data->status < 0 || con_data->str == NULL) {
  582.     ET_SendLoadEvent(context, EVENT_LOAD, NULL, NULL, 
  583.                          LO_DOCUMENT_LAYER_ID, FALSE);
  584.         ae->status = con_data->status;
  585.     goto done;
  586.     }
  587.  
  588.     /*
  589.      * If we don't already have a stream to take our output create one now
  590.      */
  591.     first_time = !stream;
  592.     if (first_time) {
  593.         StrAllocCopy(ae->URL_s->content_type, TEXT_HTML);
  594.         ae->format_out = CLEAR_CACHE_BIT(ae->format_out);
  595.  
  596.         stream = NET_StreamBuilder(ae->format_out, ae->URL_s, ae->window_id);
  597.         if (!stream) {
  598.             ae->status = MK_UNABLE_TO_CONVERT;
  599.             goto done;
  600.         }
  601.     }
  602.  
  603.     /*
  604.      * If the stream we just created isn't ready for writing just
  605.      *   hold onto the stream and try again later
  606.      */
  607.     if (!stream->is_write_ready(stream)) {
  608.     con_data->stream = stream;
  609.     START_POLLING(ae, con_data);
  610.     return 0;
  611.     }
  612.  
  613.     /* XXX this condition is fairly bogus */
  614.     output_format = CLEAR_CACHE_BIT(ae->format_out);
  615.     to_layout = (output_format != FO_INTERNAL_IMAGE &&
  616.          output_format != FO_MULTIPART_IMAGE &&
  617.          output_format != FO_EMBED &&
  618.          output_format != FO_PLUGIN);
  619.  
  620.     if (to_layout) {
  621.     /* The string must end in a newline so the parser will flush it.  */
  622.     if (con_data->len != 0 && con_data->str[con_data->len-1] != '\n') {
  623.         size_t new_len = con_data->len + 1;
  624.         char * new_str = XP_ALLOC((new_len + 1) * sizeof(char));
  625.  
  626.         if (!new_str) {
  627.         ae->status = MK_OUT_OF_MEMORY;
  628.         goto done;
  629.         }
  630.         XP_MEMCPY(new_str, con_data->str, con_data->len);
  631.         new_str[new_len-1] = '\n';
  632.         new_str[new_len] = '\0';
  633.         con_data->str = new_str;
  634.         con_data->len = new_len;
  635.     }
  636.     }
  637.  
  638.  
  639.     /*
  640.      * If this is the first time do some initial setup.  We'll set
  641.      *   the window title and maybe shove out a <BASE> tag
  642.      */
  643.     status = 0;
  644.     if (to_layout && first_time && con_data->eval_what) {
  645.  
  646.     /* Feed layout an internal tag so it will create a new top state. */
  647.     stream->put_block(stream, nscp_open_tag,
  648.               sizeof nscp_open_tag - 1);
  649.  
  650.         (void) LM_WysiwygCacheConverter(context, ae->URL_s, 
  651.                     con_data->wysiwyg_url,
  652.                     con_data->base_href);
  653.  
  654.         if (*con_data->str != '<') {
  655.             char * prefix = NULL;
  656.             StrAllocCopy(prefix, "<TITLE>");
  657.             StrAllocCat(prefix, ae->URL_s->address);
  658.             StrAllocCat(prefix, "</TITLE><PLAINTEXT>");
  659.             if (!prefix) {
  660.         ae->status = MK_OUT_OF_MEMORY;
  661.         goto done;
  662.         }
  663.             status = (*stream->put_block)(stream, prefix,
  664.                                           XP_STRLEN(prefix));
  665.             XP_FREE(prefix);
  666.         }
  667.     else {
  668.         if (con_data->base_href) {
  669.         status = (*stream->put_block)(stream, 
  670.                               con_data->base_href,
  671.                           XP_STRLEN(con_data->base_href));
  672.         }
  673.     }
  674.     }
  675.      
  676.     if (status >= 0) {
  677.         status = (*stream->put_block)(stream, con_data->str,
  678.                                       (int32)con_data->len);
  679.     }
  680.  
  681.     if (con_data->single_shot && status >= 0)
  682.         (*stream->complete)(stream);
  683.  
  684.     if (!con_data->single_shot && status >= 0) {
  685.         if (first_time) {
  686.         ET_SetDecoderStream(context, stream, ae->URL_s, JS_TRUE);
  687.         goto done;
  688.         }
  689.     } else {
  690.         if (status < 0)
  691.             (*stream->abort)(stream, status);
  692.         if (first_time)
  693.             XP_DELETE(stream);
  694.     }
  695.  
  696.     ae->status = MK_DATA_LOADED;
  697.  
  698. done:
  699.     ae->con_data = NULL;
  700.     con_data->active_entry = NULL;
  701.     DROP_CON_DATA(con_data);
  702.     return -1;
  703.  
  704. }
  705.  
  706. PRIVATE int32
  707. net_InterruptMocha(ActiveEntry *ae)
  708. {
  709.     MochaConData * con_data = (MochaConData *) ae->con_data;
  710.  
  711.     if (!con_data)
  712.     return MK_INTERRUPTED;
  713.  
  714.     /* do we need to decrement the callme all the time flag? */
  715.     if (con_data->polling) {
  716.     STOP_POLLING(ae, con_data);
  717.     }
  718.  
  719.     /* ae is about to go away, break its connection with con_data */
  720.     ae->con_data = NULL;
  721.     con_data->active_entry = NULL;
  722.  
  723.     /* ae is about to go away, better get it off the JS lock waiters list */
  724.     if (LM_ClearAttemptLockJS((JSLockReleaseFunc)net_process_mocha, con_data))
  725.     DROP_CON_DATA(con_data);
  726.  
  727.     return ae->status = MK_INTERRUPTED;
  728. }
  729.  
  730. PRIVATE void
  731. net_CleanupMocha(void)
  732. {
  733.     /* nothing so far needs freeing */
  734.     return;
  735. }
  736.  
  737. MODULE_PRIVATE void
  738. NET_InitMochaProtocol(void)
  739. {
  740.         static NET_ProtoImpl mocha_proto_impl;
  741.  
  742.         mocha_proto_impl.init = net_MochaLoad;
  743.         mocha_proto_impl.process = net_ProcessMocha;
  744.         mocha_proto_impl.interrupt = net_InterruptMocha;
  745.         mocha_proto_impl.cleanup = net_CleanupMocha;
  746.  
  747.         NET_RegisterProtocolImplementation(&mocha_proto_impl, MOCHA_TYPE_URL);
  748. }
  749.  
  750.