summaryrefslogtreecommitdiff
path: root/mailnews/mime/src/mimeunty.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/mime/src/mimeunty.cpp')
-rw-r--r--mailnews/mime/src/mimeunty.cpp588
1 files changed, 588 insertions, 0 deletions
diff --git a/mailnews/mime/src/mimeunty.cpp b/mailnews/mime/src/mimeunty.cpp
new file mode 100644
index 0000000000..212b08ba8f
--- /dev/null
+++ b/mailnews/mime/src/mimeunty.cpp
@@ -0,0 +1,588 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mimeunty.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "prlog.h"
+#include "nsMimeTypes.h"
+#include "msgCore.h"
+#include "nsMimeStringResources.h"
+#include <ctype.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 (const char *, int32_t, 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 bool MimeUntypedText_uu_begin_line_p(const char *line, int32_t length,
+ MimeDisplayOptions *opt,
+ char **type_ret,
+ char **name_ret);
+static bool MimeUntypedText_uu_end_line_p(const char *line, int32_t length);
+
+static bool MimeUntypedText_yenc_begin_line_p(const char *line, int32_t length,
+ MimeDisplayOptions *opt,
+ char **type_ret,
+ char **name_ret);
+static bool MimeUntypedText_yenc_end_line_p(const char *line, int32_t length);
+
+static bool MimeUntypedText_binhex_begin_line_p(const char *line,
+ int32_t length,
+ MimeDisplayOptions *opt);
+static bool MimeUntypedText_binhex_end_line_p(const char *line,
+ int32_t length);
+
+static int
+MimeUntypedTextClassInitialize(MimeUntypedTextClass *clazz)
+{
+ MimeObjectClass *oclass = (MimeObjectClass *) clazz;
+ PR_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 (const char *line, int32_t length, MimeObject *obj)
+{
+ MimeUntypedText *uty = (MimeUntypedText *) obj;
+ int status = 0;
+ char *name = 0, *type = 0;
+ bool begin_line_p = false;
+
+ NS_ASSERTION(line && *line, "empty line in mime untyped parse_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);
+ PR_FREEIF(name);
+ PR_FREEIF(type);
+ if (status < 0) return status;
+ begin_line_p = true;
+ }
+
+ else if (line[0] == '=' &&
+ MimeUntypedText_yenc_begin_line_p(line, length, obj->options,
+ &type, &name))
+ {
+ /* Close the old part and open a new one. */
+ status = MimeUntypedText_open_subpart (obj,
+ MimeUntypedTextSubpartTypeYEnc,
+ type, ENCODING_YENCODE,
+ name, NULL);
+ PR_FREEIF(name);
+ PR_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)
+ {
+ // rhp: If we get here and we are being fed a line ending, we should
+ // just eat it and continue and if we really get more data, we'll open
+ // up the subpart then.
+ //
+ if (line[0] == '\r') return 0;
+ if (line[0] == '\n') return 0;
+
+ PR_ASSERT(!begin_line_p);
+ status = MimeUntypedText_open_subpart (obj,
+ MimeUntypedTextSubpartTypeText,
+ TEXT_PLAIN, NULL, NULL, NULL);
+ PR_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->clazz->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;
+ NS_ASSERTION(!uty->open_subpart, "no open subpart");
+ }
+ else if (line[0] == '=' &&
+ uty->type == MimeUntypedTextSubpartTypeYEnc &&
+ MimeUntypedText_yenc_end_line_p(line, length))
+ {
+ status = MimeUntypedText_close_subpart (obj);
+ if (status < 0) return status;
+ NS_ASSERTION(!uty->open_subpart, "no open subpart");
+ }
+ else if (uty->type == MimeUntypedTextSubpartTypeBinhex &&
+ MimeUntypedText_binhex_end_line_p(line, length))
+ {
+ status = MimeUntypedText_close_subpart (obj);
+ if (status < 0) return status;
+ NS_ASSERTION(!uty->open_subpart, "no 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->clazz->parse_eof(uty->open_subpart, false);
+ uty->open_subpart = 0;
+
+ PR_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;
+ }
+
+ PR_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 || !PL_strcasecmp(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;
+ }
+ NS_ASSERTION(!uty->open_subpart, "no open subpart");
+ NS_ASSERTION(!uty->open_hdrs, "no open headers");
+
+ /* 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 MIME_OUT_OF_MEMORY;
+
+ uint32_t hlen = strlen(type) +
+ (enc ? strlen(enc) : 0) +
+ (desc ? strlen(desc) : 0) +
+ (name ? strlen(name) : 0) +
+ 100;
+ h = (char *) PR_MALLOC(hlen);
+ if (!h) return MIME_OUT_OF_MEMORY;
+
+ PL_strncpyz(h, HEADER_CONTENT_TYPE ": ", hlen);
+ PL_strcatn(h, hlen, type);
+ PL_strcatn(h, hlen, MSG_LINEBREAK);
+ status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
+ if (status < 0) goto FAIL;
+
+ if (enc)
+ {
+ PL_strncpyz(h, HEADER_CONTENT_TRANSFER_ENCODING ": ", hlen);
+ PL_strcatn(h, hlen, enc);
+ PL_strcatn(h, hlen, MSG_LINEBREAK);
+ status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
+ if (status < 0) goto FAIL;
+ }
+
+ if (desc)
+ {
+ PL_strncpyz(h, HEADER_CONTENT_DESCRIPTION ": ", hlen);
+ PL_strcatn(h, hlen, desc);
+ PL_strcatn(h, hlen, MSG_LINEBREAK);
+ status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
+ if (status < 0) goto FAIL;
+ }
+ if (name)
+ {
+ PL_strncpyz(h, HEADER_CONTENT_DISPOSITION ": inline; filename=\"", hlen);
+ PL_strcatn(h, hlen, name);
+ PL_strcatn(h, hlen, "\"" MSG_LINEBREAK);
+ status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
+ if (status < 0) goto FAIL;
+ }
+
+ /* push out a blank line. */
+ PL_strncpyz(h, MSG_LINEBREAK, hlen);
+ status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
+ if (status < 0) goto FAIL;
+
+
+ /* Create a child... */
+ {
+ 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 = MIME_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+ }
+
+ /* Add it to the list... */
+ status = ((MimeContainerClass *) obj->clazz)->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->clazz->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:
+ PR_FREEIF(h);
+
+ if (status < 0 && uty->open_hdrs)
+ {
+ MimeHeaders_free(uty->open_hdrs);
+ uty->open_hdrs = 0;
+ }
+
+ return status;
+}
+
+static bool
+MimeUntypedText_uu_begin_line_p(const char *line, int32_t 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 (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 (IS_SPACE(*s))
+ s++;
+
+ name = (char *) PR_MALLOC(((line+length)-s) + 1);
+ if (!name) return false; /* grr... */
+ memcpy(name, s, (line+length)-s);
+ name[(line+length)-s] = 0;
+
+ /* take off newline. */
+ if (name[strlen(name)-1] == '\n') name[strlen(name)-1] = 0;
+ if (name[strlen(name)-1] == '\r') name[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
+ PR_FREEIF(name);
+
+ if (type_ret)
+ *type_ret = type;
+ else
+ PR_FREEIF(type);
+
+ return true;
+}
+
+static bool
+MimeUntypedText_uu_end_line_p(const char *line, int32_t length)
+{
+#if 0
+ /* A strictly conforming uuencode end line. */
+ return (line[0] == 'e' &&
+ line[1] == 'n' &&
+ line[2] == 'd' &&
+ (line[3] == 0 || 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
+}
+
+static bool
+MimeUntypedText_yenc_begin_line_p(const char *line, int32_t length,
+ MimeDisplayOptions *opt,
+ char **type_ret, char **name_ret)
+{
+ const char *s;
+ const char *endofline = line + length;
+ char *name = 0;
+ char *type = 0;
+
+ if (type_ret) *type_ret = 0;
+ if (name_ret) *name_ret = 0;
+
+ /* we don't support yenc V2 neither multipart yencode,
+ therefore the second parameter should always be "line="*/
+ if (length < 13 || strncmp (line, "=ybegin line=", 13)) return false;
+
+ /* ...then couple digits. */
+ for (s = line + 13; s < endofline; s ++)
+ if (*s < '0' || *s > '9')
+ break;
+
+ /* ...next, look for <space>size= */
+ if ((endofline - s) < 6 || strncmp (s, " size=", 6)) return false;
+
+ /* ...then couple digits. */
+ for (s += 6; s < endofline; s ++)
+ if (*s < '0' || *s > '9')
+ break;
+
+ /* ...next, look for <space>name= */
+ if ((endofline - s) < 6 || strncmp (s, " name=", 6)) return false;
+
+ /* anything left is the file name */
+ s += 6;
+ name = (char *) PR_MALLOC((endofline-s) + 1);
+ if (!name) return false; /* grr... */
+ memcpy(name, s, endofline-s);
+ name[endofline-s] = 0;
+
+ /* take off newline. */
+ if (name[strlen(name)-1] == '\n') name[strlen(name)-1] = 0;
+ if (name[strlen(name)-1] == '\r') name[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
+ PR_FREEIF(name);
+
+ if (type_ret)
+ *type_ret = type;
+ else
+ PR_FREEIF(type);
+
+ return true;
+}
+
+static bool
+MimeUntypedText_yenc_end_line_p(const char *line, int32_t length)
+{
+ if (length < 11 || strncmp (line, "=yend size=", 11)) return false;
+
+ return true;
+}
+
+
+#define BINHEX_MAGIC "(This file must be converted with BinHex 4.0)"
+#define BINHEX_MAGIC_LEN 45
+
+static bool
+MimeUntypedText_binhex_begin_line_p(const char *line, int32_t length,
+ MimeDisplayOptions *opt)
+{
+ if (length <= BINHEX_MAGIC_LEN)
+ return false;
+
+ while(length > 0 && IS_SPACE(line[length-1]))
+ length--;
+
+ if (length != BINHEX_MAGIC_LEN)
+ return false;
+
+ if (!strncmp(line, BINHEX_MAGIC, BINHEX_MAGIC_LEN))
+ return true;
+ else
+ return false;
+}
+
+static bool
+MimeUntypedText_binhex_end_line_p(const char *line, int32_t length)
+{
+ if (length > 0 && line[length-1] == '\n') length--;
+ if (length > 0 && line[length-1] == '\r') length--;
+
+ if (length != 0 && length != 64)
+ return true;
+ else
+ return false;
+}