зеркало из https://github.com/mozilla/pjs.git
2301 строка
70 KiB
C
2301 строка
70 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||
*
|
||
* The contents of this file are subject to the Netscape Public License
|
||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||
* http://www.mozilla.org/NPL/
|
||
*
|
||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||
* for the specific language governing rights and limitations under the
|
||
* NPL.
|
||
*
|
||
* The Initial Developer of this code under the NPL is Netscape
|
||
* Communications Corporation. Portions created by Netscape are
|
||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||
* Reserved.
|
||
*/
|
||
|
||
/* mimemoz.c --- Mozilla interface to libmime.a
|
||
Created: Jamie Zawinski <jwz@netscape.com>, 15-May-96.
|
||
*/
|
||
|
||
#include "xp.h"
|
||
#include "libi18n.h"
|
||
#include "xp_time.h"
|
||
#include "msgcom.h"
|
||
#include "mimeobj.h"
|
||
#include "mimemsg.h"
|
||
#include "mimetric.h" /* for MIME_RichtextConverter */
|
||
#include "mimethtm.h"
|
||
#include "mimemsig.h"
|
||
#include "mimecryp.h"
|
||
#ifndef MOZILLA_30
|
||
# include "xpgetstr.h"
|
||
# include "mimevcrd.h" /* for MIME_VCardConverter */
|
||
# include "edt.h"
|
||
extern int XP_FORWARDED_MESSAGE_ATTACHMENT;
|
||
#endif /* !MOZILLA_30 */
|
||
#include "prefapi.h"
|
||
|
||
#include "secrng.h"
|
||
#include "prprf.h"
|
||
#include "intl_csi.h"
|
||
|
||
#ifdef HAVE_MIME_DATA_SLOT
|
||
# define LOCK_LAST_CACHED_MESSAGE
|
||
#endif
|
||
|
||
/* Interface between netlib and the top-level message/rfc822 parser:
|
||
MIME_MessageConverter()
|
||
*/
|
||
|
||
struct mime_stream_data { /* This struct is the state we pass around
|
||
amongst the various stream functions
|
||
used by MIME_MessageConverter().
|
||
*/
|
||
|
||
URL_Struct *url; /* The URL this is all coming from. */
|
||
int format_out;
|
||
MWContext *context;
|
||
NET_StreamClass *stream; /* The stream to which we write output */
|
||
NET_StreamClass *istream; /* The stream we're writing out image data,
|
||
if any. */
|
||
MimeObject *obj; /* The root parser object */
|
||
MimeDisplayOptions *options; /* Data for communicating with libmime.a */
|
||
|
||
/* These are used by FO_QUOTE_HTML_MESSAGE stuff only: */
|
||
int16 lastcsid; /* csid corresponding to above. */
|
||
int16 outcsid; /* csid passed to EDT_PasteQuoteINTL */
|
||
|
||
#ifndef MOZILLA_30
|
||
uint8 rand_buf[6]; /* Random number used in the MATCH
|
||
attribute of the ILAYER tag
|
||
pair that encapsulates a
|
||
text/html part. (The
|
||
attributes must match on the
|
||
ILAYER and the closing
|
||
/ILAYER.) This is used to
|
||
prevent stray layer tags (or
|
||
maliciously placed ones) inside
|
||
an email message allowing the
|
||
message to escape from its
|
||
encapsulated environment. */
|
||
#endif /* MOZILLA_30 */
|
||
|
||
#ifdef DEBUG_terry
|
||
XP_File logit; /* Temp file to put generated HTML into. */
|
||
#endif
|
||
};
|
||
|
||
|
||
struct MimeDisplayData { /* This struct is what we hang off of
|
||
MWContext->mime_data, to remember info
|
||
about the last MIME object we've
|
||
parsed and displayed. See
|
||
MimeGuessURLContentName() below.
|
||
*/
|
||
MimeObject *last_parsed_object;
|
||
char *last_parsed_url;
|
||
|
||
#ifdef LOCK_LAST_CACHED_MESSAGE
|
||
char *previous_locked_url;
|
||
#endif /* LOCK_LAST_CACHED_MESSAGE */
|
||
|
||
#ifndef MOZILLA_30
|
||
MSG_Pane* last_pane;
|
||
#endif /* MOZILLA_30 */
|
||
};
|
||
|
||
|
||
#ifndef MOZILLA_30
|
||
|
||
static MimeHeadersState MIME_HeaderType;
|
||
static XP_Bool MIME_NoInlineAttachments;
|
||
static XP_Bool MIME_WrapLongLines;
|
||
static XP_Bool MIME_VariableWidthPlaintext;
|
||
static XP_Bool MIME_PrefDataValid = 0; /* 0: First time. */
|
||
/* 1: Cache is not valid. */
|
||
/* 2: Cache is valid. */
|
||
|
||
#endif
|
||
|
||
/* #### defined in libmsg/msgutils.c */
|
||
extern NET_StreamClass *
|
||
msg_MakeRebufferingStream (NET_StreamClass *next_stream,
|
||
URL_Struct *url,
|
||
MWContext *context);
|
||
|
||
|
||
static char *
|
||
mime_reformat_date(const char *date, void *stream_closure)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
|
||
MWContext *context = msd->context;
|
||
const char *s;
|
||
time_t t;
|
||
XP_ASSERT(date);
|
||
if (!date) return 0;
|
||
t = XP_ParseTimeString(date, FALSE);
|
||
if (t <= 0) return 0;
|
||
s = MSG_FormatDateFromContext(context, t);
|
||
if (!s) return 0;
|
||
return XP_STRDUP(s);
|
||
}
|
||
|
||
|
||
static char *
|
||
mime_file_type (const char *filename, void *stream_closure)
|
||
{
|
||
NET_cinfo *cinfo = NET_cinfo_find_type ((char *) filename);
|
||
if (!cinfo || !cinfo->type)
|
||
return 0;
|
||
else
|
||
return XP_STRDUP(cinfo->type);
|
||
}
|
||
|
||
static char *
|
||
mime_type_desc(const char *type, void *stream_closure)
|
||
{
|
||
NET_cinfo *cinfo = NET_cinfo_find_info_by_type((char *) type);
|
||
if (!cinfo || !cinfo->desc || !*cinfo->desc)
|
||
return 0;
|
||
else
|
||
return XP_STRDUP(cinfo->desc);
|
||
}
|
||
|
||
|
||
static char *
|
||
mime_type_icon(const char *type, void *stream_closure)
|
||
{
|
||
NET_cinfo *cinfo = NET_cinfo_find_info_by_type((char *) type);
|
||
if (cinfo && cinfo->icon && *cinfo->icon)
|
||
return XP_STRDUP(cinfo->icon);
|
||
else if (!strncasecomp(type, "text/", 5))
|
||
return XP_STRDUP("internal-gopher-text");
|
||
else if (!strncasecomp(type, "image/", 6))
|
||
return XP_STRDUP("internal-gopher-image");
|
||
else if (!strncasecomp(type, "audio/", 6))
|
||
return XP_STRDUP("internal-gopher-sound");
|
||
else if (!strncasecomp(type, "video/", 6))
|
||
return XP_STRDUP("internal-gopher-movie");
|
||
else if (!strncasecomp(type, "application/", 12))
|
||
return XP_STRDUP("internal-gopher-binary");
|
||
else
|
||
return XP_STRDUP("internal-gopher-unknown");
|
||
}
|
||
|
||
|
||
static int
|
||
mime_convert_charset (const char *input_line, int32 input_length,
|
||
const char *input_charset, const char *output_charset,
|
||
char **output_ret, int32 *output_size_ret,
|
||
void *stream_closure)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
|
||
unsigned char *converted;
|
||
|
||
/* #### */
|
||
extern unsigned char *INTL_ConvMailToWinCharCode(MWContext *context,
|
||
unsigned char *pSrc,
|
||
uint32 block_size);
|
||
|
||
converted = INTL_ConvMailToWinCharCode(msd->context,
|
||
(unsigned char *) input_line,
|
||
input_length);
|
||
if (converted)
|
||
{
|
||
*output_ret = (char *) converted;
|
||
*output_size_ret = XP_STRLEN((char *) converted);
|
||
}
|
||
else
|
||
{
|
||
*output_ret = 0;
|
||
*output_size_ret = 0;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
static int
|
||
mime_convert_rfc1522 (const char *input_line, int32 input_length,
|
||
const char *input_charset, const char *output_charset,
|
||
char **output_ret, int32 *output_size_ret,
|
||
void *stream_closure)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
|
||
char *converted;
|
||
char *line;
|
||
|
||
if (input_line[input_length] == 0) /* oh good, it's null-terminated */
|
||
line = (char *) input_line;
|
||
else
|
||
{
|
||
line = (char *) XP_ALLOC(input_length+1);
|
||
if (!line) return MK_OUT_OF_MEMORY;
|
||
XP_MEMCPY(line, input_line, input_length);
|
||
line[input_length] = 0;
|
||
}
|
||
|
||
converted = IntlDecodeMimePartIIStr(line,
|
||
INTL_DocToWinCharSetID(INTL_DefaultDocCharSetID(msd->context)), FALSE);
|
||
|
||
if (line != input_line)
|
||
XP_FREE(line);
|
||
|
||
if (converted)
|
||
{
|
||
*output_ret = converted;
|
||
*output_size_ret = XP_STRLEN(converted);
|
||
}
|
||
else
|
||
{
|
||
*output_ret = 0;
|
||
*output_size_ret = 0;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
static int
|
||
mime_output_fn(char *buf, int32 size, void *stream_closure)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
|
||
XP_ASSERT(msd->stream);
|
||
if (!msd->stream) return -1;
|
||
#ifdef DEBUG_terry
|
||
if (msd->logit) {
|
||
XP_FileWrite(buf, size, msd->logit);
|
||
}
|
||
#endif
|
||
return msd->stream->put_block (msd->stream, buf, size);
|
||
}
|
||
|
||
static int
|
||
compose_only_output_fn(char *buf, int32 size, void *stream_closure)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
extern XP_Bool msg_LoadingForComposeOnly(const MSG_Pane* pane); /* in msgmpane.cpp */
|
||
|
||
static int
|
||
mime_set_html_state_fn (void *stream_closure,
|
||
XP_Bool layer_encapsulate_p,
|
||
XP_Bool start_p,
|
||
XP_Bool abort_p)
|
||
{
|
||
int status = 0;
|
||
char *buf;
|
||
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
|
||
|
||
#if 1
|
||
char random_close_tags[] = "</SCRIPT><NSCP_CLOSE>";
|
||
#else /* 0 */
|
||
char random_close_tags[] =
|
||
"</TABLE></TABLE></TABLE></TABLE></TABLE></TABLE>"
|
||
"</DL></DL></DL></DL></DL></DL></DL></DL></DL></DL>"
|
||
"</DL></DL></DL></DL></DL></DL></DL></DL></DL></DL>"
|
||
"</B></B></B></B></B></B></B></B></B></B></B></B>"
|
||
"</PRE></PRE></PRE></PRE></PRE></PRE></PRE></PRE>"
|
||
"<BASEFONT SIZE=3></SCRIPT>";
|
||
#endif /* 0 */
|
||
if (start_p) {
|
||
#ifndef MOZILLA_30
|
||
if (layer_encapsulate_p && msd->options && !msd->options->nice_html_only_p){
|
||
uint8 *rand_buf = msd->rand_buf;
|
||
RNG_GenerateGlobalRandomBytes(rand_buf, sizeof msd->rand_buf);
|
||
|
||
buf = PR_smprintf("<ILAYER LOCKED CLIP=0,0,AUTO,AUTO "
|
||
"MATCH=%02x%02x%02x%02x%02x%02x>",
|
||
rand_buf[0], rand_buf[1], rand_buf[2],
|
||
rand_buf[3], rand_buf[4], rand_buf[5]);
|
||
if (!buf)
|
||
return MK_OUT_OF_MEMORY;
|
||
status = MimeOptions_write(msd->options, buf, XP_STRLEN(buf), TRUE);
|
||
XP_FREE(buf);
|
||
}
|
||
#endif /* MOZILLA_30 */
|
||
} else {
|
||
status = MimeOptions_write(msd->options, random_close_tags,
|
||
XP_STRLEN(random_close_tags), FALSE);
|
||
if (status < 0)
|
||
return status;
|
||
|
||
#ifndef MOZILLA_30
|
||
if (layer_encapsulate_p && msd->options && !msd->options->nice_html_only_p){
|
||
uint8 *rand_buf = msd->rand_buf;
|
||
buf = PR_smprintf("</ILAYER MATCH=%02x%02x%02x%02x%02x%02x><BR>",
|
||
rand_buf[0], rand_buf[1], rand_buf[2],
|
||
rand_buf[3], rand_buf[4], rand_buf[5]);
|
||
if (!buf)
|
||
return MK_OUT_OF_MEMORY;
|
||
status = MimeOptions_write(msd->options, buf, XP_STRLEN(buf), TRUE);
|
||
XP_FREE(buf);
|
||
}
|
||
#endif /* MOZILLA_30 */
|
||
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
static int
|
||
mime_display_stream_write (NET_StreamClass *stream,
|
||
const char* buf,
|
||
int32 size)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream->data_object;
|
||
MimeObject *obj = (msd ? msd->obj : 0);
|
||
if (!obj) return -1;
|
||
return obj->class->parse_buffer((char *) buf, size, obj);
|
||
}
|
||
|
||
|
||
static unsigned int
|
||
mime_display_stream_write_ready (NET_StreamClass *stream)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream->data_object;
|
||
if (msd->istream) {
|
||
return msd->istream->is_write_ready (msd->istream);
|
||
} else if (msd->stream)
|
||
return msd->stream->is_write_ready (msd->stream);
|
||
else
|
||
return (MAX_WRITE_READY);
|
||
}
|
||
|
||
|
||
extern void MSG_MimeNotifyCryptoAttachmentKludge(NET_StreamClass *);
|
||
|
||
static void
|
||
mime_display_stream_complete (NET_StreamClass *stream)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream->data_object;
|
||
MimeObject *obj = (msd ? msd->obj : 0);
|
||
if (obj)
|
||
{
|
||
int status;
|
||
status = obj->class->parse_eof(obj, FALSE);
|
||
obj->class->parse_end(obj, (status < 0 ? TRUE : FALSE));
|
||
|
||
#ifdef HAVE_MIME_DATA_SLOT
|
||
if (msd &&
|
||
msd->context &&
|
||
msd->context->mime_data &&
|
||
obj == msd->context->mime_data->last_parsed_object)
|
||
{
|
||
/* do nothing -- we still have another pointer to this object. */
|
||
}
|
||
else
|
||
#endif /* HAVE_MIME_DATA_SLOT */
|
||
{
|
||
/* Somehow there's no pointer in context->mime_data to this
|
||
object, so destroy it now. (This can happen for any number
|
||
of normal reasons; see comment in mime_output_init_fn().)
|
||
*/
|
||
XP_ASSERT(msd->options == obj->options);
|
||
mime_free(obj);
|
||
obj = NULL;
|
||
if (msd->options)
|
||
{
|
||
FREEIF(msd->options->part_to_load);
|
||
FREEIF(msd->options->default_charset);
|
||
FREEIF(msd->options->override_charset);
|
||
XP_FREE(msd->options);
|
||
msd->options = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef LOCK_LAST_CACHED_MESSAGE
|
||
/* The code in this ifdef is to ensure that the most-recently-loaded news
|
||
message is locked down in the memory cache. (There may be more than one
|
||
message cached, but only the most-recently-loaded is *locked*.)
|
||
|
||
When loading a message, we unlock the previously-locked URL, if any.
|
||
(This happens in mime_make_output_stream().)
|
||
|
||
When we're done loading a news message, we lock it to prevent it from
|
||
going away (this happens here, in mime_display_stream_complete(). We
|
||
need to do this at the end instead of the beginning so that the document
|
||
is *in* the cache at the time we try to lock it.)
|
||
|
||
This implementation probably assumes that news messages go into the
|
||
memory cache and never go into the disk cache. (But maybe that's
|
||
actually not an issue, since if news messages were to go into the
|
||
disk cache, they would be marked as non-session-persistent anyway?)
|
||
*/
|
||
if (msd &&
|
||
msd->context &&
|
||
msd->context->mime_data &&
|
||
!msd->context->mime_data->previous_locked_url)
|
||
{
|
||
/* Save a copy of this URL so that we can unlock it next time. */
|
||
msd->context->mime_data->previous_locked_url =
|
||
XP_STRDUP(msd->url->address);
|
||
|
||
/* Lock this URL in the cache. */
|
||
if (msd->context->mime_data->previous_locked_url)
|
||
NET_ChangeCacheFileLock(msd->url, TRUE);
|
||
}
|
||
#endif /* LOCK_LAST_CACHED_MESSAGE */
|
||
|
||
if (msd->stream)
|
||
{
|
||
|
||
/* Hold your breath. This is how we notify compose.c
|
||
that the message was decrypted -- it will set a flag in the
|
||
mime_delivery_state structure, which we don't have access to from
|
||
here. */
|
||
if (obj &&
|
||
obj->options &&
|
||
obj->options->state &&
|
||
obj->options->state->decrypted_p)
|
||
MSG_MimeNotifyCryptoAttachmentKludge(msd->stream);
|
||
|
||
/* Close the output stream. */
|
||
msd->stream->complete (msd->stream);
|
||
XP_FREE (msd->stream);
|
||
}
|
||
#ifdef DEBUG_terry
|
||
if (msd->logit) XP_FileClose(msd->logit);
|
||
#endif
|
||
XP_FREE(msd);
|
||
}
|
||
|
||
static void
|
||
mime_display_stream_abort (NET_StreamClass *stream, int status)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream->data_object;
|
||
MimeObject *obj = (msd ? msd->obj : 0);
|
||
if (obj)
|
||
{
|
||
if (!obj->closed_p)
|
||
obj->class->parse_eof(obj, TRUE);
|
||
if (!obj->parsed_p)
|
||
obj->class->parse_end(obj, TRUE);
|
||
|
||
#ifdef HAVE_MIME_DATA_SLOT
|
||
if (msd &&
|
||
msd->context &&
|
||
msd->context->mime_data &&
|
||
obj == msd->context->mime_data->last_parsed_object)
|
||
{
|
||
/* do nothing -- we still have another pointer to this object. */
|
||
}
|
||
else
|
||
#endif /* HAVE_MIME_DATA_SLOT */
|
||
{
|
||
/* Somehow there's no pointer in context->mime_data to this
|
||
object, so destroy it now. (This can happen for any number
|
||
of normal reasons; see comment in mime_output_init_fn().)
|
||
*/
|
||
XP_ASSERT(msd->options == obj->options);
|
||
mime_free(obj);
|
||
if (msd->options)
|
||
{
|
||
FREEIF(msd->options->part_to_load);
|
||
XP_FREE(msd->options);
|
||
msd->options = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (msd->stream)
|
||
{
|
||
msd->stream->abort (msd->stream, status);
|
||
XP_FREE (msd->stream);
|
||
}
|
||
XP_FREE(msd);
|
||
}
|
||
|
||
|
||
|
||
#ifndef MOZILLA_30
|
||
|
||
static unsigned int
|
||
mime_insert_html_write_ready(NET_StreamClass *stream)
|
||
{
|
||
return MAX_WRITE_READY;
|
||
}
|
||
|
||
static int
|
||
mime_insert_html_put_block(NET_StreamClass *stream, const char* str, int32 length)
|
||
{
|
||
struct mime_stream_data* msd = (struct mime_stream_data*) stream->data_object;
|
||
char* s = (char*) str;
|
||
char c = s[length];
|
||
XP_ASSERT(msd);
|
||
if (!msd) return -1;
|
||
if (c) {
|
||
s[length] = '\0';
|
||
}
|
||
/* s is in the outcsid encoding at this point. That was done in
|
||
* mime_insert_html_convert_charset */
|
||
EDT_PasteQuoteINTL(msd->context, s, msd->outcsid);
|
||
if (c) {
|
||
s[length] = c;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
static void
|
||
mime_insert_html_complete(NET_StreamClass *stream)
|
||
{
|
||
struct mime_stream_data* msd = (struct mime_stream_data*) stream->data_object;
|
||
XP_ASSERT(msd);
|
||
if (!msd) return;
|
||
EDT_PasteQuote(msd->context, "</BLOCKQUOTE>");
|
||
if (msd->format_out == FO_QUOTE_HTML_MESSAGE) {
|
||
int32 eReplyOnTop = 1, nReplyWithExtraLines = 0;
|
||
PREF_GetIntPref("mailnews.reply_on_top", &eReplyOnTop);
|
||
PREF_GetIntPref("mailnews.reply_with_extra_lines", &nReplyWithExtraLines);
|
||
if (0 == eReplyOnTop && nReplyWithExtraLines) {
|
||
for (; nReplyWithExtraLines > 0; nReplyWithExtraLines--)
|
||
EDT_PasteQuote(msd->context, "<BR>");
|
||
}
|
||
|
||
}
|
||
EDT_PasteQuoteEnd(msd->context);
|
||
}
|
||
|
||
static void
|
||
mime_insert_html_abort(NET_StreamClass *stream, int status)
|
||
{
|
||
mime_insert_html_complete(stream);
|
||
}
|
||
|
||
|
||
static int
|
||
mime_insert_html_convert_charset (const char *input_line, int32 input_length,
|
||
const char *input_charset,
|
||
const char *output_charset,
|
||
char **output_ret, int32 *output_size_ret,
|
||
void *stream_closure)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
|
||
int status;
|
||
INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(msd->context);
|
||
uint16 old_csid = INTL_GetCSIDocCSID(csi);
|
||
|
||
if (input_charset) {
|
||
msd->lastcsid = INTL_CharSetNameToID((char*) input_charset);
|
||
} else {
|
||
msd->lastcsid = 0;
|
||
}
|
||
if (output_charset) {
|
||
msd->outcsid = INTL_CharSetNameToID((char*) output_charset);
|
||
} else {
|
||
msd->outcsid = 0;
|
||
}
|
||
INTL_SetCSIDocCSID(csi, msd->lastcsid);
|
||
status = mime_convert_charset (input_line, input_length,
|
||
input_charset, output_charset,
|
||
output_ret, output_size_ret,
|
||
stream_closure);
|
||
INTL_SetCSIDocCSID(csi, old_csid);
|
||
return status;
|
||
}
|
||
|
||
#endif /* !MOZILLA_30 */
|
||
|
||
|
||
static NET_StreamClass *
|
||
mime_make_output_stream(const char *content_type,
|
||
const char *charset,
|
||
const char *content_name,
|
||
const char *x_mac_type,
|
||
const char *x_mac_creator,
|
||
int format_out, URL_Struct *url,
|
||
MWContext *context,
|
||
struct mime_stream_data* msd)
|
||
{
|
||
/* To make the next stream, fill in the URL with the provided attributes,
|
||
and call NET_StreamBuilder.
|
||
|
||
But After we've gotten the stream we want, change the URL's type and
|
||
encoding back to what they were before, since things down the line might
|
||
need to know the *original* type.
|
||
*/
|
||
NET_StreamClass *stream;
|
||
char *orig_content_type;
|
||
char *orig_encoding;
|
||
char *old_part = 0;
|
||
char *old_part2 = 0;
|
||
|
||
#ifndef MOZILLA_30
|
||
if (format_out == FO_QUOTE_HTML_MESSAGE) {
|
||
/* Special case here. Make a stream that just jams data directly
|
||
into our editor context. No calling of NET_StreamBuilder for me;
|
||
I don't really understand it anyway... */
|
||
|
||
XP_ASSERT(msd);
|
||
if (msd) {
|
||
stream = XP_NEW_ZAP(NET_StreamClass);
|
||
if (!stream) return NULL;
|
||
stream->window_id = context;
|
||
stream->data_object = msd;
|
||
stream->is_write_ready = mime_insert_html_write_ready;
|
||
stream->put_block = mime_insert_html_put_block;
|
||
stream->complete = mime_insert_html_complete;
|
||
stream->abort = mime_insert_html_abort;
|
||
return stream;
|
||
}
|
||
}
|
||
#endif /* !MOZILLA_30 */
|
||
|
||
|
||
XP_ASSERT(content_type && url);
|
||
if (!content_type || !url)
|
||
return 0;
|
||
|
||
/* If we're saving a message to disk (raw), then treat the output as unknown
|
||
type. If we didn't do this, then saving a message would recurse back into
|
||
the parser (because the output has content-type message/rfc822), and we'd
|
||
be back here again in short order...
|
||
|
||
We don't do this for FO_SAVE_AS_TEXT and FO_SAVE_AS_POSTSCRIPT because
|
||
those work by generating HTML, and then converting that. Whereas
|
||
FO_SAVE_AS goes directly to disk without another format_out filter
|
||
catching it.
|
||
|
||
We only fake out the content-type when we're saving a message (mail or
|
||
news) and not when saving random other types. The reason for this is that
|
||
we only *need* to do this for messages (because those are the only types
|
||
which are registered to come back in here for FO_SAVE_AS); we don't do it
|
||
for other types, because the Mac needs to know the real type to map a
|
||
MIME type to a Mac Creator when writing the file to disk (so that the
|
||
right application will get launched when the file is clicked on, etc.)
|
||
|
||
[mwelch: I'm not adding FO_EDT_SAVE_IMAGE here, because the editor, to
|
||
the best of my knowledge, never spools out a message per se; such a message
|
||
would be filed as an attachment (FO_CACHE_AND_MAIL_TO), independent of the
|
||
editor. In addition, we want to drill down as far as we can within a quoted
|
||
message, in order to identify whatever part has been requested (usually an
|
||
image, sound, applet, or other inline data).]
|
||
|
||
In 3.0b7 and earlier, we did this for *all* types. On 9-Aug-96 jwz and
|
||
aleks changed this to only do it for message types, which seems to be the
|
||
right thing. However, since it's very late in the release cycle, this
|
||
change is being done as #ifdef XP_MAC, since the Mac is the only platform
|
||
where it was a problem that we were using octet-stream for all
|
||
attachments. After 3.0 ships, this should be done on all platforms, not
|
||
just Mac.
|
||
*/
|
||
if ((format_out == FO_SAVE_AS || format_out == FO_CACHE_AND_SAVE_AS)
|
||
&& (!strcasecomp(content_type, MESSAGE_RFC822) ||
|
||
!strcasecomp(content_type, MESSAGE_NEWS)))
|
||
content_type = APPLICATION_OCTET_STREAM;
|
||
|
||
orig_content_type = url->content_type;
|
||
orig_encoding = url->content_encoding;
|
||
|
||
url->content_type = XP_STRDUP(content_type);
|
||
if (!url->content_type) return 0;
|
||
url->content_encoding = 0;
|
||
|
||
if (charset) FREEIF(url->charset);
|
||
if (content_name) FREEIF(url->content_name);
|
||
if (x_mac_type) FREEIF(url->x_mac_type);
|
||
if (x_mac_creator) FREEIF(url->x_mac_creator);
|
||
if (charset) url->charset = XP_STRDUP(charset);
|
||
if (content_name) url->content_name = XP_STRDUP(content_name);
|
||
if (x_mac_type) url->x_mac_type = XP_STRDUP(x_mac_type);
|
||
if (x_mac_creator) url->x_mac_creator = XP_STRDUP(x_mac_creator);
|
||
|
||
/* If we're going back down into the message/rfc822 parser (that is, if
|
||
we're displaying a sub-part which is itself a message part) then remove
|
||
any part specifier from the URL. This is to prevent that same part
|
||
from being retreived a second time, which to the user, would have no
|
||
effect.
|
||
|
||
Don't do this for all other types, because that might affect image
|
||
cacheing and so on (causing it to cache on the wrong key.)
|
||
*/
|
||
if (!strcasecomp(content_type, MESSAGE_RFC822) ||
|
||
!strcasecomp(content_type, MESSAGE_NEWS))
|
||
{
|
||
old_part = XP_STRSTR(url->address, "?part=");
|
||
if (!old_part)
|
||
old_part2 = XP_STRSTR(url->address, "&part=");
|
||
|
||
if (old_part) *old_part = 0;
|
||
else if (old_part2) *old_part2 = 0;
|
||
}
|
||
|
||
if(msd && msd->options && msd->options->default_charset)
|
||
{
|
||
INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(context);
|
||
INTL_SetCSIMimeCharset(csi, msd->options->default_charset);
|
||
}
|
||
|
||
stream = NET_StreamBuilder (format_out, url, context);
|
||
if (stream)
|
||
{
|
||
NET_StreamClass * buffer = msg_MakeRebufferingStream(stream, url, context);
|
||
if (buffer)
|
||
stream = buffer;
|
||
}
|
||
|
||
/* Put it back -- note that this string is now also pointed to by
|
||
obj->options->url, so we're modifying data reachable by the
|
||
internals of the library (and that is the goal of this hack.) */
|
||
if (old_part) *old_part = '?';
|
||
else if (old_part2) *old_part2 = '&';
|
||
|
||
FREEIF(url->content_type);
|
||
url->content_type = orig_content_type;
|
||
url->content_encoding = orig_encoding;
|
||
|
||
#ifdef LOCK_LAST_CACHED_MESSAGE
|
||
/* Always cache this one. */
|
||
url->must_cache = TRUE;
|
||
|
||
/* Un-cache the last one. */
|
||
if (context->mime_data &&
|
||
context->mime_data->previous_locked_url)
|
||
{
|
||
URL_Struct *url_s =
|
||
NET_CreateURLStruct(context->mime_data->previous_locked_url,
|
||
NET_NORMAL_RELOAD);
|
||
if (url_s)
|
||
{
|
||
/* Note: if post data was involved here, we'd lose. We're assuming
|
||
that all we need to save is the url->address. */
|
||
NET_ChangeCacheFileLock(url_s, FALSE);
|
||
NET_FreeURLStruct(url_s);
|
||
}
|
||
XP_FREE(context->mime_data->previous_locked_url);
|
||
context->mime_data->previous_locked_url = 0;
|
||
}
|
||
#endif /* LOCK_LAST_CACHED_MESSAGE */
|
||
|
||
return stream;
|
||
}
|
||
|
||
|
||
|
||
static int
|
||
mime_output_init_fn (const char *type,
|
||
const char *charset,
|
||
const char *name,
|
||
const char *x_mac_type,
|
||
const char *x_mac_creator,
|
||
void *stream_closure)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
|
||
int format_out;
|
||
XP_ASSERT(!msd->stream);
|
||
if (msd->stream) return -1;
|
||
|
||
XP_ASSERT(type && *type);
|
||
if (!type || !*type) return -1;
|
||
|
||
format_out = msd->format_out;
|
||
|
||
/* If we've converted to HTML, then we've already done charset conversion,
|
||
so label this data as "internal/parser" to prevent it from being passed
|
||
through the charset converters again. */
|
||
if (msd->options->write_html_p &&
|
||
!strcasecomp(type, TEXT_HTML))
|
||
type = INTERNAL_PARSER;
|
||
|
||
|
||
/* If this stream converter was created for FO_MAIL_TO, then the input type
|
||
and output type will be identical (message/rfc822) so we need to change
|
||
the format_out to break out of the loop. libmsg/compose.c knows to treat
|
||
FO_MAIL_MESSAGE_TO roughly the same as FO_MAIL_TO.
|
||
*/
|
||
#ifdef FO_MAIL_MESSAGE_TO
|
||
if (format_out == FO_MAIL_TO)
|
||
format_out = FO_MAIL_MESSAGE_TO;
|
||
else if (format_out == FO_CACHE_AND_MAIL_TO)
|
||
format_out = FO_CACHE_AND_MAIL_MESSAGE_TO;
|
||
#endif /* FO_MAIL_MESSAGE_TO */
|
||
|
||
|
||
msd->stream = mime_make_output_stream(type, charset, name,
|
||
x_mac_type, x_mac_creator,
|
||
format_out, msd->url,
|
||
msd->context, msd);
|
||
|
||
if (!msd->stream)
|
||
/* #### We can't return MK_OUT_OF_MEMORY here because sometimes
|
||
NET_StreamBuilder() returns 0 because it ran out of memory; and
|
||
sometimes it returns 0 because the user hit Cancel on the file
|
||
save dialog box. Wonderful condition handling we've got... */
|
||
return -1;
|
||
|
||
|
||
#ifdef HAVE_MIME_DATA_SLOT
|
||
/* At this point, the window has been cleared; so discard the cached
|
||
data relating to the previously-parsed MIME object. */
|
||
|
||
XP_ASSERT(msd && msd->obj && msd->context);
|
||
if (msd && msd->obj && msd->context)
|
||
{
|
||
MWContext *context = msd->context;
|
||
|
||
if (msd->context->mime_data &&
|
||
msd->context->mime_data->last_parsed_object)
|
||
{
|
||
XP_ASSERT(msd->options !=
|
||
context->mime_data->last_parsed_object->options);
|
||
XP_ASSERT(msd->obj != context->mime_data->last_parsed_object);
|
||
|
||
/* There are two interesting cases:
|
||
|
||
Look at message-1, then look at message-2; and
|
||
Look at message-1, then look at message-1-part-A
|
||
(where part-A is itself a message.)
|
||
|
||
In the first case, by the time we've begun message-2, we're done
|
||
with message-1. Its object is still around for reference (for
|
||
examining the part names) but the parser has run to completion
|
||
on it.
|
||
|
||
In the second case, we begin parsing part-A before the parser of
|
||
message-1 has run to completion -- in fact, the parser that is
|
||
operating on message-1 is still on the stack above us.
|
||
|
||
So if there is a last_parsed_object, but that object does not have
|
||
its `parsed_p' slot set, then that means that that object has not
|
||
yet hit the `parse_eof' state; therefore, it is still *being*
|
||
parsed: it is a parent object of the one we're currently looking
|
||
at. (When presenting a part of type message/rfc822, the
|
||
MIME_MessageConverter stream is entered recursively, the first
|
||
time to extract the sub-message, and the second time to convert
|
||
that sub-message to HTML.)
|
||
|
||
So if we're in this nested call, we can simply replace the pointer
|
||
in mime_data. When current MessageConverter stream reaches its
|
||
`complete' or `abort' methods, it will skip freeing the current
|
||
object (since there is a pointer to it in mime_data); and then when
|
||
the outer MessageConverter stream reaches its `complete' or `abort'
|
||
methods, it will free that outer object (since it will see that
|
||
there is no pointer to it in mime_data.)
|
||
|
||
More precisely:
|
||
|
||
Look at message-1, then look at message-2:
|
||
In this case, the flow of control is like this:
|
||
|
||
= make object-1 (message/rfc822)
|
||
= parse it
|
||
= save it in context->mime_data
|
||
= done with object-1; free nothing.
|
||
|
||
= make object-2 (message/rfc822)
|
||
= parse it
|
||
= note that object-1 is still in context->mime_data
|
||
= free object-1
|
||
= save object-2 in context->mime_data
|
||
= done with object-2; free nothing.
|
||
|
||
Look at message-1, then look at message-1-part-A:
|
||
The flow of control in this case is somewhat different:
|
||
|
||
= make object-1 (message/rfc822)
|
||
= parse it
|
||
= save it in context->mime_data
|
||
= done with object-1; free nothing.
|
||
|
||
= make object-1 (message/rfc822)
|
||
= parse it
|
||
= note that previous-object-1 is still in context->mime_data
|
||
= free previous-object-1
|
||
= save object-1 in context->mime_data
|
||
= enter the parser recursively:
|
||
= make part-A (message/rfc822)
|
||
= parse it
|
||
= note that object1 is still in context->mime_data,
|
||
and has not yet been fully parsed (in other words,
|
||
it's still on the stack.) Don't touch it.
|
||
= save part-A in context->mime_data
|
||
(cutting the pointer to object-1)
|
||
= done with part-A; free nothing.
|
||
= done with object-1;
|
||
note that object-1 is *not* in the context->mime_data
|
||
= free object-1
|
||
= result: part-A remains in context->mime_data
|
||
*/
|
||
|
||
if (context->mime_data->last_parsed_object->parsed_p)
|
||
{
|
||
/* Free it if it's parsed.
|
||
|
||
Note that we have to call mime_free() before we free the
|
||
options struct, not after -- so since mime_free() frees
|
||
the object, we have to pull `options' out first to avoid
|
||
reaching into freed memory.
|
||
*/
|
||
MimeDisplayOptions *options =
|
||
context->mime_data->last_parsed_object->options;
|
||
|
||
mime_free(context->mime_data->last_parsed_object);
|
||
if (options)
|
||
{
|
||
FREEIF(options->part_to_load);
|
||
XP_FREE(options);
|
||
}
|
||
}
|
||
|
||
/* Cut the old saved pointer, whether its parsed or not. */
|
||
context->mime_data->last_parsed_object = 0;
|
||
FREEIF(context->mime_data->last_parsed_url);
|
||
}
|
||
|
||
/* And now save away the current object, for consultation the first
|
||
time a link is clicked upon. */
|
||
if (!context->mime_data)
|
||
{
|
||
context->mime_data = XP_NEW(struct MimeDisplayData);
|
||
if (!context->mime_data)
|
||
return MK_OUT_OF_MEMORY;
|
||
XP_MEMSET(context->mime_data, 0, sizeof(*context->mime_data));
|
||
}
|
||
context->mime_data->last_parsed_object = msd->obj;
|
||
context->mime_data->last_parsed_url = XP_STRDUP(msd->url->address);
|
||
#ifndef MOZILLA_30
|
||
context->mime_data->last_pane = msd->url->msg_pane;
|
||
#endif /* MOZILLA_30 */
|
||
|
||
XP_ASSERT(!msd->options ||
|
||
msd->options == msd->obj->options);
|
||
}
|
||
#endif /* HAVE_MIME_DATA_SLOT */
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
static void *mime_image_begin(const char *image_url, const char *content_type,
|
||
void *stream_closure);
|
||
static void mime_image_end(void *image_closure, int status);
|
||
static char *mime_image_make_image_html(void *image_data);
|
||
static int mime_image_write_buffer(char *buf, int32 size, void *image_closure);
|
||
|
||
#ifdef MOZILLA_30
|
||
extern XP_Bool MSG_VariableWidthPlaintext;
|
||
#endif
|
||
|
||
|
||
#ifndef MOZILLA_30
|
||
int PR_CALLBACK
|
||
mime_PrefsChangeCallback(const char* prefname, void* data)
|
||
{
|
||
MIME_PrefDataValid = 1; /* Invalidates our cached stuff. */
|
||
return PREF_NOERROR;
|
||
}
|
||
#endif /* !MOZILLA_30 */
|
||
|
||
|
||
NET_StreamClass *
|
||
MIME_MessageConverter (int format_out, void *closure,
|
||
URL_Struct *url, MWContext *context)
|
||
{
|
||
int status = 0;
|
||
MimeObject *obj;
|
||
struct mime_stream_data *msd;
|
||
NET_StreamClass *stream = 0;
|
||
|
||
#ifdef FO_MAIL_MESSAGE_TO
|
||
if (format_out == FO_MAIL_MESSAGE_TO ||
|
||
format_out == FO_CACHE_AND_MAIL_MESSAGE_TO)
|
||
{
|
||
/* Bad news -- this would cause an endless loop. */
|
||
XP_ASSERT(0);
|
||
return 0;
|
||
}
|
||
#else /* !FO_MAIL_MESSAGE_TO */
|
||
/* Otherwise, we oughtn't be getting in here at all. */
|
||
XP_ASSERT(format_out != FO_MAIL_TO && format_out != FO_CACHE_AND_MAIL_TO);
|
||
#endif /* !FO_MAIL_MESSAGE_TO */
|
||
|
||
msd = XP_NEW(struct mime_stream_data);
|
||
if (!msd) return 0;
|
||
XP_MEMSET(msd, 0, sizeof(*msd));
|
||
#ifdef DEBUG_terry
|
||
#if defined(XP_WIN) || defined(XP_OS2)
|
||
msd->logit = XP_FileOpen("C:\\temp\\twtemp.html", xpTemporary, XP_FILE_WRITE);
|
||
#endif
|
||
#endif
|
||
msd->url = url;
|
||
msd->context = context;
|
||
msd->format_out = format_out;
|
||
|
||
msd->options = XP_NEW(MimeDisplayOptions);
|
||
if (!msd->options)
|
||
{
|
||
XP_FREE(msd);
|
||
return 0;
|
||
}
|
||
XP_MEMSET(msd->options, 0, sizeof(*msd->options));
|
||
#ifndef MOZILLA_30
|
||
msd->options->pane = url->msg_pane;
|
||
#endif /* !MOZILLA_30 */
|
||
|
||
if ((format_out == FO_PRESENT || format_out == FO_CACHE_AND_PRESENT) &&
|
||
url->fe_data)
|
||
{
|
||
/* If we're going to the screen, and the URL has fe_data, then it is
|
||
an options structure (that is how the news code hands us its callback
|
||
functions.) We copy it and free the passed-in data right away.
|
||
(If we're not going to the screen, the fe_data might be some random
|
||
object intended for someone further down the line; for example, it
|
||
could be the XP_File that FO_SAVE_TO_DISK needs to pass around.)
|
||
*/
|
||
MimeDisplayOptions *opt2 = (MimeDisplayOptions *) url->fe_data;
|
||
*msd->options = *opt2; /* copies */
|
||
XP_FREE (opt2);
|
||
url->fe_data = 0;
|
||
msd->options->attachment_icon_layer_id = 0; /* Sigh... */
|
||
}
|
||
|
||
/* Set the defaults, based on the context, and the output-type.
|
||
*/
|
||
if (format_out == FO_PRESENT ||
|
||
format_out == FO_CACHE_AND_PRESENT ||
|
||
format_out == FO_PRINT ||
|
||
format_out == FO_CACHE_AND_PRINT ||
|
||
format_out == FO_SAVE_AS_POSTSCRIPT ||
|
||
format_out == FO_CACHE_AND_SAVE_AS_POSTSCRIPT)
|
||
msd->options->fancy_headers_p = TRUE;
|
||
|
||
#ifndef MOZILLA_30
|
||
if (format_out == FO_PRESENT ||
|
||
format_out == FO_CACHE_AND_PRESENT)
|
||
msd->options->output_vcard_buttons_p = TRUE;
|
||
#endif /* !MOZILLA_30 */
|
||
|
||
if (format_out == FO_PRESENT ||
|
||
format_out == FO_CACHE_AND_PRESENT) {
|
||
msd->options->fancy_links_p = TRUE;
|
||
}
|
||
|
||
msd->options->headers = MimeHeadersSome;
|
||
|
||
#ifndef MOZILLA_30
|
||
if (MIME_PrefDataValid < 2) {
|
||
int32 headertype;
|
||
if (MIME_PrefDataValid == 0) {
|
||
PREF_RegisterCallback("mail.", &mime_PrefsChangeCallback, NULL);
|
||
}
|
||
headertype = 1;
|
||
PREF_GetIntPref("mail.show_headers", &headertype);
|
||
switch (headertype) {
|
||
case 0: MIME_HeaderType = MimeHeadersMicro; break;
|
||
case 1: MIME_HeaderType = MimeHeadersSome; break;
|
||
case 2: MIME_HeaderType = MimeHeadersAll; break;
|
||
default:
|
||
XP_ASSERT(FALSE);
|
||
break;
|
||
}
|
||
MIME_NoInlineAttachments = TRUE;
|
||
PREF_GetBoolPref("mail.inline_attachments", &MIME_NoInlineAttachments);
|
||
MIME_NoInlineAttachments = !MIME_NoInlineAttachments;
|
||
/* This pref is written down in with the
|
||
opposite sense of what we like to use... */
|
||
MIME_WrapLongLines = FALSE;
|
||
PREF_GetBoolPref("mail.wrap_long_lines", &MIME_WrapLongLines);
|
||
MIME_VariableWidthPlaintext = TRUE;
|
||
PREF_GetBoolPref("mail.fixed_width_messages",
|
||
&MIME_VariableWidthPlaintext);
|
||
MIME_VariableWidthPlaintext = !MIME_VariableWidthPlaintext;
|
||
/* This pref is written down in with the
|
||
opposite sense of what we like to use... */
|
||
MIME_PrefDataValid = 2;
|
||
}
|
||
msd->options->no_inline_p = MIME_NoInlineAttachments;
|
||
msd->options->wrap_long_lines_p = MIME_WrapLongLines;
|
||
msd->options->headers = MIME_HeaderType;
|
||
#endif /* !MOZILLA_30 */
|
||
|
||
if (context->type == MWContextMail ||
|
||
context->type == MWContextNews
|
||
#ifndef MOZILLA_30
|
||
|| context->type == MWContextMailMsg
|
||
|| context->type == MWContextNewsMsg
|
||
#endif /* !MOZILLA_30 */
|
||
)
|
||
{
|
||
#ifndef MOZILLA_30
|
||
MSG_Pane* pane = MSG_FindPane(context, MSG_MESSAGEPANE);
|
||
msd->options->rot13_p = FALSE;
|
||
if (pane) {
|
||
msd->options->rot13_p = MSG_ShouldRot13Message(pane);
|
||
}
|
||
#else /* MOZILLA_30 */
|
||
|
||
XP_Bool all_headers_p = FALSE;
|
||
XP_Bool micro_headers_p = FALSE;
|
||
MSG_GetContextPrefs(context,
|
||
&all_headers_p,
|
||
µ_headers_p,
|
||
&msd->options->no_inline_p,
|
||
&msd->options->rot13_p,
|
||
&msd->options->wrap_long_lines_p);
|
||
if (all_headers_p)
|
||
msd->options->headers = MimeHeadersAll;
|
||
else if (micro_headers_p)
|
||
msd->options->headers = MimeHeadersMicro;
|
||
else
|
||
msd->options->headers = MimeHeadersSome;
|
||
|
||
#endif /* MOZILLA_30 */
|
||
}
|
||
|
||
status = mime_parse_url_options(url->address, msd->options);
|
||
if (status < 0)
|
||
{
|
||
FREEIF(msd->options->part_to_load);
|
||
XP_FREE(msd->options);
|
||
XP_FREE(msd);
|
||
return 0;
|
||
}
|
||
|
||
if (msd->options->headers == MimeHeadersMicro &&
|
||
#ifndef MOZILLA_30
|
||
(url->address == NULL || (XP_STRNCMP(url->address, "news:", 5) != 0 &&
|
||
XP_STRNCMP(url->address, "snews:", 6) != 0))
|
||
#else /* MOZILLA_30 */
|
||
context->type == MWContextMail
|
||
#endif /* MOZILLA_30 */
|
||
)
|
||
msd->options->headers = MimeHeadersMicroPlus;
|
||
|
||
if (format_out == FO_QUOTE_MESSAGE ||
|
||
format_out == FO_CACHE_AND_QUOTE_MESSAGE
|
||
#ifndef MOZILLA_30
|
||
|| format_out == FO_QUOTE_HTML_MESSAGE
|
||
#endif /* !MOZILLA_30 */
|
||
)
|
||
{
|
||
msd->options->headers = MimeHeadersCitation;
|
||
msd->options->fancy_headers_p = FALSE;
|
||
#ifndef MOZILLA_30
|
||
if (format_out == FO_QUOTE_HTML_MESSAGE) {
|
||
msd->options->nice_html_only_p = TRUE;
|
||
}
|
||
#endif /* !MOZILLA_30 */
|
||
}
|
||
|
||
else if (msd->options->headers == MimeHeadersSome &&
|
||
(format_out == FO_PRINT ||
|
||
format_out == FO_CACHE_AND_PRINT ||
|
||
format_out == FO_SAVE_AS_POSTSCRIPT ||
|
||
format_out == FO_CACHE_AND_SAVE_AS_POSTSCRIPT ||
|
||
format_out == FO_SAVE_AS_TEXT ||
|
||
format_out == FO_CACHE_AND_SAVE_AS_TEXT))
|
||
msd->options->headers = MimeHeadersSomeNoRef;
|
||
|
||
#ifdef FO_MAIL_MESSAGE_TO
|
||
/* If we're attaching a message (for forwarding) then we must eradicate all
|
||
traces of encryption from it, since forwarding someone else a message
|
||
that wasn't encrypted for them doesn't work. We have to decrypt it
|
||
before sending it.
|
||
*/
|
||
if ((format_out == FO_MAIL_TO || format_out == FO_CACHE_AND_MAIL_TO) &&
|
||
msd->options->write_html_p == FALSE)
|
||
msd->options->decrypt_p = TRUE;
|
||
#endif /* FO_MAIL_MESSAGE_TO */
|
||
|
||
msd->options->url = url->address;
|
||
msd->options->write_html_p = TRUE;
|
||
msd->options->output_init_fn = mime_output_init_fn;
|
||
|
||
#if !defined(MOZILLA_30) && defined(XP_MAC)
|
||
/* If it's a thread context, don't output all the mime stuff (hangs on Macintosh for
|
||
** unexpanded threadpane, because HTML is generated that needs images and layers).
|
||
*/
|
||
if (context->type == MWContextMail)
|
||
msd->options->output_fn = compose_only_output_fn;
|
||
else
|
||
#endif /* XP_MAC */
|
||
msd->options->output_fn = mime_output_fn;
|
||
|
||
msd->options->set_html_state_fn = mime_set_html_state_fn;
|
||
#ifndef MOZILLA_30
|
||
if (format_out == FO_QUOTE_HTML_MESSAGE) {
|
||
msd->options->charset_conversion_fn = mime_insert_html_convert_charset;
|
||
msd->options->dont_touch_citations_p = TRUE;
|
||
} else
|
||
#endif
|
||
msd->options->charset_conversion_fn = mime_convert_charset;
|
||
msd->options->rfc1522_conversion_fn = mime_convert_rfc1522;
|
||
msd->options->reformat_date_fn = mime_reformat_date;
|
||
msd->options->file_type_fn = mime_file_type;
|
||
msd->options->type_description_fn = mime_type_desc;
|
||
msd->options->type_icon_name_fn = mime_type_icon;
|
||
msd->options->stream_closure = msd;
|
||
msd->options->passwd_prompt_fn = 0;
|
||
msd->options->passwd_prompt_fn_arg = context;
|
||
|
||
msd->options->image_begin = mime_image_begin;
|
||
msd->options->image_end = mime_image_end;
|
||
msd->options->make_image_html = mime_image_make_image_html;
|
||
msd->options->image_write_buffer = mime_image_write_buffer;
|
||
|
||
#ifndef MOZILLA_30
|
||
msd->options->variable_width_plaintext_p = MIME_VariableWidthPlaintext;
|
||
#else /* MOZILLA_30 */
|
||
msd->options->variable_width_plaintext_p = MSG_VariableWidthPlaintext;
|
||
#endif /* MOZILLA_30 */
|
||
|
||
/* ### mwelch We want FO_EDT_SAVE_IMAGE to behave like *_SAVE_AS here
|
||
because we're spooling untranslated raw data. */
|
||
if (format_out == FO_SAVE_AS ||
|
||
format_out == FO_CACHE_AND_SAVE_AS ||
|
||
format_out == FO_MAIL_TO ||
|
||
format_out == FO_CACHE_AND_MAIL_TO ||
|
||
#ifdef FO_EDT_SAVE_IMAGE
|
||
format_out == FO_EDT_SAVE_IMAGE ||
|
||
#endif
|
||
msd->options->part_to_load)
|
||
msd->options->write_html_p = FALSE;
|
||
|
||
XP_ASSERT(!msd->stream);
|
||
|
||
obj = mime_new ((MimeObjectClass *)&mimeMessageClass,
|
||
(MimeHeaders *) NULL,
|
||
MESSAGE_RFC822);
|
||
if (!obj)
|
||
{
|
||
FREEIF(msd->options->part_to_load);
|
||
XP_FREE(msd->options);
|
||
XP_FREE(msd);
|
||
return 0;
|
||
}
|
||
obj->options = msd->options;
|
||
msd->obj = obj;
|
||
|
||
/* Both of these better not be true at the same time. */
|
||
XP_ASSERT(! (obj->options->decrypt_p && obj->options->write_html_p));
|
||
|
||
stream = XP_NEW (NET_StreamClass);
|
||
if (!stream)
|
||
{
|
||
FREEIF(msd->options->part_to_load);
|
||
XP_FREE(msd->options);
|
||
XP_FREE(msd);
|
||
XP_FREE(obj);
|
||
return 0;
|
||
}
|
||
XP_MEMSET (stream, 0, sizeof (*stream));
|
||
|
||
stream->name = "MIME Conversion Stream";
|
||
stream->complete = mime_display_stream_complete;
|
||
stream->abort = mime_display_stream_abort;
|
||
stream->put_block = mime_display_stream_write;
|
||
stream->is_write_ready = mime_display_stream_write_ready;
|
||
stream->data_object = msd;
|
||
stream->window_id = context;
|
||
|
||
status = obj->class->initialize(obj);
|
||
if (status >= 0)
|
||
status = obj->class->parse_begin(obj);
|
||
if (status < 0)
|
||
{
|
||
XP_FREE(stream);
|
||
FREEIF(msd->options->part_to_load);
|
||
XP_FREE(msd->options);
|
||
XP_FREE(msd);
|
||
XP_FREE(obj);
|
||
return 0;
|
||
}
|
||
|
||
TRACEMSG(("Returning stream from MIME_MessageConverter"));
|
||
|
||
return stream;
|
||
}
|
||
|
||
|
||
/* Interface between libmime and inline display of images: the abomination
|
||
that is known as "internal-external-reconnect".
|
||
*/
|
||
|
||
struct mime_image_stream_data {
|
||
struct mime_stream_data *msd;
|
||
URL_Struct *url_struct;
|
||
NET_StreamClass *istream;
|
||
};
|
||
|
||
|
||
static void *
|
||
mime_image_begin(const char *image_url, const char *content_type,
|
||
void *stream_closure)
|
||
{
|
||
struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
|
||
struct mime_image_stream_data *mid;
|
||
|
||
mid = XP_NEW(struct mime_image_stream_data);
|
||
if (!mid) return 0;
|
||
|
||
XP_MEMSET(mid, 0, sizeof(*mid));
|
||
mid->msd = msd;
|
||
|
||
/* Internal-external-reconnect only works when going to the screen.
|
||
In that case, return the mid, but leave it empty (returning 0
|
||
here is interpreted as out-of-memory.)
|
||
*/
|
||
if (msd->format_out != FO_PRESENT &&
|
||
msd->format_out != FO_CACHE_AND_PRESENT &&
|
||
msd->format_out != FO_PRINT &&
|
||
msd->format_out != FO_CACHE_AND_PRINT &&
|
||
msd->format_out != FO_SAVE_AS_POSTSCRIPT &&
|
||
msd->format_out != FO_CACHE_AND_SAVE_AS_POSTSCRIPT)
|
||
return mid;
|
||
#ifndef MOZILLA_30
|
||
if (!msd->context->img_cx)
|
||
/* If there is no image context, e.g. if this is a Text context or a
|
||
Mail context on the Mac, then we won't be loading images in the
|
||
image viewer. */
|
||
return mid;
|
||
#endif /* !MOZILLA_30 */
|
||
|
||
mid->url_struct = NET_CreateURLStruct (image_url, NET_DONT_RELOAD);
|
||
if (!mid->url_struct)
|
||
{
|
||
XP_FREE(mid);
|
||
return 0;
|
||
}
|
||
|
||
mid->url_struct->content_encoding = 0;
|
||
mid->url_struct->content_type = XP_STRDUP(content_type);
|
||
if (!mid->url_struct->content_type)
|
||
{
|
||
NET_FreeURLStruct (mid->url_struct);
|
||
XP_FREE(mid);
|
||
return 0;
|
||
}
|
||
|
||
mid->istream = NET_StreamBuilder (FO_MULTIPART_IMAGE, mid->url_struct,
|
||
msd->context);
|
||
if (!mid->istream)
|
||
{
|
||
NET_FreeURLStruct (mid->url_struct);
|
||
XP_FREE(mid);
|
||
return 0;
|
||
}
|
||
|
||
/* When uudecoding, we tend to come up with tiny chunks of data
|
||
at a time. Make a stream to put them back together, so that
|
||
we hand bigger pieces to the image library.
|
||
*/
|
||
{
|
||
NET_StreamClass *buffer =
|
||
msg_MakeRebufferingStream (mid->istream, mid->url_struct, msd->context);
|
||
if (buffer)
|
||
mid->istream = buffer;
|
||
}
|
||
XP_ASSERT(msd->istream == NULL);
|
||
msd->istream = mid->istream;
|
||
return mid;
|
||
}
|
||
|
||
|
||
static void
|
||
mime_image_end(void *image_closure, int status)
|
||
{
|
||
struct mime_image_stream_data *mid =
|
||
(struct mime_image_stream_data *) image_closure;
|
||
|
||
XP_ASSERT(mid);
|
||
if (!mid) return;
|
||
if (mid->istream)
|
||
{
|
||
if (status < 0)
|
||
mid->istream->abort(mid->istream, status);
|
||
else
|
||
mid->istream->complete(mid->istream);
|
||
XP_ASSERT(mid->msd->istream == mid->istream);
|
||
mid->msd->istream = NULL;
|
||
XP_FREE (mid->istream);
|
||
}
|
||
if (mid->url_struct)
|
||
NET_FreeURLStruct (mid->url_struct);
|
||
XP_FREE(mid);
|
||
}
|
||
|
||
|
||
static char *
|
||
mime_image_make_image_html(void *image_closure)
|
||
{
|
||
struct mime_image_stream_data *mid =
|
||
(struct mime_image_stream_data *) image_closure;
|
||
|
||
const char *prefix = "<P><CENTER><IMG SRC=\"";
|
||
const char *suffix = "\"></CENTER><P>";
|
||
const char *url;
|
||
char *buf;
|
||
XP_ASSERT(mid);
|
||
if (!mid) return 0;
|
||
|
||
/* Internal-external-reconnect only works when going to the screen. */
|
||
if (!mid->istream)
|
||
return XP_STRDUP("<IMG SRC=\"internal-gopher-image\" ALT=\"[Image]\">");
|
||
|
||
url = ((mid->url_struct && mid->url_struct->address)
|
||
? mid->url_struct->address
|
||
: "");
|
||
buf = (char *) XP_ALLOC (XP_STRLEN (prefix) + XP_STRLEN (suffix) +
|
||
XP_STRLEN (url) + 20);
|
||
if (!buf) return 0;
|
||
*buf = 0;
|
||
|
||
XP_STRCAT (buf, prefix);
|
||
XP_STRCAT (buf, url);
|
||
XP_STRCAT (buf, suffix);
|
||
return buf;
|
||
}
|
||
|
||
|
||
static int
|
||
mime_image_write_buffer(char *buf, int32 size, void *image_closure)
|
||
{
|
||
struct mime_image_stream_data *mid =
|
||
(struct mime_image_stream_data *) image_closure;
|
||
if (!mid) return -1;
|
||
if (!mid->istream) return 0;
|
||
return mid->istream->put_block(mid->istream, buf, size);
|
||
}
|
||
|
||
|
||
/* Guessing the filename to use in "Save As", given a URL which may point
|
||
to a MIME part that we've recently displayed. (Kloooooge!)
|
||
*/
|
||
|
||
|
||
#ifdef HAVE_MIME_DATA_SLOT
|
||
|
||
/* If the given URL points to the MimeObject currently displayed on MWContext,
|
||
or to a sub-part of it, then a freshly-allocated part-address-string will
|
||
be returned. (This string will be relative to the URL in the window.)
|
||
Else, returns NULL.
|
||
*/
|
||
static char *
|
||
mime_extract_relative_part_address(MWContext *context, const char *url)
|
||
{
|
||
char *url1 = 0, *url2 = 0, *part = 0, *result = 0; /* free these */
|
||
char *base_part = 0, *sub_part = 0, *s = 0; /* don't free these */
|
||
|
||
XP_ASSERT(context && url);
|
||
|
||
if (!context ||
|
||
!context->mime_data ||
|
||
!context->mime_data->last_parsed_object ||
|
||
!context->mime_data->last_parsed_url)
|
||
goto FAIL;
|
||
|
||
url1 = XP_STRDUP(url);
|
||
if (!url1) goto FAIL; /* MK_OUT_OF_MEMORY */
|
||
url2 = XP_STRDUP(context->mime_data->last_parsed_url);
|
||
if (!url2) goto FAIL; /* MK_OUT_OF_MEMORY */
|
||
|
||
s = XP_STRCHR(url1, '?');
|
||
if (s) *s = 0;
|
||
s = XP_STRCHR(url2, '?');
|
||
if (s) *s = 0;
|
||
|
||
if (s) base_part = s+1;
|
||
|
||
/* If the two URLs, minus their '?' parts, don't match, give up.
|
||
*/
|
||
if (!!XP_STRCMP(url1, url2))
|
||
goto FAIL;
|
||
|
||
|
||
/* Otherwise, the URLs match, so now we can go search for the part.
|
||
*/
|
||
|
||
/* First we need to extract `base_part' from url2 -- this is the common
|
||
prefix between the two URLs. */
|
||
if (base_part)
|
||
{
|
||
s = XP_STRSTR(base_part, "?part=");
|
||
if (!s)
|
||
s = XP_STRSTR(base_part, "&part=");
|
||
base_part = s;
|
||
if (base_part) /* found it */
|
||
{
|
||
base_part += 6;
|
||
/* truncate `base_part' after part spec. */
|
||
for (s = base_part; *s != 0 && *s != '?' && *s != '&'; s++)
|
||
;
|
||
*s = 0;
|
||
}
|
||
}
|
||
|
||
/* Find the part we're looking for.
|
||
*/
|
||
s = XP_STRSTR(url, "?part=");
|
||
if (!s)
|
||
s = XP_STRSTR(url, "&part=");
|
||
if (!s)
|
||
goto FAIL;
|
||
|
||
s += 6;
|
||
part = XP_STRDUP(s);
|
||
if (!part) goto FAIL; /* MK_OUT_OF_MEMORY */
|
||
|
||
/* truncate `part' after part spec. */
|
||
for (s = part; *s != 0 && *s != '?' && *s != '&'; s++)
|
||
;
|
||
*s = 0;
|
||
|
||
/* Now remove the common prefix, if any. */
|
||
sub_part = part;
|
||
if (base_part)
|
||
{
|
||
int L = XP_STRLEN(base_part);
|
||
if (!strncasecomp(sub_part, base_part, L) &&
|
||
sub_part[L] == '.')
|
||
sub_part += L + 1;
|
||
}
|
||
|
||
result = XP_STRDUP(sub_part);
|
||
|
||
FAIL:
|
||
|
||
FREEIF(part);
|
||
FREEIF(url1);
|
||
FREEIF(url2);
|
||
return result;
|
||
}
|
||
|
||
static char *
|
||
mime_guess_url_content_name_1 (MWContext *context, const char *url)
|
||
{
|
||
char *name = 0;
|
||
char *addr = mime_extract_relative_part_address(context, url);
|
||
if (!addr) return 0;
|
||
name = mime_find_suggested_name_of_part(addr,
|
||
context->mime_data->last_parsed_object);
|
||
XP_FREE(addr);
|
||
return name;
|
||
}
|
||
|
||
|
||
char *
|
||
MimeGuessURLContentName(MWContext *context, const char *url)
|
||
{
|
||
char *result = mime_guess_url_content_name_1 (context, url);
|
||
if (result)
|
||
return result;
|
||
else
|
||
{
|
||
/* Ok, that didn't work, let's go look in all the other contexts! */
|
||
XP_List *list = XP_GetGlobalContextList();
|
||
int32 i, j = XP_ListCount(list);
|
||
for (i = 1; i <= j; i++)
|
||
{
|
||
MWContext *cx2 = (MWContext *) XP_ListGetObjectNum(list, i);
|
||
if (cx2 != context)
|
||
{
|
||
result = mime_guess_url_content_name_1 (cx2, url);
|
||
if (result) return result;
|
||
}
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static char *
|
||
mime_get_url_content_type_1 (MWContext *context, const char *url)
|
||
{
|
||
char *name = 0;
|
||
char *addr = mime_extract_relative_part_address(context, url);
|
||
if (!addr) return 0;
|
||
name = mime_find_content_type_of_part(addr,
|
||
context->mime_data->last_parsed_object);
|
||
XP_FREE(addr);
|
||
return name;
|
||
}
|
||
|
||
/* This routine is currently used to get the content type for a mime part url
|
||
so the fe's can decide to open a new browser window or just open the save as dialog.
|
||
It only works on a context that is displaying the message containing the part.
|
||
*/
|
||
char *
|
||
MimeGetURLContentType(MWContext *context, const char *url)
|
||
{
|
||
char *result = mime_get_url_content_type_1 (context, url);
|
||
/* I don't think we need to look at any other contexts for our purposes,
|
||
* but I bow to Jamie's suscpicion that there's a good reason to.
|
||
*/
|
||
if (result)
|
||
return result;
|
||
else
|
||
{
|
||
/* Ok, that didn't work, let's go look in all the other contexts! */
|
||
XP_List *list = XP_GetGlobalContextList();
|
||
int32 i, j = XP_ListCount(list);
|
||
for (i = 1; i <= j; i++)
|
||
{
|
||
MWContext *cx2 = (MWContext *) XP_ListGetObjectNum(list, i);
|
||
if (cx2 != context)
|
||
{
|
||
result = mime_get_url_content_type_1 (cx2, url);
|
||
if (result) return result;
|
||
}
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
static MimeObject*
|
||
mime_find_text_html_part_1(MimeObject* obj)
|
||
{
|
||
if (mime_subclass_p(obj->class,
|
||
(MimeObjectClass*) &mimeInlineTextHTMLClass)) {
|
||
return obj;
|
||
}
|
||
if (mime_subclass_p(obj->class, (MimeObjectClass*) &mimeContainerClass)) {
|
||
MimeContainer* cobj = (MimeContainer*) obj;
|
||
int32 i;
|
||
for (i=0 ; i<cobj->nchildren ; i++) {
|
||
MimeObject* result = mime_find_text_html_part_1(cobj->children[i]);
|
||
if (result) return result;
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
|
||
static MimeObject*
|
||
mime_find_text_html_part(MWContext* context)
|
||
{
|
||
XP_ASSERT(context);
|
||
if (!context ||
|
||
!context->mime_data ||
|
||
!context->mime_data->last_parsed_object)
|
||
return NULL;
|
||
|
||
return mime_find_text_html_part_1(context->mime_data->last_parsed_object);
|
||
}
|
||
|
||
|
||
XP_Bool
|
||
MimeShowingTextHtml(MWContext* context)
|
||
{
|
||
return mime_find_text_html_part(context) != NULL;
|
||
}
|
||
|
||
|
||
extern char*
|
||
MimeGetHtmlPartURL(MWContext* context)
|
||
{
|
||
MimeObject* obj = mime_find_text_html_part(context);
|
||
if (obj == NULL) return NULL;
|
||
return mime_part_address(obj);
|
||
}
|
||
|
||
|
||
/* Finds the main object of the message -- generally a multipart/mixed,
|
||
text/plain, or text/html. */
|
||
static MimeObject*
|
||
mime_get_main_object(MimeObject* obj)
|
||
{
|
||
MimeContainer* cobj;
|
||
if (!(mime_subclass_p(obj->class, (MimeObjectClass*) &mimeMessageClass))) {
|
||
return obj;
|
||
}
|
||
cobj = (MimeContainer*) obj;
|
||
if (cobj->nchildren != 1) return obj;
|
||
obj = cobj->children[0];
|
||
for (;;) {
|
||
if (!mime_subclass_p(obj->class,
|
||
(MimeObjectClass*) &mimeMultipartSignedClass) &&
|
||
!mime_subclass_p(obj->class,
|
||
(MimeObjectClass*) &mimeFilterClass)) {
|
||
return obj;
|
||
}
|
||
/* Our main thing is a signed or encrypted object.
|
||
We don't care about that; go on inside to the thing that we signed or
|
||
encrypted. */
|
||
cobj = (MimeContainer*) obj;
|
||
if (cobj->nchildren != 1) return obj;
|
||
obj = cobj->children[0];
|
||
}
|
||
}
|
||
|
||
|
||
int
|
||
MimeGetAttachmentCount(MWContext* context)
|
||
{
|
||
MimeObject* obj;
|
||
MimeContainer* cobj;
|
||
XP_ASSERT(context);
|
||
if (!context ||
|
||
!context->mime_data ||
|
||
!context->mime_data->last_parsed_object) {
|
||
return 0;
|
||
}
|
||
obj = mime_get_main_object(context->mime_data->last_parsed_object);
|
||
if (!mime_subclass_p(obj->class, (MimeObjectClass*) &mimeContainerClass)) {
|
||
|
||
return 0;
|
||
}
|
||
cobj = (MimeContainer*) obj;
|
||
return cobj->nchildren - 1; /* ### Hardcoding subtraction of one isn't
|
||
quite right; we have to look at the first
|
||
object and decide whether it is the body or
|
||
the first attachment on a NULL body. */
|
||
}
|
||
|
||
|
||
int
|
||
MimeGetAttachmentList(MWContext* context, MSG_AttachmentData** data)
|
||
{
|
||
MimeObject* obj;
|
||
MimeContainer* cobj;
|
||
MSG_AttachmentData* tmp;
|
||
int32 n;
|
||
int32 i;
|
||
char* disp;
|
||
char c;
|
||
if (!data) return 0;
|
||
*data = NULL;
|
||
XP_ASSERT(context);
|
||
if (!context ||
|
||
!context->mime_data ||
|
||
!context->mime_data->last_parsed_object) {
|
||
return 0;
|
||
}
|
||
obj = mime_get_main_object(context->mime_data->last_parsed_object);
|
||
if (!mime_subclass_p(obj->class, (MimeObjectClass*) &mimeContainerClass)) {
|
||
return 0;
|
||
}
|
||
cobj = (MimeContainer*) obj;
|
||
n = cobj->nchildren; /* This is often too big, but that's OK. */
|
||
if (n <= 0) return n;
|
||
*data = XP_CALLOC(n + 1, sizeof(MSG_AttachmentData));
|
||
if (!*data) return MK_OUT_OF_MEMORY;
|
||
tmp = *data;
|
||
c = '?';
|
||
if (XP_STRCHR(context->mime_data->last_parsed_url, '?')) {
|
||
c = '&';
|
||
}
|
||
for (i=1 /*###ick, see above*/ ; i<cobj->nchildren ; i++, tmp++) {
|
||
MimeObject* child = cobj->children[i];
|
||
char* part = mime_part_address(child);
|
||
if (!part) return MK_OUT_OF_MEMORY;
|
||
tmp->url = PR_smprintf("%s%cpart=%s", context->mime_data->last_parsed_url,
|
||
c, part);
|
||
if (!tmp->url) return MK_OUT_OF_MEMORY;
|
||
tmp->real_type = child->content_type ?
|
||
XP_STRDUP(child->content_type) : NULL;
|
||
tmp->real_encoding = child->encoding ? XP_STRDUP(child->encoding) : NULL;
|
||
disp = MimeHeaders_get(child->headers, HEADER_CONTENT_DISPOSITION,
|
||
FALSE, FALSE);
|
||
if (disp) {
|
||
tmp->real_name = MimeHeaders_get_parameter(disp, "filename");
|
||
if (tmp->real_name)
|
||
{
|
||
char *fname = NULL;
|
||
fname = mime_decode_filename(tmp->real_name);
|
||
if (fname && fname != tmp->real_name)
|
||
{
|
||
XP_FREE(tmp->real_name);
|
||
tmp->real_name = fname;
|
||
}
|
||
}
|
||
XP_FREE(disp);
|
||
}
|
||
disp = MimeHeaders_get(child->headers, HEADER_CONTENT_TYPE,
|
||
FALSE, FALSE);
|
||
if (disp)
|
||
{
|
||
tmp->x_mac_type = MimeHeaders_get_parameter(disp, PARAM_X_MAC_TYPE);
|
||
tmp->x_mac_creator= MimeHeaders_get_parameter(disp, PARAM_X_MAC_CREATOR);
|
||
XP_FREE(disp);
|
||
}
|
||
tmp->description = MimeHeaders_get(child->headers,
|
||
HEADER_CONTENT_DESCRIPTION,
|
||
FALSE, FALSE);
|
||
#ifndef MOZILLA_30
|
||
if (tmp->real_type && !strcasecomp(tmp->real_type, MESSAGE_RFC822) &&
|
||
(!tmp->real_name || *tmp->real_name))
|
||
{
|
||
StrAllocCopy(tmp->real_name, XP_GetString(XP_FORWARDED_MESSAGE_ATTACHMENT));
|
||
}
|
||
#endif /* !MOZILLA_30 */
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
void
|
||
MimeFreeAttachmentList(MSG_AttachmentData* data)
|
||
{
|
||
if (data) {
|
||
MSG_AttachmentData* tmp;
|
||
for (tmp = data ; tmp->url ; tmp++) {
|
||
/* Can't do FREEIF on `const' values... */
|
||
if (tmp->url) XP_FREE((char *) tmp->url);
|
||
if (tmp->real_type) XP_FREE((char *) tmp->real_type);
|
||
if (tmp->real_encoding) XP_FREE((char *) tmp->real_encoding);
|
||
if (tmp->real_name) XP_FREE((char *) tmp->real_name);
|
||
if (tmp->x_mac_type) XP_FREE((char *) tmp->x_mac_type);
|
||
if (tmp->x_mac_creator) XP_FREE((char *) tmp->x_mac_creator);
|
||
if (tmp->description) XP_FREE((char *) tmp->description);
|
||
tmp->url = 0;
|
||
tmp->real_type = 0;
|
||
tmp->real_name = 0;
|
||
tmp->description = 0;
|
||
}
|
||
XP_FREE(data);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
void
|
||
MimeDestroyContextData(MWContext *context)
|
||
{
|
||
INTL_CharSetInfo csi = NULL;
|
||
|
||
XP_ASSERT(context);
|
||
if (!context) return;
|
||
|
||
csi = LO_GetDocumentCharacterSetInfo(context);
|
||
if (csi)
|
||
INTL_SetCSIMimeCharset(csi, NULL);
|
||
|
||
if (!context->mime_data) return;
|
||
|
||
if (context->mime_data->last_parsed_object)
|
||
{
|
||
MimeDisplayOptions *options =
|
||
context->mime_data->last_parsed_object->options;
|
||
|
||
mime_free(context->mime_data->last_parsed_object);
|
||
if (options)
|
||
{
|
||
FREEIF(options->part_to_load);
|
||
XP_FREE(options);
|
||
}
|
||
|
||
context->mime_data->last_parsed_object = 0;
|
||
FREEIF(context->mime_data->last_parsed_url);
|
||
}
|
||
|
||
#ifdef LOCK_LAST_CACHED_MESSAGE
|
||
if (context->mime_data->previous_locked_url)
|
||
{
|
||
/* duplicated from mime_make_output_stream()... */
|
||
URL_Struct *url_s =
|
||
NET_CreateURLStruct(context->mime_data->previous_locked_url,
|
||
NET_NORMAL_RELOAD);
|
||
if (url_s)
|
||
{
|
||
/* Note: if post data was involved here, we'd lose. We're assuming
|
||
that all we need to save is the url->address. */
|
||
NET_ChangeCacheFileLock(url_s, FALSE);
|
||
NET_FreeURLStruct(url_s);
|
||
}
|
||
XP_FREE(context->mime_data->previous_locked_url);
|
||
context->mime_data->previous_locked_url = 0;
|
||
}
|
||
#endif /* LOCK_LAST_CACHED_MESSAGE */
|
||
|
||
XP_FREE(context->mime_data);
|
||
context->mime_data = 0;
|
||
}
|
||
|
||
#else /* !HAVE_MIME_DATA_SLOT */
|
||
|
||
char *
|
||
MimeGuessURLContentName(MWContext *context, const char *url)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
void
|
||
MimeDestroyContextData(MWContext *context)
|
||
{
|
||
if (!context) return;
|
||
|
||
INTL_SetCSIMimeCharset(LO_GetDocumentCharacterSetInfo(context), NULL);
|
||
|
||
}
|
||
#endif /* !HAVE_MIME_DATA_SLOT */
|
||
|
||
|
||
|
||
|
||
|
||
/* Interface between netlib and the converters from text/richtext
|
||
and text/enriched to text/html: MIME_RichtextConverter() and
|
||
MIME_EnrichedTextConverter().
|
||
*/
|
||
|
||
struct mime_richtext_data {
|
||
URL_Struct *url; /* The URL this is all coming from. */
|
||
MWContext *context;
|
||
int format_out;
|
||
NET_StreamClass *stream; /* The stream to which we write HTML. */
|
||
char *ibuffer, *obuffer;
|
||
int32 ibuffer_size, obuffer_size;
|
||
int32 ibuffer_fp;
|
||
XP_Bool enriched_p;
|
||
};
|
||
|
||
static int
|
||
mime_richtext_stream_fn (char *buf, int32 size, void *closure)
|
||
{
|
||
struct mime_richtext_data *mrd = (struct mime_richtext_data *) closure;
|
||
XP_ASSERT(mrd->stream);
|
||
if (!mrd->stream) return -1;
|
||
return mrd->stream->put_block (mrd->stream, buf, size);
|
||
}
|
||
|
||
static int
|
||
mime_richtext_write_line (char* line, int32 size, void *closure)
|
||
{
|
||
struct mime_richtext_data *mrd = (struct mime_richtext_data *) closure;
|
||
return MimeRichtextConvert (line, size, mime_richtext_stream_fn,
|
||
mrd, &mrd->obuffer, &mrd->obuffer_size,
|
||
mrd->enriched_p);
|
||
}
|
||
|
||
static int
|
||
mime_richtext_write (NET_StreamClass *stream, const char* buf, int32 size)
|
||
{
|
||
struct mime_richtext_data *data = (struct mime_richtext_data *) stream->data_object;
|
||
return msg_LineBuffer (buf, size, &data->ibuffer, &data->ibuffer_size,
|
||
&data->ibuffer_fp, FALSE, mime_richtext_write_line,
|
||
data);
|
||
}
|
||
|
||
static unsigned int
|
||
mime_richtext_write_ready (NET_StreamClass *stream)
|
||
{
|
||
struct mime_richtext_data *data = (struct mime_richtext_data *) stream->data_object;
|
||
if (data->stream)
|
||
return ((*data->stream->is_write_ready)
|
||
(data->stream));
|
||
else
|
||
return (MAX_WRITE_READY);
|
||
}
|
||
|
||
static void
|
||
mime_richtext_complete (NET_StreamClass *stream)
|
||
{
|
||
struct mime_richtext_data *mrd = (struct mime_richtext_data *) stream->data_object;
|
||
if (!mrd) return;
|
||
FREEIF(mrd->obuffer);
|
||
if (mrd->stream)
|
||
{
|
||
mrd->stream->complete (mrd->stream);
|
||
XP_FREE (mrd->stream);
|
||
}
|
||
XP_FREE(mrd);
|
||
}
|
||
|
||
static void
|
||
mime_richtext_abort (NET_StreamClass *stream, int status)
|
||
{
|
||
struct mime_richtext_data *mrd = (struct mime_richtext_data *) stream->data_object;
|
||
if (!mrd) return;
|
||
FREEIF(mrd->obuffer);
|
||
if (mrd->stream)
|
||
{
|
||
mrd->stream->abort (mrd->stream, status);
|
||
XP_FREE (mrd->stream);
|
||
}
|
||
XP_FREE(mrd);
|
||
}
|
||
|
||
|
||
static NET_StreamClass *
|
||
MIME_RichtextConverter_1 (int format_out, void *closure,
|
||
URL_Struct *url, MWContext *context,
|
||
XP_Bool enriched_p)
|
||
{
|
||
struct mime_richtext_data *data;
|
||
NET_StreamClass *stream, *next_stream;
|
||
|
||
next_stream = mime_make_output_stream(TEXT_HTML, 0, 0, 0, 0,
|
||
format_out, url, context, NULL);
|
||
if (!next_stream) return 0;
|
||
|
||
data = XP_NEW(struct mime_richtext_data);
|
||
if (!data)
|
||
{
|
||
XP_FREE(next_stream);
|
||
return 0;
|
||
}
|
||
XP_MEMSET(data, 0, sizeof(*data));
|
||
data->url = url;
|
||
data->context = context;
|
||
data->format_out = format_out;
|
||
data->stream = next_stream;
|
||
data->enriched_p = enriched_p;
|
||
|
||
stream = XP_NEW (NET_StreamClass);
|
||
if (!stream)
|
||
{
|
||
XP_FREE(next_stream);
|
||
XP_FREE(data);
|
||
return 0;
|
||
}
|
||
XP_MEMSET (stream, 0, sizeof (*stream));
|
||
|
||
stream->name = "Richtext Conversion Stream";
|
||
stream->complete = mime_richtext_complete;
|
||
stream->abort = mime_richtext_abort;
|
||
stream->put_block = mime_richtext_write;
|
||
stream->is_write_ready = mime_richtext_write_ready;
|
||
stream->data_object = data;
|
||
stream->window_id = context;
|
||
|
||
TRACEMSG(("Returning stream from MIME_RichtextConverter"));
|
||
|
||
return stream;
|
||
}
|
||
|
||
NET_StreamClass *
|
||
MIME_RichtextConverter (int format_out, void *closure,
|
||
URL_Struct *url, MWContext *context)
|
||
{
|
||
return MIME_RichtextConverter_1 (format_out, closure, url, context, FALSE);
|
||
}
|
||
|
||
NET_StreamClass *
|
||
MIME_EnrichedTextConverter (int format_out, void *closure,
|
||
URL_Struct *url, MWContext *context)
|
||
{
|
||
return MIME_RichtextConverter_1 (format_out, closure, url, context, TRUE);
|
||
}
|
||
|
||
|
||
|
||
#ifndef MOZILLA_30
|
||
|
||
int
|
||
MIME_DisplayAttachmentPane(MWContext* context)
|
||
{
|
||
if (context && context->mime_data) {
|
||
MSG_Pane* pane = context->mime_data->last_pane;
|
||
if (!pane)
|
||
pane = MSG_FindPane(context, MSG_MESSAGEPANE);
|
||
if (pane) {
|
||
MSG_MessagePaneCallbacks* callbacks;
|
||
void* closure;
|
||
callbacks = MSG_GetMessagePaneCallbacks(pane, &closure);
|
||
if (callbacks && callbacks->UserWantsToSeeAttachments) {
|
||
(*callbacks->UserWantsToSeeAttachments)(pane, closure);
|
||
}
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* This struct is the state we used in MIME_VCardConverter() */
|
||
struct mime_vcard_data {
|
||
URL_Struct *url; /* original url */
|
||
int format_out; /* intended output format;
|
||
should be TEXT-VCARD */
|
||
MWContext *context;
|
||
NET_StreamClass *stream; /* not used for now */
|
||
MimeDisplayOptions *options; /* data for communicating with libmime.a */
|
||
MimeObject *obj; /* The root */
|
||
};
|
||
|
||
static int
|
||
mime_vcard_write (NET_StreamClass *stream,
|
||
const char *buf,
|
||
int32 size )
|
||
{
|
||
struct mime_vcard_data *vcd = (struct mime_vcard_data *) stream->data_object;
|
||
XP_ASSERT ( vcd );
|
||
|
||
if ( !vcd || !vcd->obj ) return -1;
|
||
|
||
return vcd->obj->class->parse_line ((char *) buf, size, vcd->obj);
|
||
}
|
||
|
||
static unsigned int
|
||
mime_vcard_write_ready (NET_StreamClass *stream)
|
||
{
|
||
struct mime_vcard_data *vcd = (struct mime_vcard_data *) stream->data_object;
|
||
XP_ASSERT (vcd);
|
||
|
||
if (!vcd) return MAX_WRITE_READY;
|
||
if (vcd->stream)
|
||
return vcd->stream->is_write_ready ( vcd->stream );
|
||
else
|
||
return MAX_WRITE_READY;
|
||
}
|
||
|
||
static void
|
||
mime_vcard_complete (NET_StreamClass *stream)
|
||
{
|
||
struct mime_vcard_data *vcd = (struct mime_vcard_data *) stream->data_object;
|
||
|
||
XP_ASSERT (vcd);
|
||
|
||
if (!vcd) return;
|
||
|
||
if (vcd->obj) {
|
||
int status;
|
||
|
||
status = vcd->obj->class->parse_eof ( vcd->obj, FALSE );
|
||
vcd->obj->class->parse_end( vcd->obj, status < 0 ? TRUE : FALSE );
|
||
|
||
mime_free (vcd->obj);
|
||
vcd->obj = 0;
|
||
|
||
if (vcd->stream) {
|
||
vcd->stream->complete (vcd->stream);
|
||
XP_FREE( vcd->stream );
|
||
vcd->stream = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
mime_vcard_abort (NET_StreamClass *stream, int status )
|
||
{
|
||
struct mime_vcard_data *vcd = (struct mime_vcard_data *) stream->data_object;
|
||
|
||
XP_ASSERT (vcd);
|
||
if (!vcd) return;
|
||
|
||
if (vcd->obj) {
|
||
int status;
|
||
|
||
if ( !vcd->obj->closed_p )
|
||
status = vcd->obj->class->parse_eof ( vcd->obj, TRUE );
|
||
if ( !vcd->obj->parsed_p )
|
||
vcd->obj->class->parse_end( vcd->obj, TRUE );
|
||
|
||
mime_free (vcd->obj);
|
||
vcd->obj = 0;
|
||
|
||
if (vcd->stream) {
|
||
vcd->stream->abort (vcd->stream, status);
|
||
XP_FREE( vcd->stream );
|
||
vcd->stream = 0;
|
||
}
|
||
}
|
||
XP_FREE (vcd);
|
||
}
|
||
|
||
extern int MIME_HasAttachments(MWContext *context)
|
||
{
|
||
return (context->mime_data && context->mime_data->last_parsed_object->class->showAttachmentIcon);
|
||
}
|
||
|
||
|
||
|
||
extern NET_StreamClass *
|
||
MIME_VCardConverter ( int format_out,
|
||
void *closure,
|
||
URL_Struct *url,
|
||
MWContext *context )
|
||
{
|
||
int status = 0;
|
||
NET_StreamClass * stream = NULL;
|
||
NET_StreamClass * next_stream = NULL;
|
||
struct mime_vcard_data *vcd = NULL;
|
||
MimeObject *obj;
|
||
|
||
XP_ASSERT (url && context);
|
||
if ( !url || !context ) return NULL;
|
||
|
||
next_stream = mime_make_output_stream (TEXT_HTML, 0, 0, 0, 0,
|
||
format_out, url, context, NULL);
|
||
|
||
if (!next_stream) return 0;
|
||
|
||
|
||
vcd = XP_NEW_ZAP (struct mime_vcard_data);
|
||
if (!vcd) {
|
||
XP_FREE (next_stream);
|
||
return 0;
|
||
}
|
||
|
||
vcd->url = url;
|
||
vcd->context = context;
|
||
vcd->format_out = format_out;
|
||
vcd->stream = next_stream;
|
||
|
||
vcd->options = XP_NEW_ZAP ( MimeDisplayOptions );
|
||
|
||
if ( !vcd->options ) {
|
||
XP_FREE (next_stream);
|
||
XP_FREE ( vcd );
|
||
return 0;
|
||
}
|
||
|
||
vcd->options->write_html_p = TRUE;
|
||
vcd->options->output_fn = mime_output_fn;
|
||
if (format_out == FO_PRESENT ||
|
||
format_out == FO_CACHE_AND_PRESENT)
|
||
vcd->options->output_vcard_buttons_p = TRUE;
|
||
|
||
#ifdef MIME_DRAFTS
|
||
vcd->options->decompose_file_p = FALSE; /* new field in MimeDisplayOptions */
|
||
#endif /* MIME_DRAFTS */
|
||
|
||
vcd->options->url = url->address;
|
||
vcd->options->stream_closure = vcd;
|
||
vcd->options->html_closure = vcd;
|
||
|
||
obj = mime_new ( (MimeObjectClass *) &mimeInlineTextVCardClass,
|
||
(MimeHeaders *) NULL,
|
||
TEXT_VCARD );
|
||
|
||
if ( !obj ) {
|
||
FREEIF( vcd->options->part_to_load );
|
||
XP_FREE ( next_stream );
|
||
XP_FREE ( vcd->options );
|
||
XP_FREE ( vcd );
|
||
return 0;
|
||
}
|
||
|
||
obj->options = vcd->options;
|
||
vcd->obj = obj;
|
||
|
||
stream = XP_NEW_ZAP ( NET_StreamClass );
|
||
if ( !stream ) {
|
||
FREEIF ( vcd->options->part_to_load );
|
||
XP_FREE ( next_stream );
|
||
XP_FREE ( vcd->options );
|
||
XP_FREE ( vcd );
|
||
XP_FREE ( obj );
|
||
return 0;
|
||
}
|
||
|
||
stream->name = "MIME To VCard Converter Stream";
|
||
stream->complete = mime_vcard_complete;
|
||
stream->abort = mime_vcard_abort;
|
||
stream->put_block = mime_vcard_write;
|
||
stream->is_write_ready = mime_vcard_write_ready;
|
||
stream->data_object = vcd;
|
||
stream->window_id = context;
|
||
|
||
status = obj->class->initialize ( obj );
|
||
if ( status >= 0 )
|
||
status = obj->class->parse_begin ( obj );
|
||
if ( status < 0 ) {
|
||
XP_FREE ( stream );
|
||
FREEIF( vcd->options->part_to_load );
|
||
XP_FREE ( next_stream );
|
||
XP_FREE ( vcd->options );
|
||
XP_FREE ( vcd );
|
||
XP_FREE ( obj );
|
||
return 0;
|
||
}
|
||
|
||
return stream;
|
||
}
|
||
|
||
#endif /* !MOZILLA_30 */
|