1998-03-28 05:44:41 +03:00
|
|
|
/* -*- 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* mimemalt.c --- definition of the MimeMultipartAlternative class (see mimei.h)
|
|
|
|
Created: Jamie Zawinski <jwz@netscape.com>, 15-May-96.
|
|
|
|
*/
|
|
|
|
|
1998-07-25 01:05:24 +04:00
|
|
|
#include "rosetta.h"
|
1998-03-28 05:44:41 +03:00
|
|
|
#include "mimemalt.h"
|
|
|
|
|
|
|
|
#define MIME_SUPERCLASS mimeMultipartClass
|
|
|
|
MimeDefClass(MimeMultipartAlternative, MimeMultipartAlternativeClass,
|
|
|
|
mimeMultipartAlternativeClass, &MIME_SUPERCLASS);
|
|
|
|
|
|
|
|
static int MimeMultipartAlternative_initialize (MimeObject *);
|
|
|
|
static void MimeMultipartAlternative_finalize (MimeObject *);
|
|
|
|
static int MimeMultipartAlternative_parse_eof (MimeObject *, XP_Bool);
|
|
|
|
static int MimeMultipartAlternative_create_child(MimeObject *);
|
|
|
|
static int MimeMultipartAlternative_parse_child_line (MimeObject *, char *,
|
|
|
|
int32, XP_Bool);
|
|
|
|
static int MimeMultipartAlternative_close_child(MimeObject *);
|
|
|
|
|
|
|
|
static XP_Bool MimeMultipartAlternative_display_part_p(MimeObject *self,
|
|
|
|
MimeHeaders *sub_hdrs);
|
|
|
|
static int MimeMultipartAlternative_discard_cached_part(MimeObject *);
|
|
|
|
static int MimeMultipartAlternative_display_cached_part(MimeObject *);
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartAlternativeClassInitialize(MimeMultipartAlternativeClass *class)
|
|
|
|
{
|
|
|
|
MimeObjectClass *oclass = (MimeObjectClass *) class;
|
|
|
|
MimeMultipartClass *mclass = (MimeMultipartClass *) class;
|
|
|
|
XP_ASSERT(!oclass->class_initialized);
|
|
|
|
oclass->initialize = MimeMultipartAlternative_initialize;
|
|
|
|
oclass->finalize = MimeMultipartAlternative_finalize;
|
|
|
|
oclass->parse_eof = MimeMultipartAlternative_parse_eof;
|
|
|
|
mclass->create_child = MimeMultipartAlternative_create_child;
|
|
|
|
mclass->parse_child_line = MimeMultipartAlternative_parse_child_line;
|
|
|
|
mclass->close_child = MimeMultipartAlternative_close_child;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartAlternative_initialize (MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
|
|
|
|
|
|
|
|
XP_ASSERT(!malt->part_buffer);
|
|
|
|
malt->part_buffer = MimePartBufferCreate();
|
|
|
|
if (!malt->part_buffer)
|
|
|
|
return MK_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
MimeMultipartAlternative_cleanup(MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
|
|
|
|
if (malt->buffered_hdrs)
|
|
|
|
{
|
|
|
|
MimeHeaders_free(malt->buffered_hdrs);
|
|
|
|
malt->buffered_hdrs = 0;
|
|
|
|
}
|
|
|
|
if (malt->part_buffer)
|
|
|
|
{
|
|
|
|
MimePartBufferDestroy(malt->part_buffer);
|
|
|
|
malt->part_buffer = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
MimeMultipartAlternative_finalize (MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeMultipartAlternative_cleanup(obj);
|
|
|
|
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartAlternative_parse_eof (MimeObject *obj, XP_Bool abort_p)
|
|
|
|
{
|
|
|
|
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
if (obj->closed_p) return 0;
|
|
|
|
|
|
|
|
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
/* If there's a cached part we haven't written out yet, do it now.
|
|
|
|
*/
|
|
|
|
if (malt->buffered_hdrs && !abort_p)
|
|
|
|
{
|
|
|
|
status = MimeMultipartAlternative_display_cached_part(obj);
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
MimeMultipartAlternative_cleanup(obj);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartAlternative_create_child(MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeMultipart *mult = (MimeMultipart *) obj;
|
|
|
|
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
|
|
|
|
|
|
|
|
if (MimeMultipartAlternative_display_part_p (obj, mult->hdrs))
|
|
|
|
{
|
|
|
|
/* If this part is potentially displayable, begin populating the cache
|
|
|
|
with it. If there's something in the cache already, discard it
|
|
|
|
first. (Just because this part is displayable doesn't mean we will
|
|
|
|
display it -- of two consecutive displayable parts, it is the second
|
|
|
|
one that gets displayed.)
|
|
|
|
*/
|
|
|
|
int status;
|
|
|
|
mult->state = MimeMultipartPartFirstLine;
|
|
|
|
|
|
|
|
status = MimeMultipartAlternative_discard_cached_part(obj);
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
XP_ASSERT(!malt->buffered_hdrs);
|
|
|
|
malt->buffered_hdrs = MimeHeaders_copy(mult->hdrs);
|
|
|
|
if (!malt->buffered_hdrs) return MK_OUT_OF_MEMORY;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* If this part is not displayable, then we're done -- all that is left
|
|
|
|
to do is to flush out the part that is currently in the cache.
|
|
|
|
*/
|
|
|
|
mult->state = MimeMultipartEpilogue;
|
|
|
|
return MimeMultipartAlternative_display_cached_part(obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartAlternative_parse_child_line (MimeObject *obj,
|
|
|
|
char *line, int32 length,
|
|
|
|
XP_Bool first_line_p)
|
|
|
|
{
|
|
|
|
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
|
|
|
|
|
|
|
|
XP_ASSERT(malt->part_buffer);
|
|
|
|
if (!malt->part_buffer) return -1;
|
|
|
|
|
|
|
|
/* Push this line into the buffer for later retrieval. */
|
|
|
|
return MimePartBufferWrite (malt->part_buffer, line, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartAlternative_close_child(MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeMultipart *mult = (MimeMultipart *) obj;
|
|
|
|
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
|
|
|
|
|
|
|
|
/* XP_ASSERT(malt->part_buffer); Some Mac brokenness trips this...
|
|
|
|
if (!malt->part_buffer) return -1; */
|
|
|
|
|
|
|
|
if (malt->part_buffer)
|
|
|
|
MimePartBufferClose(malt->part_buffer);
|
|
|
|
|
|
|
|
/* XP_ASSERT(mult->hdrs); I expect the Mac trips this too */
|
|
|
|
if (mult->hdrs)
|
|
|
|
MimeHeaders_free(mult->hdrs);
|
|
|
|
mult->hdrs = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
MimeMultipartAlternative_display_part_p(MimeObject *self,
|
|
|
|
MimeHeaders *sub_hdrs)
|
|
|
|
{
|
|
|
|
char *ct = MimeHeaders_get (sub_hdrs, HEADER_CONTENT_TYPE, TRUE, FALSE);
|
|
|
|
|
|
|
|
/* RFC 1521 says:
|
|
|
|
Receiving user agents should pick and display the last format
|
|
|
|
they are capable of displaying. In the case where one of the
|
|
|
|
alternatives is itself of type "multipart" and contains unrecognized
|
|
|
|
sub-parts, the user agent may choose either to show that alternative,
|
|
|
|
an earlier alternative, or both.
|
|
|
|
|
|
|
|
Ugh. If there is a multipart subtype of alternative, we simply show
|
|
|
|
that, without descending into it to determine if any of its sub-parts
|
|
|
|
are themselves unknown.
|
|
|
|
*/
|
|
|
|
|
|
|
|
MimeObjectClass *class = mime_find_class (ct, sub_hdrs, self->options, TRUE);
|
|
|
|
XP_Bool result = (class
|
|
|
|
? class->displayable_inline_p(class, sub_hdrs)
|
|
|
|
: FALSE);
|
|
|
|
FREEIF(ct);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartAlternative_discard_cached_part(MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
|
|
|
|
|
|
|
|
if (malt->buffered_hdrs)
|
|
|
|
{
|
|
|
|
MimeHeaders_free(malt->buffered_hdrs);
|
|
|
|
malt->buffered_hdrs = 0;
|
|
|
|
}
|
|
|
|
if (malt->part_buffer)
|
|
|
|
MimePartBufferReset (malt->part_buffer);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartAlternative_display_cached_part(MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
char *ct = (malt->buffered_hdrs
|
|
|
|
? MimeHeaders_get (malt->buffered_hdrs, HEADER_CONTENT_TYPE,
|
|
|
|
TRUE, FALSE)
|
|
|
|
: 0);
|
|
|
|
const char *dct = (((MimeMultipartClass *) obj->class)->default_part_type);
|
|
|
|
MimeObject *body;
|
|
|
|
XP_Bool multipart_p;
|
|
|
|
|
|
|
|
/* Don't pass in NULL as the content-type (this means that the
|
|
|
|
auto-uudecode-hack won't ever be done for subparts of a
|
|
|
|
multipart, but only for untyped children of message/rfc822.
|
|
|
|
*/
|
|
|
|
body = mime_create(((ct && *ct) ? ct : (dct ? dct: TEXT_PLAIN)),
|
|
|
|
malt->buffered_hdrs, obj->options);
|
|
|
|
|
|
|
|
FREEIF(ct);
|
|
|
|
if (!body) return MK_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
multipart_p = mime_typep(body, (MimeObjectClass *) &mimeMultipartClass);
|
|
|
|
|
|
|
|
status = ((MimeContainerClass *) obj->class)->add_child(obj, body);
|
|
|
|
if (status < 0)
|
|
|
|
{
|
|
|
|
mime_free(body);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MIME_DRAFTS
|
|
|
|
if ( obj->options &&
|
|
|
|
obj->options->decompose_file_p &&
|
|
|
|
!multipart_p &&
|
|
|
|
obj->options->decompose_file_init_fn )
|
|
|
|
{
|
|
|
|
status = obj->options->decompose_file_init_fn (
|
|
|
|
obj->options->stream_closure,
|
|
|
|
malt->buffered_hdrs);
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
#endif /* MIME_DRAFTS */
|
|
|
|
|
|
|
|
|
|
|
|
/* Now that we've added this new object to our list of children,
|
|
|
|
start its parser going. */
|
|
|
|
status = body->class->parse_begin(body);
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
#ifdef MIME_DRAFTS
|
|
|
|
if ( obj->options &&
|
|
|
|
obj->options->decompose_file_p &&
|
|
|
|
!multipart_p &&
|
|
|
|
obj->options->decompose_file_output_fn )
|
|
|
|
status = MimePartBufferRead (malt->part_buffer,
|
|
|
|
obj->options->decompose_file_output_fn,
|
|
|
|
obj->options->stream_closure);
|
|
|
|
else
|
|
|
|
#endif /* MIME_DRAFTS */
|
|
|
|
|
|
|
|
status = MimePartBufferRead (malt->part_buffer,
|
|
|
|
/* The (int (*) ...) cast is to turn the
|
|
|
|
`void' argument into `MimeObject'. */
|
|
|
|
((int (*) (char *, int32, void *))
|
|
|
|
body->class->parse_buffer),
|
|
|
|
body);
|
|
|
|
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
MimeMultipartAlternative_cleanup(obj);
|
|
|
|
|
|
|
|
/* Done parsing. */
|
|
|
|
status = body->class->parse_eof(body, FALSE);
|
|
|
|
if (status < 0) return status;
|
|
|
|
status = body->class->parse_end(body, FALSE);
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
#ifdef MIME_DRAFTS
|
|
|
|
if ( obj->options &&
|
|
|
|
obj->options->decompose_file_p &&
|
|
|
|
!multipart_p &&
|
|
|
|
obj->options->decompose_file_close_fn ) {
|
|
|
|
status = obj->options->decompose_file_close_fn ( obj->options->stream_closure );
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
#endif /* MIME_DRAFTS */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|