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.
|
|
|
|
*/
|
|
|
|
|
1998-06-30 18:06:34 +04:00
|
|
|
/* mimeunty.c --- definition of the MimeUntypedText class (see mimei.h)
|
1998-03-28 05:44:41 +03:00
|
|
|
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 "mimeunty.h"
|
|
|
|
|
|
|
|
#define MIME_SUPERCLASS mimeContainerClass
|
|
|
|
MimeDefClass(MimeUntypedText, MimeUntypedTextClass,
|
|
|
|
mimeUntypedTextClass, &MIME_SUPERCLASS);
|
|
|
|
|
|
|
|
static int MimeUntypedText_initialize (MimeObject *);
|
|
|
|
static void MimeUntypedText_finalize (MimeObject *);
|
|
|
|
static int MimeUntypedText_parse_begin (MimeObject *);
|
|
|
|
static int MimeUntypedText_parse_line (char *, int32, MimeObject *);
|
|
|
|
|
|
|
|
static int MimeUntypedText_open_subpart (MimeObject *obj,
|
|
|
|
MimeUntypedTextSubpartType ttype,
|
|
|
|
const char *type,
|
|
|
|
const char *enc,
|
|
|
|
const char *name,
|
|
|
|
const char *desc);
|
|
|
|
static int MimeUntypedText_close_subpart (MimeObject *obj);
|
|
|
|
|
|
|
|
static XP_Bool MimeUntypedText_uu_begin_line_p(const char *line, int32 length,
|
|
|
|
MimeDisplayOptions *opt,
|
|
|
|
char **type_ret,
|
|
|
|
char **name_ret);
|
|
|
|
static XP_Bool MimeUntypedText_uu_end_line_p(const char *line, int32 length);
|
|
|
|
|
|
|
|
static XP_Bool MimeUntypedText_binhex_begin_line_p(const char *line,
|
|
|
|
int32 length,
|
|
|
|
MimeDisplayOptions *opt);
|
|
|
|
static XP_Bool MimeUntypedText_binhex_end_line_p(const char *line,
|
|
|
|
int32 length);
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeUntypedTextClassInitialize(MimeUntypedTextClass *class)
|
|
|
|
{
|
|
|
|
MimeObjectClass *oclass = (MimeObjectClass *) class;
|
|
|
|
XP_ASSERT(!oclass->class_initialized);
|
|
|
|
oclass->initialize = MimeUntypedText_initialize;
|
|
|
|
oclass->finalize = MimeUntypedText_finalize;
|
|
|
|
oclass->parse_begin = MimeUntypedText_parse_begin;
|
|
|
|
oclass->parse_line = MimeUntypedText_parse_line;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeUntypedText_initialize (MimeObject *object)
|
|
|
|
{
|
|
|
|
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
MimeUntypedText_finalize (MimeObject *object)
|
|
|
|
{
|
|
|
|
MimeUntypedText *uty = (MimeUntypedText *) object;
|
|
|
|
|
|
|
|
if (uty->open_hdrs)
|
|
|
|
{
|
|
|
|
/* Oops, those shouldn't still be here... */
|
|
|
|
MimeHeaders_free(uty->open_hdrs);
|
|
|
|
uty->open_hdrs = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* What about the open_subpart? We're gonna have to assume that it
|
|
|
|
is also on the MimeContainer->children list, and will get cleaned
|
|
|
|
up by that class. */
|
|
|
|
|
|
|
|
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeUntypedText_parse_begin (MimeObject *obj)
|
|
|
|
{
|
|
|
|
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeUntypedText_parse_line (char *line, int32 length, MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeUntypedText *uty = (MimeUntypedText *) obj;
|
|
|
|
int status = 0;
|
|
|
|
char *name = 0, *type = 0;
|
|
|
|
XP_Bool begin_line_p = FALSE;
|
|
|
|
|
|
|
|
XP_ASSERT(line && *line);
|
|
|
|
if (!line || !*line) return -1;
|
|
|
|
|
|
|
|
/* If we're supposed to write this object, but aren't supposed to convert
|
|
|
|
it to HTML, simply pass it through unaltered. */
|
|
|
|
if (obj->output_p &&
|
|
|
|
obj->options &&
|
|
|
|
!obj->options->write_html_p &&
|
|
|
|
obj->options->output_fn)
|
|
|
|
return MimeObject_write(obj, line, length, TRUE);
|
|
|
|
|
|
|
|
|
|
|
|
/* Open a new sub-part if this line demands it.
|
|
|
|
*/
|
|
|
|
if (line[0] == 'b' &&
|
|
|
|
MimeUntypedText_uu_begin_line_p(line, length, obj->options,
|
|
|
|
&type, &name))
|
|
|
|
{
|
|
|
|
/* Close the old part and open a new one. */
|
|
|
|
status = MimeUntypedText_open_subpart (obj,
|
|
|
|
MimeUntypedTextSubpartTypeUUE,
|
|
|
|
type, ENCODING_UUENCODE,
|
|
|
|
name, NULL);
|
|
|
|
FREEIF(name);
|
|
|
|
FREEIF(type);
|
|
|
|
if (status < 0) return status;
|
|
|
|
begin_line_p = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (line[0] == '(' && line[1] == 'T' &&
|
|
|
|
MimeUntypedText_binhex_begin_line_p(line, length, obj->options))
|
|
|
|
{
|
|
|
|
/* Close the old part and open a new one. */
|
|
|
|
status = MimeUntypedText_open_subpart (obj,
|
|
|
|
MimeUntypedTextSubpartTypeBinhex,
|
|
|
|
APPLICATION_BINHEX, NULL,
|
|
|
|
NULL, NULL);
|
|
|
|
if (status < 0) return status;
|
|
|
|
begin_line_p = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open a text/plain sub-part if there is no sub-part open.
|
|
|
|
*/
|
|
|
|
if (!uty->open_subpart)
|
|
|
|
{
|
|
|
|
XP_ASSERT(!begin_line_p);
|
|
|
|
status = MimeUntypedText_open_subpart (obj,
|
|
|
|
MimeUntypedTextSubpartTypeText,
|
|
|
|
TEXT_PLAIN, NULL, NULL, NULL);
|
|
|
|
XP_ASSERT(uty->open_subpart);
|
|
|
|
if (!uty->open_subpart) return -1;
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hand this line to the currently-open sub-part.
|
|
|
|
*/
|
|
|
|
status = uty->open_subpart->class->parse_buffer(line, length,
|
|
|
|
uty->open_subpart);
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
/* Close this sub-part if this line demands it.
|
|
|
|
*/
|
|
|
|
if (begin_line_p)
|
|
|
|
;
|
|
|
|
else if (line[0] == 'e' &&
|
|
|
|
uty->type == MimeUntypedTextSubpartTypeUUE &&
|
|
|
|
MimeUntypedText_uu_end_line_p(line, length))
|
|
|
|
{
|
|
|
|
status = MimeUntypedText_close_subpart (obj);
|
|
|
|
if (status < 0) return status;
|
|
|
|
XP_ASSERT(!uty->open_subpart);
|
|
|
|
}
|
|
|
|
else if (uty->type == MimeUntypedTextSubpartTypeBinhex &&
|
|
|
|
MimeUntypedText_binhex_end_line_p(line, length))
|
|
|
|
{
|
|
|
|
status = MimeUntypedText_close_subpart (obj);
|
|
|
|
if (status < 0) return status;
|
|
|
|
XP_ASSERT(!uty->open_subpart);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeUntypedText_close_subpart (MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeUntypedText *uty = (MimeUntypedText *) obj;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (uty->open_subpart)
|
|
|
|
{
|
|
|
|
status = uty->open_subpart->class->parse_eof(uty->open_subpart, FALSE);
|
|
|
|
uty->open_subpart = 0;
|
|
|
|
|
|
|
|
XP_ASSERT(uty->open_hdrs);
|
|
|
|
if (uty->open_hdrs)
|
|
|
|
{
|
|
|
|
MimeHeaders_free(uty->open_hdrs);
|
|
|
|
uty->open_hdrs = 0;
|
|
|
|
}
|
|
|
|
uty->type = MimeUntypedTextSubpartTypeText;
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
/* Never put out a separator between sub-parts of UntypedText.
|
|
|
|
(This bypasses the rule that text/plain subparts always
|
|
|
|
have separators before and after them.)
|
|
|
|
*/
|
|
|
|
if (obj->options && obj->options->state)
|
|
|
|
obj->options->state->separator_suppressed_p = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
XP_ASSERT(!uty->open_hdrs);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeUntypedText_open_subpart (MimeObject *obj,
|
|
|
|
MimeUntypedTextSubpartType ttype,
|
|
|
|
const char *type,
|
|
|
|
const char *enc,
|
|
|
|
const char *name,
|
|
|
|
const char *desc)
|
|
|
|
{
|
|
|
|
MimeUntypedText *uty = (MimeUntypedText *) obj;
|
|
|
|
int status = 0;
|
|
|
|
char *h = 0;
|
|
|
|
|
|
|
|
if (!type || !*type || !strcasecomp(type, UNKNOWN_CONTENT_TYPE))
|
|
|
|
type = APPLICATION_OCTET_STREAM;
|
|
|
|
if (enc && !*enc)
|
|
|
|
enc = 0;
|
|
|
|
if (desc && !*desc)
|
|
|
|
desc = 0;
|
|
|
|
if (name && !*name)
|
|
|
|
name = 0;
|
|
|
|
|
|
|
|
if (uty->open_subpart)
|
|
|
|
{
|
|
|
|
status = MimeUntypedText_close_subpart (obj);
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
XP_ASSERT(!uty->open_subpart);
|
|
|
|
XP_ASSERT(!uty->open_hdrs);
|
|
|
|
|
|
|
|
/* To make one of these implicitly-typed sub-objects, we make up a fake
|
|
|
|
header block, containing only the minimum number of MIME headers needed.
|
|
|
|
We could do most of this (Type and Encoding) by making a null header
|
|
|
|
block, and simply setting obj->content_type and obj->encoding; but making
|
|
|
|
a fake header block is better for two reasons: first, it means that
|
|
|
|
something will actually be displayed when in `Show All Headers' mode;
|
|
|
|
and second, it's the only way to communicate the filename parameter,
|
|
|
|
aside from adding a new slot to MimeObject (which is something to be
|
|
|
|
avoided when possible.)
|
|
|
|
*/
|
|
|
|
|
|
|
|
uty->open_hdrs = MimeHeaders_new();
|
|
|
|
if (!uty->open_hdrs) return MK_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
h = (char *) XP_ALLOC(XP_STRLEN(type) +
|
|
|
|
(enc ? XP_STRLEN(enc) : 0) +
|
|
|
|
(desc ? XP_STRLEN(desc) : 0) +
|
|
|
|
(name ? XP_STRLEN(name) : 0) +
|
|
|
|
100);
|
|
|
|
if (!h) return MK_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
XP_STRCPY(h, HEADER_CONTENT_TYPE ": ");
|
|
|
|
XP_STRCAT(h, type);
|
|
|
|
XP_STRCAT(h, LINEBREAK);
|
|
|
|
status = MimeHeaders_parse_line(h, XP_STRLEN(h), uty->open_hdrs);
|
|
|
|
if (status < 0) goto FAIL;
|
|
|
|
|
|
|
|
if (enc)
|
|
|
|
{
|
|
|
|
XP_STRCPY(h, HEADER_CONTENT_TRANSFER_ENCODING ": ");
|
|
|
|
XP_STRCAT(h, enc);
|
|
|
|
XP_STRCAT(h, LINEBREAK);
|
|
|
|
status = MimeHeaders_parse_line(h, XP_STRLEN(h), uty->open_hdrs);
|
|
|
|
if (status < 0) goto FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (desc)
|
|
|
|
{
|
|
|
|
XP_STRCPY(h, HEADER_CONTENT_DESCRIPTION ": ");
|
|
|
|
XP_STRCAT(h, desc);
|
|
|
|
XP_STRCAT(h, LINEBREAK);
|
|
|
|
status = MimeHeaders_parse_line(h, XP_STRLEN(h), uty->open_hdrs);
|
|
|
|
if (status < 0) goto FAIL;
|
|
|
|
}
|
|
|
|
if (name)
|
|
|
|
{
|
|
|
|
XP_STRCPY(h, HEADER_CONTENT_DISPOSITION ": inline; filename=\"");
|
|
|
|
XP_STRCAT(h, name);
|
|
|
|
XP_STRCAT(h, "\"" LINEBREAK);
|
|
|
|
status = MimeHeaders_parse_line(h, XP_STRLEN(h), uty->open_hdrs);
|
|
|
|
if (status < 0) goto FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* push out a blank line. */
|
|
|
|
XP_STRCPY(h, LINEBREAK);
|
|
|
|
status = MimeHeaders_parse_line(h, XP_STRLEN(h), uty->open_hdrs);
|
|
|
|
if (status < 0) goto FAIL;
|
|
|
|
|
|
|
|
|
|
|
|
/* Create a child... */
|
|
|
|
{
|
|
|
|
XP_Bool horrid_kludge = (obj->options && obj->options->state &&
|
|
|
|
obj->options->state->first_part_written_p);
|
|
|
|
if (horrid_kludge)
|
|
|
|
obj->options->state->first_part_written_p = FALSE;
|
|
|
|
|
|
|
|
uty->open_subpart = mime_create(type, uty->open_hdrs, obj->options);
|
|
|
|
|
|
|
|
if (horrid_kludge)
|
|
|
|
obj->options->state->first_part_written_p = TRUE;
|
|
|
|
|
|
|
|
if (!uty->open_subpart)
|
|
|
|
{
|
|
|
|
status = MK_OUT_OF_MEMORY;
|
|
|
|
goto FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add it to the list... */
|
|
|
|
status = ((MimeContainerClass *) obj->class)->add_child(obj,
|
|
|
|
uty->open_subpart);
|
|
|
|
if (status < 0)
|
|
|
|
{
|
|
|
|
mime_free(uty->open_subpart);
|
|
|
|
uty->open_subpart = 0;
|
|
|
|
goto FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* And start its parser going. */
|
|
|
|
status = uty->open_subpart->class->parse_begin(uty->open_subpart);
|
|
|
|
if (status < 0)
|
|
|
|
{
|
|
|
|
/* MimeContainer->finalize will take care of shutting it down now. */
|
|
|
|
uty->open_subpart = 0;
|
|
|
|
goto FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
uty->type = ttype;
|
|
|
|
|
|
|
|
FAIL:
|
|
|
|
FREEIF(h);
|
|
|
|
|
|
|
|
if (status < 0 && uty->open_hdrs)
|
|
|
|
{
|
|
|
|
MimeHeaders_free(uty->open_hdrs);
|
|
|
|
uty->open_hdrs = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
MimeUntypedText_uu_begin_line_p(const char *line, int32 length,
|
|
|
|
MimeDisplayOptions *opt,
|
|
|
|
char **type_ret, char **name_ret)
|
|
|
|
{
|
|
|
|
const char *s;
|
|
|
|
char *name = 0;
|
|
|
|
char *type = 0;
|
|
|
|
|
|
|
|
if (type_ret) *type_ret = 0;
|
|
|
|
if (name_ret) *name_ret = 0;
|
|
|
|
|
|
|
|
if (XP_STRNCMP (line, "begin ", 6)) return FALSE;
|
|
|
|
/* ...then three or four octal digits. */
|
|
|
|
s = line + 6;
|
|
|
|
if (*s < '0' || *s > '7') return FALSE;
|
|
|
|
s++;
|
|
|
|
if (*s < '0' || *s > '7') return FALSE;
|
|
|
|
s++;
|
|
|
|
if (*s < '0' || *s > '7') return FALSE;
|
|
|
|
s++;
|
|
|
|
if (*s == ' ')
|
|
|
|
s++;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (*s < '0' || *s > '7') return FALSE;
|
|
|
|
s++;
|
|
|
|
if (*s != ' ') return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (XP_IS_SPACE(*s))
|
|
|
|
s++;
|
|
|
|
|
|
|
|
name = (char *) XP_ALLOC(((line+length)-s) + 1);
|
|
|
|
if (!name) return FALSE; /* grr... */
|
|
|
|
XP_MEMCPY(name, s, (line+length)-s);
|
|
|
|
name[(line+length)-s] = 0;
|
|
|
|
|
|
|
|
/* take off newline. */
|
|
|
|
if (name[XP_STRLEN(name)-1] == LF) name[XP_STRLEN(name)-1] = 0;
|
|
|
|
if (name[XP_STRLEN(name)-1] == CR) name[XP_STRLEN(name)-1] = 0;
|
|
|
|
|
|
|
|
/* Now try and figure out a type.
|
|
|
|
*/
|
|
|
|
if (opt && opt->file_type_fn)
|
|
|
|
type = opt->file_type_fn(name, opt->stream_closure);
|
|
|
|
else
|
|
|
|
type = 0;
|
|
|
|
|
|
|
|
if (name_ret)
|
|
|
|
*name_ret = name;
|
|
|
|
else
|
|
|
|
FREEIF(name);
|
|
|
|
|
|
|
|
if (type_ret)
|
|
|
|
*type_ret = type;
|
|
|
|
else
|
|
|
|
FREEIF(type);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
MimeUntypedText_uu_end_line_p(const char *line, int32 length)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
/* A strictly conforming uuencode end line. */
|
|
|
|
return (line[0] == 'e' &&
|
|
|
|
line[1] == 'n' &&
|
|
|
|
line[2] == 'd' &&
|
|
|
|
(line[3] == 0 || XP_IS_SPACE(line[3])));
|
|
|
|
#else
|
|
|
|
/* ...but, why don't we accept any line that begins with the three
|
|
|
|
letters "END" in any case: I've seen lots of partial messages
|
|
|
|
that look like
|
|
|
|
|
|
|
|
BEGIN----- Cut Here-----
|
|
|
|
begin 644 foo.gif
|
|
|
|
Mxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
|
|
Mxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
|
|
Mxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
|
|
END------- Cut Here-----
|
|
|
|
|
|
|
|
so let's be lenient here. (This is only for the untyped-text-plain
|
|
|
|
case -- the uudecode parser itself is strict.)
|
|
|
|
*/
|
|
|
|
return (line[0] == ' ' ||
|
|
|
|
line[0] == '\t' ||
|
|
|
|
((line[0] == 'e' || line[0] == 'E') &&
|
|
|
|
(line[1] == 'n' || line[1] == 'N') &&
|
|
|
|
(line[2] == 'd' || line[2] == 'D')));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define BINHEX_MAGIC "(This file must be converted with BinHex 4.0)"
|
|
|
|
#define BINHEX_MAGIC_LEN 45
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
MimeUntypedText_binhex_begin_line_p(const char *line, int32 length,
|
|
|
|
MimeDisplayOptions *opt)
|
|
|
|
{
|
|
|
|
if (length <= BINHEX_MAGIC_LEN)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
while(length > 0 && XP_IS_SPACE(line[length-1]))
|
|
|
|
length--;
|
|
|
|
|
|
|
|
if (length != BINHEX_MAGIC_LEN)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!XP_STRNCMP(line, BINHEX_MAGIC, BINHEX_MAGIC_LEN))
|
|
|
|
return TRUE;
|
|
|
|
else
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
MimeUntypedText_binhex_end_line_p(const char *line, int32 length)
|
|
|
|
{
|
|
|
|
if (length > 0 && line[length-1] == LF) length--;
|
|
|
|
if (length > 0 && line[length-1] == CR) length--;
|
|
|
|
|
|
|
|
if (length != 0 && length != 64)
|
|
|
|
return TRUE;
|
|
|
|
else
|
|
|
|
return FALSE;
|
|
|
|
}
|