home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libmime / mimemoz.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  72.6 KB  |  2,301 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. /* mimemoz.c --- Mozilla interface to libmime.a
  20.    Created: Jamie Zawinski <jwz@netscape.com>, 15-May-96.
  21.  */
  22.  
  23. #include "xp.h"
  24. #include "libi18n.h"
  25. #include "xp_time.h"
  26. #include "msgcom.h"
  27. #include "mimeobj.h"
  28. #include "mimemsg.h"
  29. #include "mimetric.h"   /* for MIME_RichtextConverter */
  30. #include "mimethtm.h"
  31. #include "mimemsig.h"
  32. #include "mimecryp.h"
  33. #ifndef MOZILLA_30
  34. # include "xpgetstr.h"
  35. # include "mimevcrd.h"  /* for MIME_VCardConverter */
  36. # include "edt.h"
  37.   extern int XP_FORWARDED_MESSAGE_ATTACHMENT;
  38. #endif /* !MOZILLA_30 */
  39. #include "prefapi.h"
  40.  
  41. #include "secrng.h"
  42. #include "prprf.h"
  43. #include "intl_csi.h"
  44.  
  45. #ifdef HAVE_MIME_DATA_SLOT
  46. # define LOCK_LAST_CACHED_MESSAGE
  47. #endif
  48.  
  49. /* Interface between netlib and the top-level message/rfc822 parser:
  50.    MIME_MessageConverter()
  51.  */
  52.  
  53. struct mime_stream_data {           /* This struct is the state we pass around
  54.                                        amongst the various stream functions
  55.                                        used by MIME_MessageConverter().
  56.                                      */
  57.  
  58.   URL_Struct *url;                  /* The URL this is all coming from. */
  59.   int format_out;
  60.   MWContext *context;
  61.   NET_StreamClass *stream;          /* The stream to which we write output */
  62.   NET_StreamClass *istream;   /* The stream we're writing out image data,
  63.                                                                   if any. */
  64.   MimeObject *obj;                  /* The root parser object */
  65.   MimeDisplayOptions *options;      /* Data for communicating with libmime.a */
  66.  
  67.   /* These are used by FO_QUOTE_HTML_MESSAGE stuff only: */
  68.   int16 lastcsid;                   /* csid corresponding to above. */
  69.   int16 outcsid;                    /* csid passed to EDT_PasteQuoteINTL */
  70.  
  71. #ifndef MOZILLA_30
  72.   uint8 rand_buf[6];                /* Random number used in the MATCH
  73.                                        attribute of the ILAYER tag
  74.                                        pair that encapsulates a
  75.                                        text/html part.  (The
  76.                                        attributes must match on the
  77.                                        ILAYER and the closing
  78.                                        /ILAYER.)  This is used to
  79.                                        prevent stray layer tags (or
  80.                                        maliciously placed ones) inside
  81.                                        an email message allowing the
  82.                                        message to escape from its
  83.                                        encapsulated environment. */
  84. #endif /* MOZILLA_30 */
  85.     
  86. #ifdef DEBUG_terry
  87.     XP_File logit;              /* Temp file to put generated HTML into. */
  88. #endif
  89. };
  90.  
  91.  
  92. struct MimeDisplayData {            /* This struct is what we hang off of
  93.                                        MWContext->mime_data, to remember info
  94.                                        about the last MIME object we've
  95.                                        parsed and displayed.  See
  96.                                        MimeGuessURLContentName() below.
  97.                                      */
  98.   MimeObject *last_parsed_object;
  99.   char *last_parsed_url;
  100.  
  101. #ifdef LOCK_LAST_CACHED_MESSAGE
  102.   char *previous_locked_url;
  103. #endif /* LOCK_LAST_CACHED_MESSAGE */
  104.  
  105. #ifndef MOZILLA_30
  106.   MSG_Pane* last_pane;
  107. #endif /* MOZILLA_30 */
  108. };
  109.  
  110.  
  111. #ifndef MOZILLA_30
  112.  
  113. static MimeHeadersState MIME_HeaderType;
  114. static XP_Bool MIME_NoInlineAttachments;
  115. static XP_Bool MIME_WrapLongLines;
  116. static XP_Bool MIME_VariableWidthPlaintext;
  117. static XP_Bool MIME_PrefDataValid = 0; /* 0: First time. */
  118.                                 /* 1: Cache is not valid. */
  119.                                 /* 2: Cache is valid. */
  120.  
  121. #endif
  122.  
  123. /* #### defined in libmsg/msgutils.c */
  124. extern NET_StreamClass * 
  125. msg_MakeRebufferingStream (NET_StreamClass *next_stream,
  126.                            URL_Struct *url,
  127.                            MWContext *context);
  128.  
  129.  
  130. static char *
  131. mime_reformat_date(const char *date, void *stream_closure)
  132. {
  133.   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
  134.   MWContext *context = msd->context;
  135.   const char *s;
  136.   time_t t;
  137.   XP_ASSERT(date);
  138.   if (!date) return 0;
  139.   t = XP_ParseTimeString(date, FALSE);
  140.   if (t <= 0) return 0;
  141.   s = MSG_FormatDateFromContext(context, t);
  142.   if (!s) return 0;
  143.   return XP_STRDUP(s);
  144. }
  145.  
  146.  
  147. static char *
  148. mime_file_type (const char *filename, void *stream_closure)
  149. {
  150.   NET_cinfo *cinfo = NET_cinfo_find_type ((char *) filename);
  151.   if (!cinfo || !cinfo->type)
  152.     return 0;
  153.   else
  154.     return XP_STRDUP(cinfo->type);
  155. }
  156.  
  157. static char *
  158. mime_type_desc(const char *type, void *stream_closure)
  159. {
  160.   NET_cinfo *cinfo = NET_cinfo_find_info_by_type((char *) type);
  161.   if (!cinfo || !cinfo->desc || !*cinfo->desc)
  162.     return 0;
  163.   else
  164.     return XP_STRDUP(cinfo->desc);
  165. }
  166.  
  167.  
  168. static char *
  169. mime_type_icon(const char *type, void *stream_closure)
  170. {
  171.   NET_cinfo *cinfo = NET_cinfo_find_info_by_type((char *) type);
  172.   if (cinfo && cinfo->icon && *cinfo->icon)
  173.     return XP_STRDUP(cinfo->icon);
  174.   else if (!strncasecomp(type, "text/", 5))
  175.     return XP_STRDUP("internal-gopher-text");
  176.   else if (!strncasecomp(type, "image/", 6))
  177.     return XP_STRDUP("internal-gopher-image");
  178.   else if (!strncasecomp(type, "audio/", 6))
  179.     return XP_STRDUP("internal-gopher-sound");
  180.   else if (!strncasecomp(type, "video/", 6))
  181.     return XP_STRDUP("internal-gopher-movie");
  182.   else if (!strncasecomp(type, "application/", 12))
  183.     return XP_STRDUP("internal-gopher-binary");
  184.   else
  185.     return XP_STRDUP("internal-gopher-unknown");
  186. }
  187.  
  188.  
  189. static int
  190. mime_convert_charset (const char *input_line, int32 input_length,
  191.                       const char *input_charset, const char *output_charset,
  192.                       char **output_ret, int32 *output_size_ret,
  193.                       void *stream_closure)
  194. {
  195.   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
  196.   unsigned char *converted;
  197.  
  198.   /* #### */
  199.   extern unsigned char *INTL_ConvMailToWinCharCode(MWContext *context,
  200.                                                    unsigned char *pSrc,
  201.                                                    uint32 block_size);
  202.  
  203.   converted = INTL_ConvMailToWinCharCode(msd->context,
  204.                                          (unsigned char *) input_line,
  205.                                          input_length);
  206.   if (converted)
  207.     {
  208.       *output_ret = (char *) converted;
  209.       *output_size_ret = XP_STRLEN((char *) converted);
  210.     }
  211.   else
  212.     {
  213.       *output_ret = 0;
  214.       *output_size_ret = 0;
  215.     }
  216.   return 0;
  217. }
  218.  
  219.  
  220. static int
  221. mime_convert_rfc1522 (const char *input_line, int32 input_length,
  222.                       const char *input_charset, const char *output_charset,
  223.                       char **output_ret, int32 *output_size_ret,
  224.                       void *stream_closure)
  225. {
  226.   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
  227.   char *converted;
  228.   char *line;
  229.  
  230.   if (input_line[input_length] == 0)  /* oh good, it's null-terminated */
  231.     line = (char *) input_line;
  232.   else
  233.     {
  234.       line = (char *) XP_ALLOC(input_length+1);
  235.       if (!line) return MK_OUT_OF_MEMORY;
  236.       XP_MEMCPY(line, input_line, input_length);
  237.       line[input_length] = 0;
  238.     }
  239.  
  240.   converted = IntlDecodeMimePartIIStr(line, 
  241.       INTL_DocToWinCharSetID(INTL_DefaultDocCharSetID(msd->context)), FALSE);
  242.  
  243.   if (line != input_line)
  244.     XP_FREE(line);
  245.  
  246.   if (converted)
  247.     {
  248.       *output_ret = converted;
  249.       *output_size_ret = XP_STRLEN(converted);
  250.     }
  251.   else
  252.     {
  253.       *output_ret = 0;
  254.       *output_size_ret = 0;
  255.     }
  256.   return 0;
  257. }
  258.  
  259.  
  260. static int
  261. mime_output_fn(char *buf, int32 size, void *stream_closure)
  262. {
  263.   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
  264.   XP_ASSERT(msd->stream);
  265.   if (!msd->stream) return -1;
  266. #ifdef DEBUG_terry
  267.   if (msd->logit) {
  268.       XP_FileWrite(buf, size, msd->logit);
  269.   }
  270. #endif
  271.   return msd->stream->put_block (msd->stream, buf, size);
  272. }
  273.  
  274. static int
  275. compose_only_output_fn(char *buf, int32 size, void *stream_closure)
  276. {
  277.     return 0;
  278. }
  279.  
  280. extern XP_Bool msg_LoadingForComposeOnly(const MSG_Pane* pane); /* in msgmpane.cpp */
  281.  
  282. static int
  283. mime_set_html_state_fn (void *stream_closure,
  284.                         XP_Bool layer_encapsulate_p,
  285.                         XP_Bool start_p,
  286.                         XP_Bool abort_p)
  287. {
  288.   int status = 0;
  289.   char *buf;
  290.  
  291.   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
  292.  
  293. #if 1
  294.   char random_close_tags[] = "</SCRIPT><NSCP_CLOSE>";
  295. #else /* 0 */
  296.   char random_close_tags[] =
  297.         "</TABLE></TABLE></TABLE></TABLE></TABLE></TABLE>"
  298.         "</DL></DL></DL></DL></DL></DL></DL></DL></DL></DL>"
  299.         "</DL></DL></DL></DL></DL></DL></DL></DL></DL></DL>"
  300.         "</B></B></B></B></B></B></B></B></B></B></B></B>"
  301.         "</PRE></PRE></PRE></PRE></PRE></PRE></PRE></PRE>"
  302.         "<BASEFONT SIZE=3></SCRIPT>";
  303. #endif /* 0 */
  304.   if (start_p) {
  305. #ifndef MOZILLA_30
  306.     if (layer_encapsulate_p && msd->options && !msd->options->nice_html_only_p){
  307.       uint8 *rand_buf = msd->rand_buf;
  308.       RNG_GenerateGlobalRandomBytes(rand_buf, sizeof msd->rand_buf);
  309.  
  310.       buf = PR_smprintf("<ILAYER LOCKED CLIP=0,0,AUTO,AUTO "
  311.                         "MATCH=%02x%02x%02x%02x%02x%02x>",
  312.                         rand_buf[0], rand_buf[1], rand_buf[2],
  313.                         rand_buf[3], rand_buf[4], rand_buf[5]);
  314.       if (!buf)
  315.         return MK_OUT_OF_MEMORY;
  316.       status = MimeOptions_write(msd->options, buf, XP_STRLEN(buf), TRUE);
  317.       XP_FREE(buf);
  318.     }
  319. #endif /* MOZILLA_30 */
  320.   } else {
  321.     status = MimeOptions_write(msd->options, random_close_tags,
  322.                                XP_STRLEN(random_close_tags), FALSE);
  323.     if (status < 0)
  324.       return status;
  325.  
  326. #ifndef MOZILLA_30
  327.     if (layer_encapsulate_p && msd->options && !msd->options->nice_html_only_p){
  328.       uint8 *rand_buf = msd->rand_buf;
  329.       buf = PR_smprintf("</ILAYER MATCH=%02x%02x%02x%02x%02x%02x><BR>",
  330.                         rand_buf[0], rand_buf[1], rand_buf[2],
  331.                         rand_buf[3], rand_buf[4], rand_buf[5]);
  332.       if (!buf)
  333.         return MK_OUT_OF_MEMORY;
  334.       status = MimeOptions_write(msd->options, buf, XP_STRLEN(buf), TRUE);
  335.       XP_FREE(buf);
  336.     }
  337. #endif /* MOZILLA_30 */
  338.  
  339.   }
  340.   return status;
  341. }
  342.  
  343.  
  344. static int
  345. mime_display_stream_write (NET_StreamClass *stream,
  346.                            const char* buf,
  347.                            int32 size)
  348. {
  349.   struct mime_stream_data *msd = (struct mime_stream_data *) stream->data_object;
  350.   MimeObject *obj = (msd ? msd->obj : 0);  
  351.   if (!obj) return -1;
  352.   return obj->class->parse_buffer((char *) buf, size, obj);
  353. }
  354.  
  355.  
  356. static unsigned int
  357. mime_display_stream_write_ready (NET_StreamClass *stream)
  358. {
  359.   struct mime_stream_data *msd = (struct mime_stream_data *) stream->data_object;  
  360.   if (msd->istream) {
  361.       return msd->istream->is_write_ready (msd->istream);
  362.   } else if (msd->stream)
  363.     return msd->stream->is_write_ready (msd->stream);
  364.   else
  365.     return (MAX_WRITE_READY);
  366. }
  367.  
  368.  
  369. extern void MSG_MimeNotifyCryptoAttachmentKludge(NET_StreamClass *);
  370.  
  371. static void
  372. mime_display_stream_complete (NET_StreamClass *stream)
  373. {
  374.   struct mime_stream_data *msd = (struct mime_stream_data *) stream->data_object;
  375.   MimeObject *obj = (msd ? msd->obj : 0);  
  376.   if (obj)
  377.     {
  378.       int status;
  379.       status = obj->class->parse_eof(obj, FALSE);
  380.       obj->class->parse_end(obj, (status < 0 ? TRUE : FALSE));
  381.  
  382. #ifdef HAVE_MIME_DATA_SLOT
  383.       if (msd &&
  384.           msd->context &&
  385.           msd->context->mime_data &&
  386.           obj == msd->context->mime_data->last_parsed_object)
  387.         {
  388.           /* do nothing -- we still have another pointer to this object. */
  389.         }
  390.       else
  391. #endif /* HAVE_MIME_DATA_SLOT */
  392.         {
  393.           /* Somehow there's no pointer in context->mime_data to this
  394.              object, so destroy it now.  (This can happen for any number
  395.              of normal reasons; see comment in mime_output_init_fn().)
  396.            */
  397.           XP_ASSERT(msd->options == obj->options);
  398.           mime_free(obj);
  399.           obj = NULL;
  400.           if (msd->options)
  401.             {
  402.               FREEIF(msd->options->part_to_load);
  403.               FREEIF(msd->options->default_charset);
  404.               FREEIF(msd->options->override_charset);
  405.               XP_FREE(msd->options);
  406.               msd->options = 0;
  407.             }
  408.         }
  409.     }
  410.  
  411. #ifdef LOCK_LAST_CACHED_MESSAGE
  412.   /* The code in this ifdef is to ensure that the most-recently-loaded news
  413.      message is locked down in the memory cache.  (There may be more than one
  414.      message cached, but only the most-recently-loaded is *locked*.)
  415.  
  416.      When loading a message, we unlock the previously-locked URL, if any.
  417.      (This happens in mime_make_output_stream().)
  418.  
  419.      When we're done loading a news message, we lock it to prevent it from
  420.      going away (this happens here, in mime_display_stream_complete().  We
  421.      need to do this at the end instead of the beginning so that the document
  422.      is *in* the cache at the time we try to lock it.)
  423.  
  424.      This implementation probably assumes that news messages go into the
  425.      memory cache and never go into the disk cache.  (But maybe that's
  426.      actually not an issue, since if news messages were to go into the
  427.      disk cache, they would be marked as non-session-persistent anyway?)
  428.    */
  429.   if (msd &&
  430.       msd->context &&
  431.       msd->context->mime_data &&
  432.       !msd->context->mime_data->previous_locked_url)
  433.     {
  434.       /* Save a copy of this URL so that we can unlock it next time. */
  435.       msd->context->mime_data->previous_locked_url =
  436.         XP_STRDUP(msd->url->address);
  437.  
  438.       /* Lock this URL in the cache. */
  439.       if (msd->context->mime_data->previous_locked_url)
  440.         NET_ChangeCacheFileLock(msd->url, TRUE);
  441.     }
  442. #endif /* LOCK_LAST_CACHED_MESSAGE */
  443.  
  444.   if (msd->stream)
  445.     {
  446.  
  447.       /* Hold your breath.  This is how we notify compose.c
  448.          that the message was decrypted -- it will set a flag in the
  449.          mime_delivery_state structure, which we don't have access to from
  450.          here. */
  451.       if (obj &&
  452.           obj->options &&
  453.           obj->options->state &&
  454.           obj->options->state->decrypted_p)
  455.         MSG_MimeNotifyCryptoAttachmentKludge(msd->stream);
  456.  
  457.       /* Close the output stream. */
  458.       msd->stream->complete (msd->stream);
  459.       XP_FREE (msd->stream);
  460.     }
  461. #ifdef DEBUG_terry
  462.   if (msd->logit) XP_FileClose(msd->logit);
  463. #endif
  464.   XP_FREE(msd);
  465. }
  466.  
  467. static void
  468. mime_display_stream_abort (NET_StreamClass *stream, int status)
  469. {
  470.   struct mime_stream_data *msd = (struct mime_stream_data *) stream->data_object;
  471.   MimeObject *obj = (msd ? msd->obj : 0);  
  472.   if (obj)
  473.     {
  474.       if (!obj->closed_p)
  475.         obj->class->parse_eof(obj, TRUE);
  476.       if (!obj->parsed_p)
  477.         obj->class->parse_end(obj, TRUE);
  478.  
  479. #ifdef HAVE_MIME_DATA_SLOT
  480.       if (msd &&
  481.           msd->context &&
  482.           msd->context->mime_data &&
  483.           obj == msd->context->mime_data->last_parsed_object)
  484.         {
  485.           /* do nothing -- we still have another pointer to this object. */
  486.         }
  487.       else
  488. #endif /* HAVE_MIME_DATA_SLOT */
  489.         {
  490.           /* Somehow there's no pointer in context->mime_data to this
  491.              object, so destroy it now.  (This can happen for any number
  492.              of normal reasons; see comment in mime_output_init_fn().)
  493.            */
  494.           XP_ASSERT(msd->options == obj->options);
  495.           mime_free(obj);
  496.           if (msd->options)
  497.             {
  498.               FREEIF(msd->options->part_to_load);
  499.               XP_FREE(msd->options);
  500.               msd->options = 0;
  501.             }
  502.         }
  503.     }
  504.  
  505.   if (msd->stream)
  506.     {
  507.       msd->stream->abort (msd->stream, status);
  508.       XP_FREE (msd->stream);
  509.     }
  510.   XP_FREE(msd);
  511. }
  512.  
  513.  
  514.  
  515. #ifndef MOZILLA_30
  516.  
  517. static unsigned int
  518. mime_insert_html_write_ready(NET_StreamClass *stream)
  519. {    
  520.   return MAX_WRITE_READY;
  521. }
  522.  
  523. static int
  524. mime_insert_html_put_block(NET_StreamClass *stream, const char* str, int32 length)
  525. {
  526.   struct mime_stream_data* msd = (struct mime_stream_data*) stream->data_object;
  527.   char* s = (char*) str;
  528.   char c = s[length];  
  529.   XP_ASSERT(msd);
  530.   if (!msd) return -1;
  531.   if (c) {
  532.     s[length] = '\0';
  533.   }
  534.   /* s is in the outcsid encoding at this point. That was done in
  535.    * mime_insert_html_convert_charset */
  536.   EDT_PasteQuoteINTL(msd->context, s, msd->outcsid); 
  537.   if (c) {
  538.     s[length] = c;
  539.   }
  540.   return 0;
  541. }
  542.  
  543.  
  544. static void
  545. mime_insert_html_complete(NET_StreamClass *stream)
  546. {
  547.   struct mime_stream_data* msd = (struct mime_stream_data*) stream->data_object;  
  548.   XP_ASSERT(msd);
  549.   if (!msd) return;
  550.   EDT_PasteQuote(msd->context, "</BLOCKQUOTE>");
  551.   if (msd->format_out == FO_QUOTE_HTML_MESSAGE) {
  552.       int32 eReplyOnTop = 1, nReplyWithExtraLines = 0;
  553.       PREF_GetIntPref("mailnews.reply_on_top", &eReplyOnTop);
  554.       PREF_GetIntPref("mailnews.reply_with_extra_lines", &nReplyWithExtraLines);
  555.       if (0 == eReplyOnTop && nReplyWithExtraLines) {
  556.         for (; nReplyWithExtraLines > 0; nReplyWithExtraLines--)
  557.           EDT_PasteQuote(msd->context, "<BR>");
  558.       }
  559.  
  560.   }
  561.   EDT_PasteQuoteEnd(msd->context);
  562. }
  563.  
  564. static void
  565. mime_insert_html_abort(NET_StreamClass *stream, int status)
  566. {    
  567.   mime_insert_html_complete(stream);
  568. }
  569.  
  570.  
  571. static int
  572. mime_insert_html_convert_charset (const char *input_line, int32 input_length,
  573.                                   const char *input_charset,
  574.                                   const char *output_charset,
  575.                                   char **output_ret, int32 *output_size_ret,
  576.                                   void *stream_closure)
  577. {
  578.   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
  579.   int status;
  580.   INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(msd->context);
  581.   uint16 old_csid = INTL_GetCSIDocCSID(csi);
  582.  
  583.   if (input_charset) {
  584.     msd->lastcsid = INTL_CharSetNameToID((char*) input_charset);
  585.   } else {
  586.     msd->lastcsid = 0;
  587.   }
  588.   if (output_charset) {
  589.     msd->outcsid = INTL_CharSetNameToID((char*) output_charset);
  590.   } else {
  591.     msd->outcsid = 0;
  592.   }
  593.   INTL_SetCSIDocCSID(csi, msd->lastcsid);
  594.   status = mime_convert_charset (input_line, input_length,
  595.                                  input_charset, output_charset,
  596.                                  output_ret, output_size_ret,
  597.                                  stream_closure);
  598.   INTL_SetCSIDocCSID(csi, old_csid);
  599.   return status;
  600. }
  601.  
  602. #endif /* !MOZILLA_30 */
  603.  
  604.  
  605. static NET_StreamClass *
  606. mime_make_output_stream(const char *content_type,
  607.                         const char *charset,
  608.                         const char *content_name,
  609.                         const char *x_mac_type,
  610.                         const char *x_mac_creator,
  611.                         int format_out, URL_Struct *url,
  612.                         MWContext *context,
  613.                         struct mime_stream_data* msd)
  614. {
  615.   /* To make the next stream, fill in the URL with the provided attributes,
  616.      and call NET_StreamBuilder.
  617.  
  618.      But After we've gotten the stream we want, change the URL's type and
  619.      encoding back to what they were before, since things down the line might
  620.      need to know the *original* type.
  621.    */
  622.   NET_StreamClass *stream;
  623.   char *orig_content_type;
  624.   char *orig_encoding;
  625.   char *old_part = 0;
  626.   char *old_part2 = 0;
  627.  
  628. #ifndef MOZILLA_30
  629.   if (format_out == FO_QUOTE_HTML_MESSAGE) {
  630.     /* Special case here.  Make a stream that just jams data directly
  631.        into our editor context.  No calling of NET_StreamBuilder for me;
  632.        I don't really understand it anyway... */
  633.     
  634.     XP_ASSERT(msd);
  635.     if (msd) {
  636.       stream = XP_NEW_ZAP(NET_StreamClass);
  637.       if (!stream) return NULL;
  638.       stream->window_id = context;
  639.       stream->data_object = msd;
  640.       stream->is_write_ready = mime_insert_html_write_ready;
  641.       stream->put_block = mime_insert_html_put_block;
  642.       stream->complete = mime_insert_html_complete;
  643.       stream->abort = mime_insert_html_abort;
  644.       return stream;
  645.     }
  646.   }
  647. #endif /* !MOZILLA_30 */
  648.  
  649.  
  650.   XP_ASSERT(content_type && url);
  651.   if (!content_type || !url)
  652.     return 0;
  653.  
  654.   /* If we're saving a message to disk (raw), then treat the output as unknown
  655.      type.  If we didn't do this, then saving a message would recurse back into
  656.      the parser (because the output has content-type message/rfc822), and we'd
  657.      be back here again in short order...
  658.  
  659.      We don't do this for FO_SAVE_AS_TEXT and FO_SAVE_AS_POSTSCRIPT because
  660.      those work by generating HTML, and then converting that.  Whereas
  661.      FO_SAVE_AS goes directly to disk without another format_out filter
  662.      catching it.
  663.  
  664.      We only fake out the content-type when we're saving a message (mail or
  665.      news) and not when saving random other types.  The reason for this is that
  666.      we only *need* to do this for messages (because those are the only types
  667.      which are registered to come back in here for FO_SAVE_AS); we don't do it
  668.      for other types, because the Mac needs to know the real type to map a
  669.      MIME type to a Mac Creator when writing the file to disk (so that the
  670.      right application will get launched when the file is clicked on, etc.)
  671.  
  672.      [mwelch: I'm not adding FO_EDT_SAVE_IMAGE here, because the editor, to
  673.      the best of my knowledge, never spools out a message per se; such a message 
  674.      would be filed as an attachment (FO_CACHE_AND_MAIL_TO), independent of the 
  675.      editor. In addition, we want to drill down as far as we can within a quoted 
  676.      message, in order to identify whatever part has been requested (usually an 
  677.      image, sound, applet, or other inline data).]
  678.  
  679.      In 3.0b7 and earlier, we did this for *all* types.  On 9-Aug-96 jwz and
  680.      aleks changed this to only do it for message types, which seems to be the
  681.      right thing.  However, since it's very late in the release cycle, this
  682.      change is being done as #ifdef XP_MAC, since the Mac is the only platform
  683.      where it was a problem that we were using octet-stream for all
  684.      attachments.  After 3.0 ships, this should be done on all platforms, not
  685.      just Mac.
  686.    */
  687.   if ((format_out == FO_SAVE_AS || format_out == FO_CACHE_AND_SAVE_AS)
  688.       && (!strcasecomp(content_type, MESSAGE_RFC822) ||
  689.           !strcasecomp(content_type, MESSAGE_NEWS)))
  690.     content_type = APPLICATION_OCTET_STREAM;
  691.  
  692.   orig_content_type = url->content_type;
  693.   orig_encoding     = url->content_encoding;
  694.  
  695.   url->content_type = XP_STRDUP(content_type);
  696.   if (!url->content_type) return 0;
  697.   url->content_encoding = 0;
  698.  
  699.   if (charset)       FREEIF(url->charset);
  700.   if (content_name)  FREEIF(url->content_name);
  701.   if (x_mac_type)    FREEIF(url->x_mac_type);
  702.   if (x_mac_creator) FREEIF(url->x_mac_creator);
  703.   if (charset)       url->charset       = XP_STRDUP(charset);
  704.   if (content_name)  url->content_name  = XP_STRDUP(content_name);
  705.   if (x_mac_type)    url->x_mac_type    = XP_STRDUP(x_mac_type);
  706.   if (x_mac_creator) url->x_mac_creator = XP_STRDUP(x_mac_creator);
  707.  
  708.   /* If we're going back down into the message/rfc822 parser (that is, if
  709.      we're displaying a sub-part which is itself a message part) then remove
  710.      any part specifier from the URL.  This is to prevent that same part
  711.      from being retreived a second time, which to the user, would have no
  712.      effect.
  713.  
  714.      Don't do this for all other types, because that might affect image
  715.      cacheing and so on (causing it to cache on the wrong key.)
  716.    */
  717.   if (!strcasecomp(content_type, MESSAGE_RFC822) ||
  718.       !strcasecomp(content_type, MESSAGE_NEWS))
  719.     {
  720.       old_part = XP_STRSTR(url->address, "?part=");
  721.       if (!old_part)
  722.         old_part2 = XP_STRSTR(url->address, "&part=");
  723.  
  724.       if (old_part) *old_part = 0;
  725.       else if (old_part2) *old_part2 = 0;
  726.     }
  727.  
  728.   if(msd && msd->options && msd->options->default_charset)
  729.   {
  730.       INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(context);
  731.       INTL_SetCSIMimeCharset(csi, msd->options->default_charset);
  732.   }
  733.  
  734.   stream = NET_StreamBuilder (format_out, url, context);
  735.   if (stream)
  736.   {
  737.     NET_StreamClass * buffer = msg_MakeRebufferingStream(stream, url, context);
  738.     if (buffer)
  739.       stream = buffer;
  740.   }
  741.  
  742.   /* Put it back -- note that this string is now also pointed to by
  743.      obj->options->url, so we're modifying data reachable by the
  744.      internals of the library (and that is the goal of this hack.) */
  745.   if (old_part) *old_part = '?';
  746.   else if (old_part2) *old_part2 = '&';
  747.  
  748.   FREEIF(url->content_type);
  749.   url->content_type     = orig_content_type;
  750.   url->content_encoding = orig_encoding;
  751.  
  752. #ifdef LOCK_LAST_CACHED_MESSAGE
  753.   /* Always cache this one. */
  754.   url->must_cache = TRUE;
  755.  
  756.   /* Un-cache the last one. */
  757.   if (context->mime_data &&
  758.       context->mime_data->previous_locked_url)
  759.     {
  760.       URL_Struct *url_s =
  761.         NET_CreateURLStruct(context->mime_data->previous_locked_url,
  762.                             NET_NORMAL_RELOAD);
  763.       if (url_s)
  764.         {
  765.           /* Note: if post data was involved here, we'd lose.  We're assuming
  766.              that all we need to save is the url->address. */
  767.           NET_ChangeCacheFileLock(url_s, FALSE);
  768.           NET_FreeURLStruct(url_s);
  769.         }
  770.       XP_FREE(context->mime_data->previous_locked_url);
  771.       context->mime_data->previous_locked_url = 0;
  772.     }
  773. #endif /* LOCK_LAST_CACHED_MESSAGE */
  774.  
  775.   return stream;
  776. }
  777.  
  778.  
  779.  
  780. static int
  781. mime_output_init_fn (const char *type,
  782.                      const char *charset,
  783.                      const char *name,
  784.                      const char *x_mac_type,
  785.                      const char *x_mac_creator,
  786.                      void *stream_closure)
  787. {
  788.   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
  789.   int format_out;
  790.   XP_ASSERT(!msd->stream);
  791.   if (msd->stream) return -1;
  792.  
  793.   XP_ASSERT(type && *type);
  794.   if (!type || !*type) return -1;
  795.  
  796.   format_out = msd->format_out;
  797.  
  798.   /* If we've converted to HTML, then we've already done charset conversion,
  799.      so label this data as "internal/parser" to prevent it from being passed
  800.      through the charset converters again. */
  801.   if (msd->options->write_html_p &&
  802.       !strcasecomp(type, TEXT_HTML))
  803.     type = INTERNAL_PARSER;
  804.  
  805.  
  806.   /* If this stream converter was created for FO_MAIL_TO, then the input type
  807.      and output type will be identical (message/rfc822) so we need to change
  808.      the format_out to break out of the loop.  libmsg/compose.c knows to treat
  809.      FO_MAIL_MESSAGE_TO roughly the same as FO_MAIL_TO.
  810.    */
  811. #ifdef FO_MAIL_MESSAGE_TO
  812.   if (format_out == FO_MAIL_TO)
  813.     format_out = FO_MAIL_MESSAGE_TO;
  814.   else if (format_out == FO_CACHE_AND_MAIL_TO)
  815.     format_out = FO_CACHE_AND_MAIL_MESSAGE_TO;
  816. #endif /* FO_MAIL_MESSAGE_TO */
  817.  
  818.  
  819.   msd->stream = mime_make_output_stream(type, charset, name,
  820.                                         x_mac_type, x_mac_creator,
  821.                                         format_out, msd->url,
  822.                                         msd->context, msd);
  823.  
  824.   if (!msd->stream)
  825.     /* #### We can't return MK_OUT_OF_MEMORY here because sometimes
  826.        NET_StreamBuilder() returns 0 because it ran out of memory; and
  827.        sometimes it returns 0 because the user hit Cancel on the file
  828.        save dialog box.  Wonderful condition handling we've got... */
  829.     return -1;
  830.  
  831.  
  832. #ifdef HAVE_MIME_DATA_SLOT
  833.   /* At this point, the window has been cleared; so discard the cached
  834.      data relating to the previously-parsed MIME object. */
  835.  
  836.   XP_ASSERT(msd && msd->obj && msd->context);
  837.   if (msd && msd->obj && msd->context)
  838.     {
  839.       MWContext *context = msd->context;
  840.  
  841.       if (msd->context->mime_data &&
  842.           msd->context->mime_data->last_parsed_object)
  843.         {
  844.           XP_ASSERT(msd->options !=
  845.                     context->mime_data->last_parsed_object->options);
  846.           XP_ASSERT(msd->obj != context->mime_data->last_parsed_object);
  847.  
  848.           /* There are two interesting cases:
  849.  
  850.              Look at message-1, then look at message-2; and
  851.              Look at message-1, then look at message-1-part-A
  852.               (where part-A is itself a message.)
  853.  
  854.             In the first case, by the time we've begun message-2, we're done
  855.             with message-1.  Its object is still around for reference (for
  856.             examining the part names) but the parser has run to completion
  857.             on it.
  858.  
  859.             In the second case, we begin parsing part-A before the parser of
  860.             message-1 has run to completion -- in fact, the parser that is
  861.             operating on message-1 is still on the stack above us.
  862.  
  863.             So if there is a last_parsed_object, but that object does not have
  864.             its `parsed_p' slot set, then that means that that object has not
  865.             yet hit the `parse_eof' state; therefore, it is still *being*
  866.             parsed: it is a parent object of the one we're currently looking
  867.             at.  (When presenting a part of type message/rfc822, the
  868.             MIME_MessageConverter stream is entered recursively, the first
  869.             time to extract the sub-message, and the second time to convert
  870.             that sub-message to HTML.)
  871.  
  872.             So if we're in this nested call, we can simply replace the pointer
  873.             in mime_data.  When current MessageConverter stream reaches its
  874.             `complete' or `abort' methods, it will skip freeing the current
  875.             object (since there is a pointer to it in mime_data); and then when
  876.             the outer MessageConverter stream reaches its `complete' or `abort'
  877.             methods, it will free that outer object (since it will see that
  878.             there is no pointer to it in mime_data.)
  879.  
  880.             More precisely:
  881.  
  882.             Look at message-1, then look at message-2:
  883.               In this case, the flow of control is like this:
  884.  
  885.               = make object-1 (message/rfc822)
  886.               = parse it
  887.               = save it in context->mime_data
  888.               = done with object-1; free nothing.
  889.  
  890.               = make object-2 (message/rfc822)
  891.               = parse it
  892.               = note that object-1 is still in context->mime_data
  893.               = free object-1
  894.               = save object-2 in context->mime_data
  895.               = done with object-2; free nothing.
  896.             
  897.             Look at message-1, then look at message-1-part-A:
  898.               The flow of control in this case is somewhat different:
  899.  
  900.               = make object-1 (message/rfc822)
  901.               = parse it
  902.               = save it in context->mime_data
  903.               = done with object-1; free nothing.
  904.  
  905.               = make object-1 (message/rfc822)
  906.               = parse it
  907.               = note that previous-object-1 is still in context->mime_data
  908.               = free previous-object-1
  909.               = save object-1 in context->mime_data
  910.               = enter the parser recursively:
  911.                  = make part-A (message/rfc822)
  912.                  = parse it
  913.                  = note that object1 is still in context->mime_data,
  914.                    and has not yet been fully parsed (in other words,
  915.                    it's still on the stack.)  Don't touch it.
  916.                  = save part-A in context->mime_data
  917.                    (cutting the pointer to object-1)
  918.                  = done with part-A; free nothing.
  919.               = done with object-1;
  920.                 note that object-1 is *not* in the context->mime_data
  921.               = free object-1
  922.               = result: part-A remains in context->mime_data
  923.             */
  924.  
  925.           if (context->mime_data->last_parsed_object->parsed_p)
  926.             {
  927.               /* Free it if it's parsed.
  928.  
  929.                  Note that we have to call mime_free() before we free the
  930.                  options struct, not after -- so since mime_free() frees
  931.                  the object, we have to pull `options' out first to avoid
  932.                  reaching into freed memory.
  933.                */
  934.               MimeDisplayOptions *options =
  935.                 context->mime_data->last_parsed_object->options;
  936.  
  937.               mime_free(context->mime_data->last_parsed_object);
  938.               if (options)
  939.                 {
  940.                   FREEIF(options->part_to_load);
  941.                   XP_FREE(options);
  942.                 }
  943.             }
  944.  
  945.           /* Cut the old saved pointer, whether its parsed or not. */
  946.           context->mime_data->last_parsed_object = 0;
  947.           FREEIF(context->mime_data->last_parsed_url);
  948.         }
  949.  
  950.       /* And now save away the current object, for consultation the first
  951.          time a link is clicked upon. */
  952.       if (!context->mime_data)
  953.         {
  954.           context->mime_data = XP_NEW(struct MimeDisplayData);
  955.           if (!context->mime_data)
  956.             return MK_OUT_OF_MEMORY;
  957.           XP_MEMSET(context->mime_data, 0, sizeof(*context->mime_data));
  958.         }
  959.       context->mime_data->last_parsed_object = msd->obj;
  960.       context->mime_data->last_parsed_url = XP_STRDUP(msd->url->address);
  961. #ifndef MOZILLA_30
  962.       context->mime_data->last_pane = msd->url->msg_pane;
  963. #endif /* MOZILLA_30 */
  964.  
  965.       XP_ASSERT(!msd->options ||
  966.                 msd->options == msd->obj->options);
  967.     }
  968. #endif /* HAVE_MIME_DATA_SLOT */
  969.  
  970.   return 0;
  971. }
  972.  
  973.  
  974. static void *mime_image_begin(const char *image_url, const char *content_type,
  975.                               void *stream_closure);
  976. static void mime_image_end(void *image_closure, int status);
  977. static char *mime_image_make_image_html(void *image_data);
  978. static int mime_image_write_buffer(char *buf, int32 size, void *image_closure);
  979.  
  980. #ifdef MOZILLA_30
  981. extern XP_Bool MSG_VariableWidthPlaintext;
  982. #endif
  983.  
  984.  
  985. #ifndef MOZILLA_30
  986. int PR_CALLBACK
  987. mime_PrefsChangeCallback(const char* prefname, void* data)
  988. {
  989.   MIME_PrefDataValid = 1;       /* Invalidates our cached stuff. */
  990.   return PREF_NOERROR;
  991. }
  992. #endif /* !MOZILLA_30 */
  993.  
  994.  
  995. NET_StreamClass * 
  996. MIME_MessageConverter (int format_out, void *closure,
  997.                        URL_Struct *url, MWContext *context)
  998. {
  999.   int status = 0;
  1000.   MimeObject *obj;
  1001.   struct mime_stream_data *msd;
  1002.   NET_StreamClass *stream = 0;
  1003.  
  1004. #ifdef FO_MAIL_MESSAGE_TO
  1005.   if (format_out == FO_MAIL_MESSAGE_TO ||
  1006.       format_out == FO_CACHE_AND_MAIL_MESSAGE_TO)
  1007.     {
  1008.       /* Bad news -- this would cause an endless loop. */
  1009.       XP_ASSERT(0);
  1010.       return 0;
  1011.     }
  1012. #else  /* !FO_MAIL_MESSAGE_TO */
  1013.   /* Otherwise, we oughtn't be getting in here at all. */
  1014.   XP_ASSERT(format_out != FO_MAIL_TO && format_out != FO_CACHE_AND_MAIL_TO);
  1015. #endif /* !FO_MAIL_MESSAGE_TO */
  1016.  
  1017.   msd = XP_NEW(struct mime_stream_data);
  1018.   if (!msd) return 0;
  1019.   XP_MEMSET(msd, 0, sizeof(*msd));
  1020. #ifdef DEBUG_terry
  1021. #if defined(XP_WIN) || defined(XP_OS2)
  1022.   msd->logit = XP_FileOpen("C:\\temp\\twtemp.html", xpTemporary, XP_FILE_WRITE);
  1023. #endif
  1024. #endif
  1025.   msd->url = url;
  1026.   msd->context = context;
  1027.   msd->format_out = format_out;
  1028.  
  1029.   msd->options = XP_NEW(MimeDisplayOptions);
  1030.   if (!msd->options)
  1031.     {
  1032.       XP_FREE(msd);
  1033.       return 0;
  1034.     }
  1035.   XP_MEMSET(msd->options, 0, sizeof(*msd->options));
  1036. #ifndef MOZILLA_30
  1037.   msd->options->pane = url->msg_pane;
  1038. #endif /* !MOZILLA_30 */
  1039.  
  1040.   if ((format_out == FO_PRESENT || format_out == FO_CACHE_AND_PRESENT) &&
  1041.       url->fe_data)
  1042.     {
  1043.       /* If we're going to the screen, and the URL has fe_data, then it is
  1044.          an options structure (that is how the news code hands us its callback
  1045.          functions.)  We copy it and free the passed-in data right away.
  1046.          (If we're not going to the screen, the fe_data might be some random
  1047.          object intended for someone further down the line; for example, it
  1048.          could be the XP_File that FO_SAVE_TO_DISK needs to pass around.)
  1049.        */
  1050.       MimeDisplayOptions *opt2 = (MimeDisplayOptions *) url->fe_data;
  1051.       *msd->options = *opt2;  /* copies */
  1052.       XP_FREE (opt2);
  1053.       url->fe_data = 0;
  1054.       msd->options->attachment_icon_layer_id = 0; /* Sigh... */
  1055.     }
  1056.  
  1057.   /* Set the defaults, based on the context, and the output-type.
  1058.    */
  1059.   if (format_out == FO_PRESENT ||
  1060.       format_out == FO_CACHE_AND_PRESENT ||
  1061.       format_out == FO_PRINT ||
  1062.       format_out == FO_CACHE_AND_PRINT ||
  1063.       format_out == FO_SAVE_AS_POSTSCRIPT ||
  1064.       format_out == FO_CACHE_AND_SAVE_AS_POSTSCRIPT)
  1065.     msd->options->fancy_headers_p = TRUE;
  1066.  
  1067. #ifndef MOZILLA_30
  1068.   if (format_out == FO_PRESENT ||
  1069.       format_out == FO_CACHE_AND_PRESENT)
  1070.     msd->options->output_vcard_buttons_p = TRUE;
  1071. #endif /* !MOZILLA_30 */
  1072.  
  1073.   if (format_out == FO_PRESENT ||
  1074.       format_out == FO_CACHE_AND_PRESENT) {
  1075.     msd->options->fancy_links_p = TRUE;
  1076.   }
  1077.  
  1078.   msd->options->headers = MimeHeadersSome;
  1079.  
  1080. #ifndef MOZILLA_30
  1081.   if (MIME_PrefDataValid < 2) {
  1082.     int32 headertype;
  1083.     if (MIME_PrefDataValid == 0) {
  1084.       PREF_RegisterCallback("mail.", &mime_PrefsChangeCallback, NULL);
  1085.     }
  1086.     headertype = 1;
  1087.     PREF_GetIntPref("mail.show_headers", &headertype);
  1088.     switch (headertype) {
  1089.       case 0: MIME_HeaderType = MimeHeadersMicro; break;
  1090.       case 1: MIME_HeaderType = MimeHeadersSome; break;
  1091.       case 2: MIME_HeaderType = MimeHeadersAll; break;
  1092.       default:
  1093.         XP_ASSERT(FALSE);
  1094.         break;
  1095.     }
  1096.     MIME_NoInlineAttachments = TRUE;
  1097.     PREF_GetBoolPref("mail.inline_attachments", &MIME_NoInlineAttachments);
  1098.     MIME_NoInlineAttachments = !MIME_NoInlineAttachments;
  1099.                                 /* This pref is written down in with the
  1100.                                    opposite sense of what we like to use... */
  1101.     MIME_WrapLongLines = FALSE;
  1102.     PREF_GetBoolPref("mail.wrap_long_lines", &MIME_WrapLongLines);
  1103.     MIME_VariableWidthPlaintext = TRUE;
  1104.     PREF_GetBoolPref("mail.fixed_width_messages",
  1105.                      &MIME_VariableWidthPlaintext);
  1106.     MIME_VariableWidthPlaintext = !MIME_VariableWidthPlaintext;
  1107.                                 /* This pref is written down in with the
  1108.                                    opposite sense of what we like to use... */
  1109.     MIME_PrefDataValid = 2;
  1110.   }
  1111.   msd->options->no_inline_p = MIME_NoInlineAttachments;
  1112.   msd->options->wrap_long_lines_p = MIME_WrapLongLines;
  1113.   msd->options->headers = MIME_HeaderType;
  1114. #endif /* !MOZILLA_30 */
  1115.  
  1116.   if (context->type == MWContextMail ||
  1117.       context->type == MWContextNews
  1118. #ifndef MOZILLA_30
  1119.       || context->type == MWContextMailMsg
  1120.       || context->type == MWContextNewsMsg
  1121. #endif /* !MOZILLA_30 */
  1122.       )
  1123.     {
  1124. #ifndef MOZILLA_30
  1125.       MSG_Pane* pane = MSG_FindPane(context, MSG_MESSAGEPANE);
  1126.       msd->options->rot13_p = FALSE;
  1127.       if (pane) {
  1128.         msd->options->rot13_p = MSG_ShouldRot13Message(pane);
  1129.       }
  1130. #else  /* MOZILLA_30 */
  1131.  
  1132.       XP_Bool all_headers_p = FALSE;
  1133.       XP_Bool micro_headers_p = FALSE;
  1134.       MSG_GetContextPrefs(context,
  1135.                           &all_headers_p,
  1136.                           µ_headers_p,
  1137.                           &msd->options->no_inline_p,
  1138.                           &msd->options->rot13_p,
  1139.                           &msd->options->wrap_long_lines_p);
  1140.       if (all_headers_p)
  1141.         msd->options->headers = MimeHeadersAll;
  1142.       else if (micro_headers_p)
  1143.         msd->options->headers = MimeHeadersMicro;
  1144.       else
  1145.         msd->options->headers = MimeHeadersSome;
  1146.  
  1147. #endif /* MOZILLA_30 */
  1148.     }
  1149.  
  1150.   status = mime_parse_url_options(url->address, msd->options);
  1151.   if (status < 0)
  1152.     {
  1153.       FREEIF(msd->options->part_to_load);
  1154.       XP_FREE(msd->options);
  1155.       XP_FREE(msd);
  1156.       return 0;
  1157.     }
  1158.  
  1159.   if (msd->options->headers == MimeHeadersMicro &&
  1160. #ifndef MOZILLA_30
  1161.       (url->address == NULL || (XP_STRNCMP(url->address, "news:", 5) != 0 &&
  1162.                                 XP_STRNCMP(url->address, "snews:", 6) != 0))
  1163. #else  /* MOZILLA_30 */
  1164.       context->type == MWContextMail
  1165. #endif /* MOZILLA_30 */
  1166.         )
  1167.     msd->options->headers = MimeHeadersMicroPlus;
  1168.  
  1169.   if (format_out == FO_QUOTE_MESSAGE ||
  1170.            format_out == FO_CACHE_AND_QUOTE_MESSAGE
  1171. #ifndef MOZILLA_30
  1172.            || format_out == FO_QUOTE_HTML_MESSAGE
  1173. #endif /* !MOZILLA_30 */
  1174.            )
  1175.     {
  1176.       msd->options->headers = MimeHeadersCitation;
  1177.       msd->options->fancy_headers_p = FALSE;
  1178. #ifndef MOZILLA_30
  1179.       if (format_out == FO_QUOTE_HTML_MESSAGE) {
  1180.         msd->options->nice_html_only_p = TRUE;
  1181.       }
  1182. #endif /* !MOZILLA_30 */
  1183.     }
  1184.  
  1185.   else if (msd->options->headers == MimeHeadersSome &&
  1186.            (format_out == FO_PRINT ||
  1187.             format_out == FO_CACHE_AND_PRINT ||
  1188.             format_out == FO_SAVE_AS_POSTSCRIPT ||
  1189.             format_out == FO_CACHE_AND_SAVE_AS_POSTSCRIPT ||
  1190.             format_out == FO_SAVE_AS_TEXT ||
  1191.             format_out == FO_CACHE_AND_SAVE_AS_TEXT))
  1192.     msd->options->headers = MimeHeadersSomeNoRef;
  1193.  
  1194. #ifdef FO_MAIL_MESSAGE_TO
  1195.   /* If we're attaching a message (for forwarding) then we must eradicate all
  1196.      traces of encryption from it, since forwarding someone else a message
  1197.      that wasn't encrypted for them doesn't work.  We have to decrypt it
  1198.      before sending it.
  1199.    */
  1200.   if ((format_out == FO_MAIL_TO || format_out == FO_CACHE_AND_MAIL_TO) &&
  1201.       msd->options->write_html_p == FALSE)
  1202.     msd->options->decrypt_p = TRUE;
  1203. #endif /* FO_MAIL_MESSAGE_TO */
  1204.  
  1205.   msd->options->url                   = url->address;
  1206.   msd->options->write_html_p          = TRUE;
  1207.   msd->options->output_init_fn        = mime_output_init_fn;
  1208.  
  1209. #if !defined(MOZILLA_30) && defined(XP_MAC)
  1210.   /* If it's a thread context, don't output all the mime stuff (hangs on Macintosh for
  1211.   ** unexpanded threadpane, because HTML is generated that needs images and layers).
  1212.   */
  1213.   if (context->type == MWContextMail)
  1214.     msd->options->output_fn           = compose_only_output_fn;
  1215.   else
  1216. #endif /* XP_MAC */
  1217.     msd->options->output_fn           = mime_output_fn;
  1218.  
  1219.   msd->options->set_html_state_fn     = mime_set_html_state_fn;
  1220. #ifndef MOZILLA_30
  1221.   if (format_out == FO_QUOTE_HTML_MESSAGE) {
  1222.     msd->options->charset_conversion_fn = mime_insert_html_convert_charset;
  1223.     msd->options->dont_touch_citations_p = TRUE;
  1224.   } else 
  1225. #endif
  1226.     msd->options->charset_conversion_fn = mime_convert_charset;
  1227.   msd->options->rfc1522_conversion_fn = mime_convert_rfc1522;
  1228.   msd->options->reformat_date_fn      = mime_reformat_date;
  1229.   msd->options->file_type_fn          = mime_file_type;
  1230.   msd->options->type_description_fn   = mime_type_desc;
  1231.   msd->options->type_icon_name_fn     = mime_type_icon;
  1232.   msd->options->stream_closure        = msd;
  1233.   msd->options->passwd_prompt_fn      = 0;
  1234.   msd->options->passwd_prompt_fn_arg  = context;
  1235.  
  1236.   msd->options->image_begin           = mime_image_begin;
  1237.   msd->options->image_end             = mime_image_end;
  1238.   msd->options->make_image_html       = mime_image_make_image_html;
  1239.   msd->options->image_write_buffer    = mime_image_write_buffer;
  1240.  
  1241. #ifndef MOZILLA_30
  1242.   msd->options->variable_width_plaintext_p = MIME_VariableWidthPlaintext;
  1243. #else  /* MOZILLA_30 */
  1244.   msd->options->variable_width_plaintext_p = MSG_VariableWidthPlaintext;
  1245. #endif /* MOZILLA_30 */
  1246.  
  1247.     /* ### mwelch We want FO_EDT_SAVE_IMAGE to behave like *_SAVE_AS here
  1248.                   because we're spooling untranslated raw data. */
  1249.   if (format_out == FO_SAVE_AS ||
  1250.       format_out == FO_CACHE_AND_SAVE_AS ||
  1251.       format_out == FO_MAIL_TO ||
  1252.       format_out == FO_CACHE_AND_MAIL_TO ||
  1253. #ifdef FO_EDT_SAVE_IMAGE
  1254.       format_out == FO_EDT_SAVE_IMAGE ||
  1255. #endif
  1256.       msd->options->part_to_load)
  1257.     msd->options->write_html_p = FALSE;
  1258.  
  1259.   XP_ASSERT(!msd->stream);
  1260.  
  1261.   obj = mime_new ((MimeObjectClass *)&mimeMessageClass,
  1262.                   (MimeHeaders *) NULL,
  1263.                   MESSAGE_RFC822);
  1264.   if (!obj)
  1265.     {
  1266.       FREEIF(msd->options->part_to_load);
  1267.       XP_FREE(msd->options);
  1268.       XP_FREE(msd);
  1269.       return 0;
  1270.     }
  1271.   obj->options = msd->options;
  1272.   msd->obj = obj;
  1273.  
  1274.   /* Both of these better not be true at the same time. */
  1275.   XP_ASSERT(! (obj->options->decrypt_p && obj->options->write_html_p));
  1276.  
  1277.   stream = XP_NEW (NET_StreamClass);
  1278.   if (!stream)
  1279.     {
  1280.       FREEIF(msd->options->part_to_load);
  1281.       XP_FREE(msd->options);
  1282.       XP_FREE(msd);
  1283.       XP_FREE(obj);
  1284.       return 0;
  1285.     }
  1286.   XP_MEMSET (stream, 0, sizeof (*stream));
  1287.  
  1288.   stream->name           = "MIME Conversion Stream";
  1289.   stream->complete       = mime_display_stream_complete;
  1290.   stream->abort          = mime_display_stream_abort;
  1291.   stream->put_block      = mime_display_stream_write;
  1292.   stream->is_write_ready = mime_display_stream_write_ready;
  1293.   stream->data_object    = msd;
  1294.   stream->window_id      = context;
  1295.  
  1296.   status = obj->class->initialize(obj);
  1297.   if (status >= 0)
  1298.     status = obj->class->parse_begin(obj);
  1299.   if (status < 0)
  1300.     {
  1301.       XP_FREE(stream);
  1302.       FREEIF(msd->options->part_to_load);
  1303.       XP_FREE(msd->options);
  1304.       XP_FREE(msd);
  1305.       XP_FREE(obj);
  1306.       return 0;
  1307.     }
  1308.  
  1309.   TRACEMSG(("Returning stream from MIME_MessageConverter"));
  1310.  
  1311.   return stream;
  1312. }
  1313.  
  1314.  
  1315. /* Interface between libmime and inline display of images: the abomination
  1316.    that is known as "internal-external-reconnect".
  1317.  */
  1318.  
  1319. struct mime_image_stream_data {
  1320.   struct mime_stream_data *msd;
  1321.   URL_Struct *url_struct;
  1322.   NET_StreamClass *istream;
  1323. };
  1324.  
  1325.  
  1326. static void *
  1327. mime_image_begin(const char *image_url, const char *content_type,
  1328.                  void *stream_closure)
  1329. {
  1330.   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
  1331.   struct mime_image_stream_data *mid;
  1332.  
  1333.   mid = XP_NEW(struct mime_image_stream_data);
  1334.   if (!mid) return 0;
  1335.  
  1336.   XP_MEMSET(mid, 0, sizeof(*mid));
  1337.   mid->msd = msd;
  1338.  
  1339.   /* Internal-external-reconnect only works when going to the screen.
  1340.      In that case, return the mid, but leave it empty (returning 0
  1341.      here is interpreted as out-of-memory.)
  1342.    */
  1343.   if (msd->format_out != FO_PRESENT &&
  1344.       msd->format_out != FO_CACHE_AND_PRESENT &&
  1345.       msd->format_out != FO_PRINT &&
  1346.       msd->format_out != FO_CACHE_AND_PRINT &&
  1347.       msd->format_out != FO_SAVE_AS_POSTSCRIPT &&
  1348.       msd->format_out != FO_CACHE_AND_SAVE_AS_POSTSCRIPT)
  1349.     return mid;
  1350. #ifndef MOZILLA_30
  1351.   if (!msd->context->img_cx)
  1352.       /* If there is no image context, e.g. if this is a Text context or a
  1353.          Mail context on the Mac, then we won't be loading images in the
  1354.          image viewer. */
  1355.       return mid;
  1356. #endif /* !MOZILLA_30 */
  1357.  
  1358.   mid->url_struct = NET_CreateURLStruct (image_url, NET_DONT_RELOAD);
  1359.   if (!mid->url_struct)
  1360.     {
  1361.       XP_FREE(mid);
  1362.       return 0;
  1363.     }
  1364.  
  1365.   mid->url_struct->content_encoding = 0;
  1366.   mid->url_struct->content_type = XP_STRDUP(content_type);
  1367.   if (!mid->url_struct->content_type)
  1368.     {
  1369.       NET_FreeURLStruct (mid->url_struct);
  1370.       XP_FREE(mid);
  1371.       return 0;
  1372.     }
  1373.  
  1374.   mid->istream = NET_StreamBuilder (FO_MULTIPART_IMAGE, mid->url_struct,
  1375.                                     msd->context);
  1376.   if (!mid->istream)
  1377.     {
  1378.       NET_FreeURLStruct (mid->url_struct);
  1379.       XP_FREE(mid);
  1380.       return 0;
  1381.     }
  1382.  
  1383.   /* When uudecoding, we tend to come up with tiny chunks of data
  1384.      at a time.  Make a stream to put them back together, so that
  1385.      we hand bigger pieces to the image library.
  1386.    */
  1387.   {
  1388.     NET_StreamClass *buffer =
  1389.       msg_MakeRebufferingStream (mid->istream, mid->url_struct, msd->context);
  1390.     if (buffer)
  1391.       mid->istream = buffer;
  1392.   }
  1393.   XP_ASSERT(msd->istream == NULL);
  1394.   msd->istream = mid->istream;
  1395.   return mid;
  1396. }
  1397.  
  1398.  
  1399. static void
  1400. mime_image_end(void *image_closure, int status)
  1401. {
  1402.   struct mime_image_stream_data *mid =
  1403.     (struct mime_image_stream_data *) image_closure;
  1404.  
  1405.   XP_ASSERT(mid);
  1406.   if (!mid) return;
  1407.   if (mid->istream)
  1408.     {
  1409.       if (status < 0)
  1410.         mid->istream->abort(mid->istream, status);
  1411.       else
  1412.         mid->istream->complete(mid->istream);
  1413.       XP_ASSERT(mid->msd->istream == mid->istream);
  1414.       mid->msd->istream = NULL;
  1415.       XP_FREE (mid->istream);
  1416.     }
  1417.   if (mid->url_struct)
  1418.     NET_FreeURLStruct (mid->url_struct);
  1419.   XP_FREE(mid);
  1420. }
  1421.  
  1422.  
  1423. static char *
  1424. mime_image_make_image_html(void *image_closure)
  1425. {
  1426.   struct mime_image_stream_data *mid =
  1427.     (struct mime_image_stream_data *) image_closure;
  1428.  
  1429.   const char *prefix = "<P><CENTER><IMG SRC=\"";
  1430.   const char *suffix = "\"></CENTER><P>";
  1431.   const char *url;
  1432.   char *buf;
  1433.   XP_ASSERT(mid);
  1434.   if (!mid) return 0;
  1435.  
  1436.   /* Internal-external-reconnect only works when going to the screen. */
  1437.   if (!mid->istream)
  1438.     return XP_STRDUP("<IMG SRC=\"internal-gopher-image\" ALT=\"[Image]\">");
  1439.  
  1440.   url = ((mid->url_struct && mid->url_struct->address)
  1441.          ? mid->url_struct->address
  1442.          : "");
  1443.   buf = (char *) XP_ALLOC (XP_STRLEN (prefix) + XP_STRLEN (suffix) +
  1444.                            XP_STRLEN (url) + 20);
  1445.   if (!buf) return 0;
  1446.   *buf = 0;
  1447.  
  1448.   XP_STRCAT (buf, prefix);
  1449.   XP_STRCAT (buf, url);
  1450.   XP_STRCAT (buf, suffix);
  1451.   return buf;
  1452. }
  1453.  
  1454.  
  1455. static int
  1456. mime_image_write_buffer(char *buf, int32 size, void *image_closure)
  1457. {
  1458.   struct mime_image_stream_data *mid =
  1459.     (struct mime_image_stream_data *) image_closure;
  1460.   if (!mid) return -1;
  1461.   if (!mid->istream) return 0;
  1462.   return mid->istream->put_block(mid->istream, buf, size);
  1463. }
  1464.  
  1465.  
  1466. /* Guessing the filename to use in "Save As", given a URL which may point
  1467.    to a MIME part that we've recently displayed.  (Kloooooge!)
  1468.  */
  1469.  
  1470.  
  1471. #ifdef HAVE_MIME_DATA_SLOT
  1472.  
  1473. /* If the given URL points to the MimeObject currently displayed on MWContext,
  1474.    or to a sub-part of it, then a freshly-allocated part-address-string will
  1475.    be returned.  (This string will be relative to the URL in the window.)
  1476.    Else, returns NULL.
  1477.  */
  1478. static char *
  1479. mime_extract_relative_part_address(MWContext *context, const char *url)
  1480. {
  1481.   char *url1 = 0, *url2 = 0, *part = 0, *result = 0;    /* free these */
  1482.   char *base_part = 0, *sub_part = 0, *s = 0;           /* don't free these */
  1483.  
  1484.   XP_ASSERT(context && url);
  1485.  
  1486.   if (!context ||
  1487.       !context->mime_data ||
  1488.       !context->mime_data->last_parsed_object ||
  1489.       !context->mime_data->last_parsed_url)
  1490.     goto FAIL;
  1491.  
  1492.   url1 = XP_STRDUP(url);
  1493.   if (!url1) goto FAIL;  /* MK_OUT_OF_MEMORY */
  1494.   url2 = XP_STRDUP(context->mime_data->last_parsed_url);
  1495.   if (!url2) goto FAIL;  /* MK_OUT_OF_MEMORY */
  1496.  
  1497.   s = XP_STRCHR(url1, '?');
  1498.   if (s) *s = 0;
  1499.   s = XP_STRCHR(url2, '?');
  1500.   if (s) *s = 0;
  1501.  
  1502.   if (s) base_part = s+1;
  1503.  
  1504.   /* If the two URLs, minus their '?' parts, don't match, give up.
  1505.    */
  1506.   if (!!XP_STRCMP(url1, url2))
  1507.     goto FAIL;
  1508.  
  1509.  
  1510.   /* Otherwise, the URLs match, so now we can go search for the part.
  1511.    */
  1512.  
  1513.   /* First we need to extract `base_part' from url2 -- this is the common
  1514.      prefix between the two URLs. */
  1515.   if (base_part)
  1516.     {
  1517.       s = XP_STRSTR(base_part, "?part=");
  1518.       if (!s)
  1519.         s = XP_STRSTR(base_part, "&part=");
  1520.       base_part = s;
  1521.       if (base_part) /* found it */
  1522.         {
  1523.           base_part += 6;
  1524.           /* truncate `base_part' after part spec. */
  1525.           for (s = base_part; *s != 0 && *s != '?' && *s != '&'; s++)
  1526.             ;
  1527.           *s = 0;
  1528.         }
  1529.     }
  1530.  
  1531.   /* Find the part we're looking for.
  1532.    */
  1533.   s = XP_STRSTR(url, "?part=");
  1534.   if (!s)
  1535.     s = XP_STRSTR(url, "&part=");
  1536.   if (!s)
  1537.     goto FAIL;
  1538.  
  1539.   s += 6;
  1540.   part = XP_STRDUP(s);
  1541.   if (!part) goto FAIL;   /* MK_OUT_OF_MEMORY */
  1542.  
  1543.   /* truncate `part' after part spec. */
  1544.   for (s = part; *s != 0 && *s != '?' && *s != '&'; s++)
  1545.     ;
  1546.   *s = 0;
  1547.  
  1548.   /* Now remove the common prefix, if any. */
  1549.   sub_part = part;
  1550.   if (base_part)
  1551.     {
  1552.       int L = XP_STRLEN(base_part);
  1553.       if (!strncasecomp(sub_part, base_part, L) &&
  1554.           sub_part[L] == '.')
  1555.         sub_part += L + 1;
  1556.     }
  1557.  
  1558.   result = XP_STRDUP(sub_part);
  1559.  
  1560. FAIL:
  1561.  
  1562.   FREEIF(part);
  1563.   FREEIF(url1);
  1564.   FREEIF(url2);
  1565.   return result;
  1566. }
  1567.  
  1568. static char *
  1569. mime_guess_url_content_name_1 (MWContext *context, const char *url)
  1570. {
  1571.   char *name = 0;
  1572.   char *addr = mime_extract_relative_part_address(context, url);
  1573.   if (!addr) return 0;
  1574.   name = mime_find_suggested_name_of_part(addr,
  1575.                                       context->mime_data->last_parsed_object);
  1576.   XP_FREE(addr);
  1577.   return name;
  1578. }
  1579.  
  1580.  
  1581. char *
  1582. MimeGuessURLContentName(MWContext *context, const char *url)
  1583. {
  1584.   char *result = mime_guess_url_content_name_1 (context, url);
  1585.   if (result)
  1586.     return result;
  1587.   else
  1588.     {
  1589.       /* Ok, that didn't work, let's go look in all the other contexts! */
  1590.       XP_List *list = XP_GetGlobalContextList();
  1591.       int32 i, j = XP_ListCount(list);
  1592.       for (i = 1; i <= j; i++)
  1593.         {
  1594.           MWContext *cx2 = (MWContext *) XP_ListGetObjectNum(list, i);
  1595.           if (cx2 != context)
  1596.             {
  1597.               result = mime_guess_url_content_name_1 (cx2, url);
  1598.               if (result) return result;
  1599.             }
  1600.         }
  1601.     }
  1602.   return 0;
  1603. }
  1604.  
  1605. static char *
  1606. mime_get_url_content_type_1 (MWContext *context, const char *url)
  1607. {
  1608.   char *name = 0;
  1609.   char *addr = mime_extract_relative_part_address(context, url);
  1610.   if (!addr) return 0;
  1611.   name = mime_find_content_type_of_part(addr,
  1612.                                       context->mime_data->last_parsed_object);
  1613.   XP_FREE(addr);
  1614.   return name;
  1615. }
  1616.  
  1617. /* This routine is currently used to get the content type for a mime part url
  1618.    so the fe's can decide to open a new browser window or just open the save as dialog.
  1619.    It only works on a context that is displaying the message containing the part.
  1620. */
  1621. char *
  1622. MimeGetURLContentType(MWContext *context, const char *url)
  1623. {
  1624.   char *result = mime_get_url_content_type_1 (context, url);
  1625.   /* I don't think we need to look at any other contexts for our purposes,
  1626.    * but I bow to Jamie's suscpicion that there's a good reason to.
  1627.   */
  1628.   if (result)
  1629.     return result;
  1630.   else
  1631.     {
  1632.       /* Ok, that didn't work, let's go look in all the other contexts! */
  1633.       XP_List *list = XP_GetGlobalContextList();
  1634.       int32 i, j = XP_ListCount(list);
  1635.       for (i = 1; i <= j; i++)
  1636.         {
  1637.           MWContext *cx2 = (MWContext *) XP_ListGetObjectNum(list, i);
  1638.           if (cx2 != context)
  1639.             {
  1640.               result = mime_get_url_content_type_1 (cx2, url);
  1641.               if (result) return result;
  1642.             }
  1643.         }
  1644.     }
  1645.   return 0;
  1646. }
  1647.  
  1648.  
  1649. static MimeObject*
  1650. mime_find_text_html_part_1(MimeObject* obj)
  1651. {
  1652.   if (mime_subclass_p(obj->class,
  1653.                       (MimeObjectClass*) &mimeInlineTextHTMLClass)) {
  1654.     return obj;
  1655.   }
  1656.   if (mime_subclass_p(obj->class, (MimeObjectClass*) &mimeContainerClass)) {
  1657.     MimeContainer* cobj = (MimeContainer*) obj;
  1658.     int32 i;
  1659.     for (i=0 ; i<cobj->nchildren ; i++) {
  1660.       MimeObject* result = mime_find_text_html_part_1(cobj->children[i]);
  1661.       if (result) return result;
  1662.     }
  1663.   }
  1664.   return NULL;
  1665. }
  1666.  
  1667.  
  1668. static MimeObject*
  1669. mime_find_text_html_part(MWContext* context)
  1670. {
  1671.   XP_ASSERT(context);
  1672.   if (!context ||
  1673.       !context->mime_data ||
  1674.       !context->mime_data->last_parsed_object)
  1675.     return NULL;
  1676.  
  1677.   return mime_find_text_html_part_1(context->mime_data->last_parsed_object);
  1678. }
  1679.  
  1680.  
  1681. XP_Bool
  1682. MimeShowingTextHtml(MWContext* context)
  1683. {
  1684.   return mime_find_text_html_part(context) != NULL;
  1685. }
  1686.  
  1687.  
  1688. extern char*
  1689. MimeGetHtmlPartURL(MWContext* context)
  1690. {
  1691.   MimeObject* obj = mime_find_text_html_part(context);
  1692.   if (obj == NULL) return NULL;
  1693.   return mime_part_address(obj);
  1694. }
  1695.  
  1696.  
  1697. /* Finds the main object of the message -- generally a multipart/mixed,
  1698.    text/plain, or text/html. */
  1699. static MimeObject*
  1700. mime_get_main_object(MimeObject* obj)
  1701. {
  1702.   MimeContainer* cobj;
  1703.   if (!(mime_subclass_p(obj->class, (MimeObjectClass*) &mimeMessageClass))) {
  1704.     return obj;
  1705.   }
  1706.   cobj = (MimeContainer*) obj;
  1707.   if (cobj->nchildren != 1) return obj;
  1708.   obj = cobj->children[0];
  1709.   for (;;) {
  1710.     if (!mime_subclass_p(obj->class,
  1711.                          (MimeObjectClass*) &mimeMultipartSignedClass) &&
  1712.         !mime_subclass_p(obj->class,
  1713.                          (MimeObjectClass*) &mimeFilterClass)) {
  1714.       return obj;
  1715.     }
  1716.   /* Our main thing is a signed or encrypted object.
  1717.      We don't care about that; go on inside to the thing that we signed or
  1718.      encrypted. */
  1719.     cobj = (MimeContainer*) obj;
  1720.     if (cobj->nchildren != 1) return obj;
  1721.     obj = cobj->children[0];
  1722.   }
  1723. }
  1724.  
  1725.  
  1726. int
  1727. MimeGetAttachmentCount(MWContext* context)
  1728. {
  1729.   MimeObject* obj;
  1730.   MimeContainer* cobj;
  1731.   XP_ASSERT(context);
  1732.   if (!context ||
  1733.       !context->mime_data ||
  1734.       !context->mime_data->last_parsed_object) {
  1735.     return 0;
  1736.   }
  1737.   obj = mime_get_main_object(context->mime_data->last_parsed_object);
  1738.   if (!mime_subclass_p(obj->class, (MimeObjectClass*) &mimeContainerClass)) {
  1739.  
  1740.     return 0;
  1741.   }
  1742.   cobj = (MimeContainer*) obj;
  1743.   return cobj->nchildren - 1;   /* ### Hardcoding subtraction of one isn't
  1744.                                  quite right; we have to look at the first
  1745.                                  object and decide whether it is the body or
  1746.                                  the first attachment on a NULL body. */
  1747. }
  1748.  
  1749.  
  1750. int
  1751. MimeGetAttachmentList(MWContext* context, MSG_AttachmentData** data)
  1752. {
  1753.   MimeObject* obj;
  1754.   MimeContainer* cobj;
  1755.   MSG_AttachmentData* tmp;
  1756.   int32 n;
  1757.   int32 i;
  1758.   char* disp;
  1759.   char c;
  1760.   if (!data) return 0;
  1761.   *data = NULL;
  1762.   XP_ASSERT(context);
  1763.   if (!context ||
  1764.       !context->mime_data ||
  1765.       !context->mime_data->last_parsed_object) {
  1766.     return 0;
  1767.   }
  1768.   obj = mime_get_main_object(context->mime_data->last_parsed_object);
  1769.   if (!mime_subclass_p(obj->class, (MimeObjectClass*) &mimeContainerClass)) {
  1770.     return 0;
  1771.   }
  1772.   cobj = (MimeContainer*) obj;
  1773.   n = cobj->nchildren;          /* This is often too big, but that's OK. */
  1774.   if (n <= 0) return n;
  1775.   *data = XP_CALLOC(n + 1, sizeof(MSG_AttachmentData));
  1776.   if (!*data) return MK_OUT_OF_MEMORY;
  1777.   tmp = *data;
  1778.   c = '?';
  1779.   if (XP_STRCHR(context->mime_data->last_parsed_url, '?')) {
  1780.     c = '&';
  1781.   }
  1782.   for (i=1 /*###ick, see above*/ ; i<cobj->nchildren ; i++, tmp++) {
  1783.     MimeObject* child = cobj->children[i];
  1784.     char* part = mime_part_address(child);
  1785.     if (!part) return MK_OUT_OF_MEMORY;
  1786.     tmp->url = PR_smprintf("%s%cpart=%s", context->mime_data->last_parsed_url,
  1787.                            c, part);
  1788.     if (!tmp->url) return MK_OUT_OF_MEMORY;
  1789.     tmp->real_type = child->content_type ?
  1790.       XP_STRDUP(child->content_type) : NULL;
  1791.     tmp->real_encoding = child->encoding ? XP_STRDUP(child->encoding) : NULL;
  1792.     disp = MimeHeaders_get(child->headers, HEADER_CONTENT_DISPOSITION,
  1793.                            FALSE, FALSE);
  1794.     if (disp) {
  1795.       tmp->real_name = MimeHeaders_get_parameter(disp, "filename");
  1796.       if (tmp->real_name)
  1797.       {
  1798.         char *fname = NULL;
  1799.         fname = mime_decode_filename(tmp->real_name);
  1800.         if (fname && fname != tmp->real_name)
  1801.         {
  1802.             XP_FREE(tmp->real_name);
  1803.             tmp->real_name = fname;
  1804.         }
  1805.       }
  1806.       XP_FREE(disp);
  1807.     }
  1808.     disp = MimeHeaders_get(child->headers, HEADER_CONTENT_TYPE,
  1809.                        FALSE, FALSE);
  1810.     if (disp)
  1811.     {
  1812.       tmp->x_mac_type   = MimeHeaders_get_parameter(disp, PARAM_X_MAC_TYPE);
  1813.       tmp->x_mac_creator= MimeHeaders_get_parameter(disp, PARAM_X_MAC_CREATOR);
  1814.       XP_FREE(disp);
  1815.     }
  1816.     tmp->description = MimeHeaders_get(child->headers,
  1817.                                        HEADER_CONTENT_DESCRIPTION,
  1818.                                        FALSE, FALSE);
  1819. #ifndef MOZILLA_30
  1820.     if (tmp->real_type && !strcasecomp(tmp->real_type, MESSAGE_RFC822) &&
  1821.         (!tmp->real_name || *tmp->real_name))
  1822.     {
  1823.         StrAllocCopy(tmp->real_name, XP_GetString(XP_FORWARDED_MESSAGE_ATTACHMENT));
  1824.     }
  1825. #endif /* !MOZILLA_30 */
  1826.   }
  1827.   return 0;
  1828. }
  1829.  
  1830.  
  1831. void
  1832. MimeFreeAttachmentList(MSG_AttachmentData* data)
  1833. {
  1834.   if (data) {
  1835.     MSG_AttachmentData* tmp;
  1836.     for (tmp = data ; tmp->url ; tmp++) {
  1837.       /* Can't do FREEIF on `const' values... */
  1838.       if (tmp->url) XP_FREE((char *) tmp->url);
  1839.       if (tmp->real_type) XP_FREE((char *) tmp->real_type);
  1840.       if (tmp->real_encoding) XP_FREE((char *) tmp->real_encoding);
  1841.       if (tmp->real_name) XP_FREE((char *) tmp->real_name);
  1842.       if (tmp->x_mac_type) XP_FREE((char *) tmp->x_mac_type);
  1843.       if (tmp->x_mac_creator) XP_FREE((char *) tmp->x_mac_creator);
  1844.       if (tmp->description) XP_FREE((char *) tmp->description);
  1845.       tmp->url = 0;
  1846.       tmp->real_type = 0;
  1847.       tmp->real_name = 0;
  1848.       tmp->description = 0;
  1849.     }
  1850.     XP_FREE(data);
  1851.   }
  1852. }
  1853.  
  1854.  
  1855.  
  1856. void
  1857. MimeDestroyContextData(MWContext *context)
  1858. {
  1859.   INTL_CharSetInfo csi = NULL;
  1860.  
  1861.   XP_ASSERT(context);
  1862.   if (!context) return;
  1863.  
  1864.   csi = LO_GetDocumentCharacterSetInfo(context);
  1865.   if (csi)
  1866.       INTL_SetCSIMimeCharset(csi, NULL);
  1867.   
  1868.   if (!context->mime_data) return;
  1869.  
  1870.   if (context->mime_data->last_parsed_object)
  1871.     {
  1872.       MimeDisplayOptions *options =
  1873.         context->mime_data->last_parsed_object->options;
  1874.  
  1875.       mime_free(context->mime_data->last_parsed_object);
  1876.       if (options)
  1877.         {
  1878.           FREEIF(options->part_to_load);
  1879.           XP_FREE(options);
  1880.         }
  1881.  
  1882.       context->mime_data->last_parsed_object = 0;
  1883.       FREEIF(context->mime_data->last_parsed_url);
  1884.     }
  1885.  
  1886. #ifdef LOCK_LAST_CACHED_MESSAGE
  1887.   if (context->mime_data->previous_locked_url)
  1888.     {
  1889.       /* duplicated from mime_make_output_stream()... */
  1890.       URL_Struct *url_s =
  1891.         NET_CreateURLStruct(context->mime_data->previous_locked_url,
  1892.                             NET_NORMAL_RELOAD);
  1893.       if (url_s)
  1894.         {
  1895.           /* Note: if post data was involved here, we'd lose.  We're assuming
  1896.              that all we need to save is the url->address. */
  1897.           NET_ChangeCacheFileLock(url_s, FALSE);
  1898.           NET_FreeURLStruct(url_s);
  1899.         }
  1900.       XP_FREE(context->mime_data->previous_locked_url);
  1901.       context->mime_data->previous_locked_url = 0;
  1902.     }
  1903. #endif /* LOCK_LAST_CACHED_MESSAGE */
  1904.  
  1905.   XP_FREE(context->mime_data);
  1906.   context->mime_data = 0;
  1907. }
  1908.  
  1909. #else  /* !HAVE_MIME_DATA_SLOT */
  1910.  
  1911. char *
  1912. MimeGuessURLContentName(MWContext *context, const char *url)
  1913. {
  1914.   return 0;
  1915. }
  1916.  
  1917. void
  1918. MimeDestroyContextData(MWContext *context)
  1919. {
  1920.   if (!context) return;
  1921.  
  1922.   INTL_SetCSIMimeCharset(LO_GetDocumentCharacterSetInfo(context), NULL);
  1923.  
  1924. }
  1925. #endif /* !HAVE_MIME_DATA_SLOT */
  1926.  
  1927.  
  1928.  
  1929.  
  1930.  
  1931. /* Interface between netlib and the converters from text/richtext
  1932.    and text/enriched to text/html: MIME_RichtextConverter() and
  1933.    MIME_EnrichedTextConverter().
  1934.  */
  1935.  
  1936. struct mime_richtext_data {
  1937.   URL_Struct *url;                  /* The URL this is all coming from. */
  1938.   MWContext *context;
  1939.   int format_out;
  1940.   NET_StreamClass *stream;          /* The stream to which we write HTML. */
  1941.   char *ibuffer, *obuffer;
  1942.   int32 ibuffer_size, obuffer_size;
  1943.   int32 ibuffer_fp;
  1944.   XP_Bool enriched_p;
  1945. };
  1946.  
  1947. static int 
  1948. mime_richtext_stream_fn (char *buf, int32 size, void *closure)
  1949. {
  1950.   struct mime_richtext_data *mrd = (struct mime_richtext_data *) closure;
  1951.   XP_ASSERT(mrd->stream);
  1952.   if (!mrd->stream) return -1;
  1953.   return mrd->stream->put_block (mrd->stream, buf, size);
  1954. }
  1955.  
  1956. static int
  1957. mime_richtext_write_line (char* line, int32 size, void *closure)
  1958. {
  1959.   struct mime_richtext_data *mrd = (struct mime_richtext_data *) closure;
  1960.   return MimeRichtextConvert (line, size, mime_richtext_stream_fn,
  1961.                               mrd, &mrd->obuffer, &mrd->obuffer_size,
  1962.                               mrd->enriched_p);
  1963. }
  1964.  
  1965. static int
  1966. mime_richtext_write (NET_StreamClass *stream, const char* buf, int32 size)
  1967. {
  1968.   struct mime_richtext_data *data = (struct mime_richtext_data *) stream->data_object;  
  1969.   return msg_LineBuffer (buf, size, &data->ibuffer, &data->ibuffer_size,
  1970.                          &data->ibuffer_fp, FALSE, mime_richtext_write_line,
  1971.                          data);
  1972. }
  1973.  
  1974. static unsigned int
  1975. mime_richtext_write_ready (NET_StreamClass *stream)
  1976. {
  1977.   struct mime_richtext_data *data = (struct mime_richtext_data *) stream->data_object;  
  1978.   if (data->stream)
  1979.     return ((*data->stream->is_write_ready)
  1980.             (data->stream));
  1981.   else
  1982.     return (MAX_WRITE_READY);
  1983. }
  1984.  
  1985. static void
  1986. mime_richtext_complete (NET_StreamClass *stream)
  1987. {
  1988.   struct mime_richtext_data *mrd = (struct mime_richtext_data *) stream->data_object;  
  1989.   if (!mrd) return;
  1990.   FREEIF(mrd->obuffer);
  1991.   if (mrd->stream)
  1992.     {
  1993.       mrd->stream->complete (mrd->stream);
  1994.       XP_FREE (mrd->stream);
  1995.     }
  1996.   XP_FREE(mrd);
  1997. }
  1998.  
  1999. static void
  2000. mime_richtext_abort (NET_StreamClass *stream, int status)
  2001. {
  2002.   struct mime_richtext_data *mrd = (struct mime_richtext_data *) stream->data_object;  
  2003.   if (!mrd) return;
  2004.   FREEIF(mrd->obuffer);
  2005.   if (mrd->stream)
  2006.     {
  2007.       mrd->stream->abort (mrd->stream, status);
  2008.       XP_FREE (mrd->stream);
  2009.     }
  2010.   XP_FREE(mrd);
  2011. }
  2012.  
  2013.  
  2014. static NET_StreamClass * 
  2015. MIME_RichtextConverter_1 (int format_out, void *closure,
  2016.                           URL_Struct *url, MWContext *context,
  2017.                           XP_Bool enriched_p)
  2018. {
  2019.   struct mime_richtext_data *data;
  2020.   NET_StreamClass *stream, *next_stream;
  2021.  
  2022.   next_stream = mime_make_output_stream(TEXT_HTML, 0, 0, 0, 0,
  2023.                                         format_out, url, context, NULL);
  2024.   if (!next_stream) return 0;
  2025.  
  2026.   data = XP_NEW(struct mime_richtext_data);
  2027.   if (!data)
  2028.     {
  2029.       XP_FREE(next_stream);
  2030.       return 0;
  2031.     }
  2032.   XP_MEMSET(data, 0, sizeof(*data));
  2033.   data->url = url;
  2034.   data->context = context;
  2035.   data->format_out = format_out;
  2036.   data->stream = next_stream;
  2037.   data->enriched_p = enriched_p;
  2038.  
  2039.   stream = XP_NEW (NET_StreamClass);
  2040.   if (!stream)
  2041.     {
  2042.       XP_FREE(next_stream);
  2043.       XP_FREE(data);
  2044.       return 0;
  2045.     }
  2046.   XP_MEMSET (stream, 0, sizeof (*stream));
  2047.  
  2048.   stream->name           = "Richtext Conversion Stream";
  2049.   stream->complete       = mime_richtext_complete;
  2050.   stream->abort          = mime_richtext_abort;
  2051.   stream->put_block      = mime_richtext_write;
  2052.   stream->is_write_ready = mime_richtext_write_ready;
  2053.   stream->data_object    = data;
  2054.   stream->window_id      = context;
  2055.  
  2056.   TRACEMSG(("Returning stream from MIME_RichtextConverter"));
  2057.  
  2058.   return stream;
  2059. }
  2060.  
  2061. NET_StreamClass * 
  2062. MIME_RichtextConverter (int format_out, void *closure,
  2063.                         URL_Struct *url, MWContext *context)
  2064. {
  2065.   return MIME_RichtextConverter_1 (format_out, closure, url, context, FALSE);
  2066. }
  2067.  
  2068. NET_StreamClass * 
  2069. MIME_EnrichedTextConverter (int format_out, void *closure,
  2070.                             URL_Struct *url, MWContext *context)
  2071. {
  2072.   return MIME_RichtextConverter_1 (format_out, closure, url, context, TRUE);
  2073. }
  2074.  
  2075.  
  2076.  
  2077. #ifndef MOZILLA_30
  2078.  
  2079. int
  2080. MIME_DisplayAttachmentPane(MWContext* context)
  2081. {
  2082.     if (context && context->mime_data) {
  2083.         MSG_Pane* pane = context->mime_data->last_pane;
  2084.         if (!pane)
  2085.             pane = MSG_FindPane(context, MSG_MESSAGEPANE);
  2086.         if (pane) {
  2087.             MSG_MessagePaneCallbacks* callbacks;
  2088.             void* closure;
  2089.             callbacks = MSG_GetMessagePaneCallbacks(pane, &closure);
  2090.             if (callbacks && callbacks->UserWantsToSeeAttachments) {
  2091.                 (*callbacks->UserWantsToSeeAttachments)(pane, closure);
  2092.             }
  2093.         }
  2094.     }
  2095.     return 0;
  2096. }
  2097.  
  2098.  
  2099. /* This struct is the state we used in MIME_VCardConverter() */
  2100. struct mime_vcard_data {
  2101.     URL_Struct *url;                         /* original url */
  2102.     int format_out;                          /* intended output format; 
  2103.                                               should be TEXT-VCARD */
  2104.     MWContext *context;
  2105.     NET_StreamClass *stream;                 /* not used for now */
  2106.     MimeDisplayOptions *options;             /* data for communicating with libmime.a */
  2107.     MimeObject *obj;                         /* The root */
  2108. };
  2109.  
  2110. static int
  2111. mime_vcard_write (NET_StreamClass *stream,
  2112.               const char *buf,
  2113.               int32 size )
  2114. {
  2115.   struct mime_vcard_data *vcd = (struct mime_vcard_data *) stream->data_object;  
  2116.   XP_ASSERT ( vcd );
  2117.  
  2118.   if ( !vcd || !vcd->obj ) return -1;
  2119.  
  2120.   return vcd->obj->class->parse_line ((char *) buf, size, vcd->obj);
  2121. }
  2122.  
  2123. static unsigned int
  2124. mime_vcard_write_ready (NET_StreamClass *stream)
  2125. {
  2126.   struct mime_vcard_data *vcd = (struct mime_vcard_data *) stream->data_object;  
  2127.   XP_ASSERT (vcd);
  2128.  
  2129.   if (!vcd) return MAX_WRITE_READY;
  2130.   if (vcd->stream)
  2131.     return vcd->stream->is_write_ready ( vcd->stream );
  2132.   else
  2133.     return MAX_WRITE_READY;
  2134. }
  2135.  
  2136. static void
  2137. mime_vcard_complete (NET_StreamClass *stream)
  2138. {
  2139.   struct mime_vcard_data *vcd = (struct mime_vcard_data *) stream->data_object;  
  2140.   
  2141.   XP_ASSERT (vcd);
  2142.  
  2143.   if (!vcd) return;
  2144.   
  2145.   if (vcd->obj) {
  2146.     int status;
  2147.  
  2148.     status = vcd->obj->class->parse_eof ( vcd->obj, FALSE );
  2149.     vcd->obj->class->parse_end( vcd->obj, status < 0 ? TRUE : FALSE );
  2150.     
  2151.     mime_free (vcd->obj);
  2152.     vcd->obj = 0;
  2153.  
  2154.     if (vcd->stream) {
  2155.       vcd->stream->complete (vcd->stream);
  2156.       XP_FREE( vcd->stream );
  2157.       vcd->stream = 0;
  2158.     }
  2159.   }
  2160. }
  2161.  
  2162. static void
  2163. mime_vcard_abort (NET_StreamClass *stream, int status )
  2164. {
  2165.   struct mime_vcard_data *vcd = (struct mime_vcard_data *) stream->data_object;
  2166.   
  2167.   XP_ASSERT (vcd);
  2168.   if (!vcd) return;
  2169.   
  2170.   if (vcd->obj) {
  2171.       int status;
  2172.       
  2173.       if ( !vcd->obj->closed_p )
  2174.           status = vcd->obj->class->parse_eof ( vcd->obj, TRUE );
  2175.       if ( !vcd->obj->parsed_p )
  2176.           vcd->obj->class->parse_end( vcd->obj, TRUE );
  2177.       
  2178.       mime_free (vcd->obj);
  2179.       vcd->obj = 0;
  2180.    
  2181.       if (vcd->stream) {
  2182.           vcd->stream->abort (vcd->stream, status);
  2183.           XP_FREE( vcd->stream );
  2184.           vcd->stream = 0;
  2185.       }
  2186.   }
  2187.   XP_FREE (vcd);
  2188. }
  2189.  
  2190. extern int MIME_HasAttachments(MWContext *context)
  2191. {
  2192.     return (context->mime_data && context->mime_data->last_parsed_object->class->showAttachmentIcon);
  2193. }
  2194.  
  2195.  
  2196.  
  2197. extern NET_StreamClass *
  2198. MIME_VCardConverter ( int format_out,
  2199.             void *closure,
  2200.             URL_Struct *url,
  2201.             MWContext *context )
  2202. {
  2203.     int status = 0;
  2204.     NET_StreamClass * stream = NULL;
  2205.     NET_StreamClass * next_stream = NULL;
  2206.     struct mime_vcard_data *vcd = NULL;
  2207.     MimeObject *obj;
  2208.  
  2209.     XP_ASSERT (url && context);
  2210.     if ( !url || !context ) return NULL;
  2211.  
  2212.     next_stream = mime_make_output_stream (TEXT_HTML, 0, 0, 0, 0,
  2213.                 format_out, url, context, NULL);
  2214.  
  2215.     if (!next_stream) return 0;
  2216.  
  2217.     
  2218.     vcd = XP_NEW_ZAP (struct mime_vcard_data);
  2219.     if (!vcd) {
  2220.         XP_FREE (next_stream);
  2221.         return 0;
  2222.     }
  2223.  
  2224.     vcd->url = url;
  2225.     vcd->context = context;
  2226.     vcd->format_out = format_out;
  2227.     vcd->stream = next_stream;
  2228.  
  2229.     vcd->options = XP_NEW_ZAP ( MimeDisplayOptions );
  2230.  
  2231.     if ( !vcd->options ) {
  2232.         XP_FREE (next_stream);
  2233.         XP_FREE ( vcd );
  2234.         return 0;
  2235.     }
  2236.  
  2237.     vcd->options->write_html_p        = TRUE;
  2238.     vcd->options->output_fn           = mime_output_fn;
  2239.     if (format_out == FO_PRESENT ||
  2240.         format_out == FO_CACHE_AND_PRESENT)
  2241.     vcd->options->output_vcard_buttons_p = TRUE;
  2242.  
  2243. #ifdef MIME_DRAFTS
  2244.     vcd->options->decompose_file_p = FALSE; /* new field in MimeDisplayOptions */
  2245. #endif /* MIME_DRAFTS */
  2246.  
  2247.     vcd->options->url = url->address;
  2248.     vcd->options->stream_closure = vcd;
  2249.     vcd->options->html_closure = vcd;
  2250.  
  2251.     obj = mime_new ( (MimeObjectClass *) &mimeInlineTextVCardClass,
  2252.         (MimeHeaders *) NULL,
  2253.         TEXT_VCARD );
  2254.  
  2255.     if ( !obj ) {
  2256.         FREEIF( vcd->options->part_to_load );
  2257.         XP_FREE ( next_stream );
  2258.         XP_FREE ( vcd->options );
  2259.         XP_FREE ( vcd );
  2260.         return 0;
  2261.     }
  2262.   
  2263.     obj->options = vcd->options;
  2264.     vcd->obj = obj;
  2265.  
  2266.     stream = XP_NEW_ZAP ( NET_StreamClass );
  2267.     if ( !stream ) {
  2268.         FREEIF ( vcd->options->part_to_load );
  2269.         XP_FREE ( next_stream );
  2270.         XP_FREE ( vcd->options );
  2271.         XP_FREE ( vcd );
  2272.         XP_FREE ( obj );
  2273.         return 0;
  2274.     }
  2275.  
  2276.     stream->name = "MIME To VCard Converter Stream";
  2277.     stream->complete = mime_vcard_complete;
  2278.     stream->abort = mime_vcard_abort;
  2279.     stream->put_block = mime_vcard_write;
  2280.     stream->is_write_ready = mime_vcard_write_ready;
  2281.     stream->data_object = vcd;
  2282.     stream->window_id = context;
  2283.  
  2284.     status = obj->class->initialize ( obj );
  2285.     if ( status >= 0 )
  2286.         status = obj->class->parse_begin ( obj );
  2287.     if ( status < 0 ) {
  2288.         XP_FREE ( stream );
  2289.         FREEIF( vcd->options->part_to_load );
  2290.         XP_FREE ( next_stream );
  2291.         XP_FREE ( vcd->options );
  2292.         XP_FREE ( vcd );
  2293.         XP_FREE ( obj );
  2294.         return 0;
  2295.     }
  2296.  
  2297.     return stream;
  2298. }
  2299.  
  2300. #endif /* !MOZILLA_30 */
  2301.