summaryrefslogtreecommitdiffstats
path: root/mailnews/mime/src/mimeenc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/mime/src/mimeenc.cpp')
-rw-r--r--mailnews/mime/src/mimeenc.cpp1107
1 files changed, 1107 insertions, 0 deletions
diff --git a/mailnews/mime/src/mimeenc.cpp b/mailnews/mime/src/mimeenc.cpp
new file mode 100644
index 000000000..d565a6067
--- /dev/null
+++ b/mailnews/mime/src/mimeenc.cpp
@@ -0,0 +1,1107 @@
+/* -*- 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 <stdio.h>
+#include "mimei.h"
+#include "prmem.h"
+#include "mimeobj.h"
+#include "mozilla/RangedPtr.h"
+#include "mozilla/mailnews/MimeEncoder.h"
+
+typedef enum mime_encoding {
+ mime_Base64, mime_QuotedPrintable, mime_uuencode, mime_yencode
+} mime_encoding;
+
+typedef enum mime_decoder_state {
+ DS_BEGIN, DS_BODY, DS_END
+} mime_decoder_state;
+
+struct MimeDecoderData {
+ mime_encoding encoding; /* Which encoding to use */
+
+ /* A read-buffer used for QP and B64. */
+ char token[4];
+ int token_size;
+
+ /* State and read-buffer used for uudecode and yencode. */
+ mime_decoder_state ds_state;
+ char *line_buffer;
+ int line_buffer_size;
+
+ MimeObject *objectToDecode; // might be null, only used for QP currently
+ /* Where to write the decoded data */
+ MimeConverterOutputCallback write_buffer;
+ void *closure;
+};
+
+
+static int
+mime_decode_qp_buffer (MimeDecoderData *data, const char *buffer,
+ int32_t length, int32_t *outSize)
+{
+ /* Warning, we are overwriting the buffer which was passed in.
+ This is ok, because decoding these formats will never result
+ in larger data than the input, only smaller. */
+ const char *in = buffer;
+ char *out = (char *) buffer;
+ char token [3];
+ int i;
+
+ NS_ASSERTION(data->encoding == mime_QuotedPrintable, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ if (data->encoding != mime_QuotedPrintable) return -1;
+
+ /* For the first pass, initialize the token from the unread-buffer. */
+ i = 0;
+ while (i < 3 && data->token_size > 0)
+ {
+ token [i] = data->token[i];
+ data->token_size--;
+ i++;
+ }
+
+ /* #### BUG: when decoding quoted-printable, we are required to
+ strip trailing whitespace from lines -- since when encoding in
+ qp, one is required to quote such trailing whitespace, any
+ trailing whitespace which remains must have been introduced
+ by a stupid gateway. */
+
+ /* Treat null bytes as spaces when format_out is
+ nsMimeOutput::nsMimeMessageBodyDisplay (see bug 243199 comment 7) */
+ bool treatNullAsSpace = data->objectToDecode &&
+ data->objectToDecode->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay;
+
+ while (length > 0 || i != 0)
+ {
+ while (i < 3 && length > 0)
+ {
+ token [i++] = *in;
+ in++;
+ length--;
+ }
+
+ if (i < 3)
+ {
+ /* Didn't get enough for a complete token.
+ If it might be a token, unread it.
+ Otherwise, just dump it.
+ */
+ memcpy (data->token, token, i);
+ data->token_size = i;
+ i = 0;
+ length = 0;
+ break;
+ }
+ i = 0;
+
+ if (token [0] == '=')
+ {
+ unsigned char c = 0;
+ if (token[1] >= '0' && token[1] <= '9')
+ c = token[1] - '0';
+ else if (token[1] >= 'A' && token[1] <= 'F')
+ c = token[1] - ('A' - 10);
+ else if (token[1] >= 'a' && token[1] <= 'f')
+ c = token[1] - ('a' - 10);
+ else if (token[1] == '\r' || token[1] == '\n')
+ {
+ /* =\n means ignore the newline. */
+ if (token[1] == '\r' && token[2] == '\n')
+ ; /* swallow all three chars */
+ else
+ {
+ in--; /* put the third char back */
+ length++;
+ }
+ continue;
+ }
+ else
+ {
+ /* = followed by something other than hex or newline -
+ pass it through unaltered, I guess. (But, if
+ this bogus token happened to occur over a buffer
+ boundary, we can't do this, since we don't have
+ space for it. Oh well. Screw it.) */
+ if (in > out) *out++ = token[0];
+ if (in > out) *out++ = token[1];
+ if (in > out) *out++ = token[2];
+ continue;
+ }
+
+ /* Second hex digit */
+ c = (c << 4);
+ if (token[2] >= '0' && token[2] <= '9')
+ c += token[2] - '0';
+ else if (token[2] >= 'A' && token[2] <= 'F')
+ c += token[2] - ('A' - 10);
+ else if (token[2] >= 'a' && token[2] <= 'f')
+ c += token[2] - ('a' - 10);
+ else
+ {
+ /* We got =xy where "x" was hex and "y" was not, so
+ treat that as a literal "=", x, and y. (But, if
+ this bogus token happened to occur over a buffer
+ boundary, we can't do this, since we don't have
+ space for it. Oh well. Screw it.) */
+ if (in > out) *out++ = token[0];
+ if (in > out) *out++ = token[1];
+ if (in > out) *out++ = token[2];
+ continue;
+ }
+
+ *out++ = c ? (char) c : ((treatNullAsSpace) ? ' ' : (char) c);
+ }
+ else
+ {
+ *out++ = token[0];
+
+ token[0] = token[1];
+ token[1] = token[2];
+ i = 2;
+ }
+ }
+
+ // Fill the size
+ if (outSize)
+ *outSize = out - buffer;
+
+ /* Now that we've altered the data in place, write it. */
+ if (out > buffer)
+ return data->write_buffer (buffer, (out - buffer), data->closure);
+ else
+ return 1;
+}
+
+
+static int
+mime_decode_base64_token (const char *in, char *out)
+{
+ /* reads 4, writes 0-3. Returns bytes written.
+ (Writes less than 3 only at EOF.) */
+ int j;
+ int eq_count = 0;
+ unsigned long num = 0;
+
+ for (j = 0; j < 4; j++)
+ {
+ unsigned char c = 0;
+ if (in[j] >= 'A' && in[j] <= 'Z') c = in[j] - 'A';
+ else if (in[j] >= 'a' && in[j] <= 'z') c = in[j] - ('a' - 26);
+ else if (in[j] >= '0' && in[j] <= '9') c = in[j] - ('0' - 52);
+ else if (in[j] == '+') c = 62;
+ else if (in[j] == '/') c = 63;
+ else if (in[j] == '=') c = 0, eq_count++;
+ else
+ NS_ERROR("Invalid character");
+ num = (num << 6) | c;
+ }
+
+ *out++ = (char) (num >> 16);
+ *out++ = (char) ((num >> 8) & 0xFF);
+ *out++ = (char) (num & 0xFF);
+
+ if (eq_count == 0)
+ return 3; /* No "=" padding means 4 bytes mapped to 3. */
+ else if (eq_count == 1)
+ return 2; /* "xxx=" means 3 bytes mapped to 2. */
+ else if (eq_count == 2)
+ return 1; /* "xx==" means 2 bytes mapped to 1. */
+ else
+ {
+ // "x===" can't happen, because "x" would then be encoding only
+ // 6 bits, not the min of 8.
+ NS_ERROR("Count is 6 bits, should be at least 8");
+ return 1;
+ }
+}
+
+
+static int
+mime_decode_base64_buffer (MimeDecoderData *data,
+ const char *buffer, int32_t length, int32_t *outSize)
+{
+ /* Warning, we are overwriting the buffer which was passed in.
+ This is ok, because decoding these formats will never result
+ in larger data than the input, only smaller. */
+ const char *in = buffer;
+ char *out = (char *) buffer;
+ char token [4];
+ int i;
+ bool leftover = (data->token_size > 0);
+
+ NS_ASSERTION(data->encoding == mime_Base64, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+
+ /* For the first pass, initialize the token from the unread-buffer. */
+ i = 0;
+ while (i < 4 && data->token_size > 0)
+ {
+ token [i] = data->token[i];
+ data->token_size--;
+ i++;
+ }
+
+ while (length > 0)
+ {
+ while (i < 4 && length > 0)
+ {
+ if ((*in >= 'A' && *in <= 'Z') ||
+ (*in >= 'a' && *in <= 'z') ||
+ (*in >= '0' && *in <= '9') ||
+ *in == '+' || *in == '/' || *in == '=')
+ token [i++] = *in;
+ in++;
+ length--;
+ }
+
+ if (i < 4)
+ {
+ /* Didn't get enough for a complete token. */
+ memcpy (data->token, token, i);
+ data->token_size = i;
+ length = 0;
+ break;
+ }
+ i = 0;
+
+ if (leftover)
+ {
+ /* If there are characters left over from the last time around,
+ we might not have space in the buffer to do our dirty work
+ (if there were 2 or 3 left over, then there is only room for
+ 1 or 2 in the buffer right now, and we need 3.) This is only
+ a problem for the first chunk in each buffer, so in that
+ case, just write prematurely. */
+ int n;
+ n = mime_decode_base64_token (token, token);
+ n = data->write_buffer (token, n, data->closure);
+ if (n < 0) /* abort */
+ return n;
+
+ /* increment buffer so that we don't write the 1 or 2 unused
+ characters now at the front. */
+ buffer = in;
+ out = (char *) buffer;
+
+ leftover = false;
+ }
+ else
+ {
+ int n = mime_decode_base64_token (token, out);
+ /* Advance "out" by the number of bytes just written to it. */
+ out += n;
+ }
+ }
+
+ if (outSize)
+ *outSize = out - buffer;
+ /* Now that we've altered the data in place, write it. */
+ if (out > buffer)
+ return data->write_buffer (buffer, (out - buffer), data->closure);
+ else
+ return 1;
+}
+
+
+static int
+mime_decode_uue_buffer (MimeDecoderData *data,
+ const char *input_buffer, int32_t input_length, int32_t *outSize)
+{
+ /* First, copy input_buffer into state->line_buffer until we have
+ a complete line.
+
+ Then decode that line in place (in the line_buffer) and write
+ it out.
+
+ Then pull the next line into line_buffer and continue.
+ */
+ if (!data->line_buffer)
+ {
+ data->line_buffer_size = 128;
+ data->line_buffer = (char *)PR_MALLOC(data->line_buffer_size);
+ if (!data->line_buffer)
+ return -1;
+ data->line_buffer[0] = 0;
+ }
+
+ int status = 0;
+ char *line = data->line_buffer;
+ char *line_end = data->line_buffer + data->line_buffer_size - 1;
+
+ NS_ASSERTION(data->encoding == mime_uuencode, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ if (data->encoding != mime_uuencode) return -1;
+
+ if (data->ds_state == DS_END)
+ {
+ status = 0;
+ goto DONE;
+ }
+
+ while (input_length > 0)
+ {
+ /* Copy data from input_buffer to `line' until we have a complete line,
+ or until we've run out of input.
+
+ (line may have data in it already if the last time we were called,
+ we weren't called with a buffer that ended on a line boundary.)
+ */
+ {
+ char *out = line + strlen(line);
+ while (input_length > 0 &&
+ out < line_end)
+ {
+ *out++ = *input_buffer++;
+ input_length--;
+
+ if (out[-1] == '\r' || out[-1] == '\n')
+ {
+ /* If we just copied a CR, and an LF is waiting, grab it too.
+ */
+ if (out[-1] == '\r' &&
+ input_length > 0 &&
+ *input_buffer == '\n')
+ input_buffer++, input_length--;
+
+ /* We have a line. */
+ break;
+ }
+ }
+ *out = 0;
+
+ /* Ignore blank lines.
+ */
+ if (*line == '\r' || *line == '\n')
+ {
+ *line = 0;
+ continue;
+ }
+
+ /* If this line was bigger than our buffer, truncate it.
+ (This means the data was way corrupted, and there's basically
+ no chance of decoding it properly, but give it a shot anyway.)
+ */
+ if (out == line_end)
+ {
+ out--;
+ out[-1] = '\r';
+ out[0] = 0;
+ }
+
+ /* If we didn't get a complete line, simply return; we'll be called
+ with the rest of this line next time.
+ */
+ if (out[-1] != '\r' && out[-1] != '\n')
+ {
+ NS_ASSERTION (input_length == 0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ break;
+ }
+ }
+
+
+ /* Now we have a complete line. Deal with it.
+ */
+
+
+ if (data->ds_state == DS_BODY &&
+ line[0] == 'e' &&
+ line[1] == 'n' &&
+ line[2] == 'd' &&
+ (line[3] == '\r' ||
+ line[3] == '\n'))
+ {
+ /* done! */
+ data->ds_state = DS_END;
+ *line = 0;
+ break;
+ }
+ else if (data->ds_state == DS_BEGIN)
+ {
+ if (!strncmp (line, "begin ", 6))
+ data->ds_state = DS_BODY;
+ *line = 0;
+ continue;
+ }
+ else
+ {
+ /* We're in DS_BODY. Decode the line. */
+ char *in, *out;
+ int32_t i;
+ long lost;
+
+ NS_ASSERTION (data->ds_state == DS_BODY, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+
+ /* We map down `line', reading four bytes and writing three.
+ That means that `out' always stays safely behind `in'.
+ */
+ in = line;
+ out = line;
+
+# undef DEC
+# define DEC(c) (((c) - ' ') & 077)
+ i = DEC (*in); /* get length */
+
+ /* all the parens and casts are because gcc was doing something evil.
+ */
+ lost = ((long) i) - (((((long) strlen (in)) - 2L) * 3L) / 4L);
+
+ if (lost > 0) /* Short line!! */
+ {
+ /* If we get here, then the line is shorter than the length byte
+ at the beginning says it should be. However, the case where
+ the line is short because it was at the end of the buffer and
+ we didn't get the whole line was handled earlier (up by the
+ "didn't get a complete line" comment.) So if we've gotten
+ here, then this is a complete line which is internally
+ inconsistent. We will parse from it what we can...
+
+ This probably happened because some gateway stripped trailing
+ whitespace from the end of the line -- so pretend the line
+ was padded with spaces (which map to \000.)
+ */
+ i -= lost;
+ }
+
+ for (++in; i > 0; in += 4, i -= 3)
+ {
+ char ch;
+ NS_ASSERTION(out <= in, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+
+ if (i >= 3)
+ {
+ /* We read four; write three. */
+ ch = DEC (in[0]) << 2 | DEC (in[1]) >> 4;
+ *out++ = ch;
+
+ NS_ASSERTION(out <= in+1, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+
+ ch = DEC (in[1]) << 4 | DEC (in[2]) >> 2;
+ *out++ = ch;
+
+ NS_ASSERTION(out <= in+2, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+
+ ch = DEC (in[2]) << 6 | DEC (in[3]);
+ *out++ = ch;
+
+ NS_ASSERTION(out <= in+3, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ }
+ else
+ {
+ /* Handle a line that isn't a multiple of 4 long.
+ (We read 1, 2, or 3, and will write 1 or 2.)
+ */
+ NS_ASSERTION (i > 0 && i < 3, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+
+ ch = DEC (in[0]) << 2 | DEC (in[1]) >> 4;
+ *out++ = ch;
+
+ NS_ASSERTION(out <= in+1, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+
+ if (i == 2)
+ {
+ ch = DEC (in[1]) << 4 | DEC (in[2]) >> 2;
+ *out++ = ch;
+
+ NS_ASSERTION(out <= in+2, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ }
+ }
+ }
+
+ /* If the line was truncated, pad the missing bytes with 0 (SPC). */
+ while (lost > 0)
+ {
+ *out++ = 0;
+ lost--;
+ in = out+1; /* just to prevent the assert, below. */
+ }
+# undef DEC
+
+ /* Now write out what we decoded for this line.
+ */
+ NS_ASSERTION(out >= line && out < in, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ if (out > line)
+ status = data->write_buffer (line, (out - line), data->closure);
+
+ // The assertion above tells us this is >= 0
+ if (outSize)
+ *outSize = out - line;
+
+ /* Reset the line so that we don't think it's partial next time. */
+ *line = 0;
+
+ if (status < 0) /* abort */
+ goto DONE;
+ }
+ }
+
+ status = 1;
+
+ DONE:
+
+ return status;
+}
+
+static int
+mime_decode_yenc_buffer (MimeDecoderData *data,
+ const char *input_buffer, int32_t input_length, int32_t *outSize)
+{
+ /* First, copy input_buffer into state->line_buffer until we have
+ a complete line.
+
+ Then decode that line in place (in the line_buffer) and write
+ it out.
+
+ Then pull the next line into line_buffer and continue.
+ */
+ if (!data->line_buffer)
+ {
+ data->line_buffer_size = 1000; // let make sure we have plenty of space for the header line
+ data->line_buffer = (char *)PR_MALLOC(data->line_buffer_size);
+ if (!data->line_buffer)
+ return -1;
+ data->line_buffer[0] = 0;
+ }
+
+ int status = 0;
+ char *line = data->line_buffer;
+ char *line_end = data->line_buffer + data->line_buffer_size - 1;
+
+ NS_ASSERTION(data->encoding == mime_yencode, "wrong decoder!");
+ if (data->encoding != mime_yencode) return -1;
+
+ if (data->ds_state == DS_END)
+ return 0;
+
+ while (input_length > 0)
+ {
+ /* Copy data from input_buffer to `line' until we have a complete line,
+ or until we've run out of input.
+
+ (line may have data in it already if the last time we were called,
+ we weren't called with a buffer that ended on a line boundary.)
+ */
+ {
+ char *out = line + strlen(line);
+ while (input_length > 0 && out < line_end)
+ {
+ *out++ = *input_buffer++;
+ input_length--;
+
+ if (out[-1] == '\r' || out[-1] == '\n')
+ {
+ /* If we just copied a CR, and an LF is waiting, grab it too. */
+ if (out[-1] == '\r' &&
+ input_length > 0 &&
+ *input_buffer == '\n')
+ input_buffer++, input_length--;
+
+ /* We have a line. */
+ break;
+ }
+ }
+ *out = 0;
+
+ /* Ignore blank lines. */
+ if (*line == '\r' || *line == '\n')
+ {
+ *line = 0;
+ continue;
+ }
+
+ /* If this line was bigger than our buffer, truncate it.
+ (This means the data was way corrupted, and there's basically
+ no chance of decoding it properly, but give it a shot anyway.)
+ */
+ if (out == line_end)
+ {
+ out--;
+ out[-1] = '\r';
+ out[0] = 0;
+ }
+
+ /* If we didn't get a complete line, simply return; we'll be called
+ with the rest of this line next time.
+ */
+ if (out[-1] != '\r' && out[-1] != '\n')
+ {
+ NS_ASSERTION (input_length == 0, "empty buffer!");
+ break;
+ }
+ }
+
+
+ /* Now we have a complete line. Deal with it.
+ */
+ const char * endOfLine = line + strlen(line);
+
+ if (data->ds_state == DS_BEGIN)
+ {
+ int new_line_size = 0;
+ /* this yenc decoder does not support yenc v2 or multipart yenc.
+ Therefore, we are looking first for "=ybegin line="
+ */
+ if ((endOfLine - line) >= 13 && !strncmp (line, "=ybegin line=", 13))
+ {
+ /* ...then couple digits. */
+ for (line += 13; line < endOfLine; line ++)
+ {
+ if (*line < '0' || *line > '9')
+ break;
+ new_line_size = (new_line_size * 10) + *line - '0';
+ }
+
+ /* ...next, look for <space>size= */
+ if ((endOfLine - line) >= 6 && !strncmp (line, " size=", 6))
+ {
+ /* ...then couple digits. */
+ for (line += 6; line < endOfLine; line ++)
+ if (*line < '0' || *line > '9')
+ break;
+
+ /* ...next, look for <space>name= */
+ if ((endOfLine - line) >= 6 && !strncmp (line, " name=", 6))
+ {
+ /* we have found the yenc header line.
+ Now check if we need to grow our buffer line
+ */
+ data->ds_state = DS_BODY;
+ if (new_line_size > data->line_buffer_size && new_line_size <= 997) /* don't let bad value hurt us! */
+ {
+ PR_Free(data->line_buffer);
+ data->line_buffer_size = new_line_size + 4; //extra chars for line ending and potential escape char
+ data->line_buffer = (char *)PR_MALLOC(data->line_buffer_size);
+ if (!data->line_buffer)
+ return -1;
+ }
+ }
+ }
+
+ }
+ *data->line_buffer = 0;
+ continue;
+ }
+
+ if (data->ds_state == DS_BODY && line[0] == '=')
+ {
+ /* look if this this the final line */
+ if (!strncmp (line, "=yend size=", 11))
+ {
+ /* done! */
+ data->ds_state = DS_END;
+ *line = 0;
+ break;
+ }
+ }
+
+ /* We're in DS_BODY. Decode the line in place. */
+ {
+ char *src = line;
+ char *dest = src;
+ char c;
+ for (; src < line_end; src ++)
+ {
+ c = *src;
+ if (!c || c == '\r' || c == '\n')
+ break;
+
+ if (c == '=')
+ {
+ src++;
+ c = *src;
+ if (c == 0)
+ return -1; /* last character cannot be escape char */
+ c -= 64;
+ }
+ c -= 42;
+ *dest = c;
+ dest ++;
+ }
+
+ // The assertion below is helpful, too
+ if (outSize)
+ *outSize = dest - line;
+
+ /* Now write out what we decoded for this line. */
+ NS_ASSERTION(dest >= line && dest <= src, "nothing to write!");
+ if (dest > line)
+ {
+ status = data->write_buffer (line, dest - line, data->closure);
+ if (status < 0) /* abort */
+ return status;
+ }
+
+ /* Reset the line so that we don't think it's partial next time. */
+ *line = 0;
+ }
+ }
+
+ return 1;
+}
+
+int
+MimeDecoderDestroy (MimeDecoderData *data, bool abort_p)
+{
+ int status = 0;
+ /* Flush out the last few buffered characters. */
+ if (!abort_p &&
+ data->token_size > 0 &&
+ data->token[0] != '=')
+ {
+ if (data->encoding == mime_Base64)
+ while ((unsigned int)data->token_size < sizeof (data->token))
+ data->token [data->token_size++] = '=';
+
+ status = data->write_buffer (data->token, data->token_size,
+ data->closure);
+ }
+
+ if (data->line_buffer)
+ PR_Free(data->line_buffer);
+ PR_Free (data);
+ return status;
+}
+
+
+static MimeDecoderData *
+mime_decoder_init (mime_encoding which,
+ MimeConverterOutputCallback output_fn,
+ void *closure)
+{
+ MimeDecoderData *data = PR_NEW(MimeDecoderData);
+ if (!data) return 0;
+ memset(data, 0, sizeof(*data));
+ data->encoding = which;
+ data->write_buffer = output_fn;
+ data->closure = closure;
+ data->line_buffer_size = 0;
+ data->line_buffer = nullptr;
+
+ return data;
+}
+
+MimeDecoderData *
+MimeB64DecoderInit (MimeConverterOutputCallback output_fn, void *closure)
+{
+ return mime_decoder_init (mime_Base64, output_fn, closure);
+}
+
+MimeDecoderData *
+MimeQPDecoderInit (MimeConverterOutputCallback output_fn,
+ void *closure, MimeObject *object)
+{
+ MimeDecoderData *retData = mime_decoder_init (mime_QuotedPrintable, output_fn, closure);
+ if (retData)
+ retData->objectToDecode = object;
+ return retData;
+}
+
+MimeDecoderData *
+MimeUUDecoderInit (MimeConverterOutputCallback output_fn,
+ void *closure)
+{
+ return mime_decoder_init (mime_uuencode, output_fn, closure);
+}
+
+MimeDecoderData *
+MimeYDecoderInit (MimeConverterOutputCallback output_fn,
+ void *closure)
+{
+ return mime_decoder_init (mime_yencode, output_fn, closure);
+}
+
+int
+MimeDecoderWrite (MimeDecoderData *data, const char *buffer, int32_t size,
+ int32_t *outSize)
+{
+ NS_ASSERTION(data, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ if (!data) return -1;
+ switch(data->encoding)
+ {
+ case mime_Base64:
+ return mime_decode_base64_buffer (data, buffer, size, outSize);
+ case mime_QuotedPrintable:
+ return mime_decode_qp_buffer (data, buffer, size, outSize);
+ case mime_uuencode:
+ return mime_decode_uue_buffer (data, buffer, size, outSize);
+ case mime_yencode:
+ return mime_decode_yenc_buffer (data, buffer, size, outSize);
+ default:
+ NS_ERROR("Invalid decoding");
+ return -1;
+ }
+}
+
+
+namespace mozilla {
+namespace mailnews {
+
+MimeEncoder::MimeEncoder(OutputCallback callback, void *closure)
+: mCallback(callback),
+ mClosure(closure),
+ mCurrentColumn(0)
+{}
+
+class Base64Encoder : public MimeEncoder {
+ unsigned char in_buffer[3];
+ int32_t in_buffer_count;
+
+public:
+ Base64Encoder(OutputCallback callback, void *closure)
+ : MimeEncoder(callback, closure),
+ in_buffer_count(0) {}
+ virtual ~Base64Encoder() {}
+
+ virtual nsresult Write(const char *buffer, int32_t size) override;
+ virtual nsresult Flush() override;
+
+private:
+ static void Base64EncodeBits(RangedPtr<char> &out, uint32_t bits);
+};
+
+nsresult Base64Encoder::Write(const char *buffer, int32_t size)
+{
+ if (size == 0)
+ return NS_OK;
+ else if (size < 0)
+ {
+ NS_ERROR("Size is less than 0");
+ return NS_ERROR_FAILURE;
+ }
+
+ // If this input buffer is too small, wait until next time.
+ if (size < (3 - in_buffer_count))
+ {
+ NS_ASSERTION(size == 1 || size == 2, "Unexpected size");
+ in_buffer[in_buffer_count++] = buffer[0];
+ if (size == 2)
+ in_buffer[in_buffer_count++] = buffer[1];
+ NS_ASSERTION(in_buffer_count < 3, "Unexpected out buffer size");
+ return NS_OK;
+ }
+
+
+ // If there are bytes that were put back last time, take them now.
+ uint32_t i = in_buffer_count, bits = 0;
+ if (in_buffer_count > 0) bits = in_buffer[0];
+ if (in_buffer_count > 1) bits = (bits << 8) + in_buffer[1];
+ in_buffer_count = 0;
+
+ // If this buffer is not a multiple of three, put one or two bytes back.
+ uint32_t excess = ((size + i) % 3);
+ if (excess)
+ {
+ in_buffer[0] = buffer[size - excess];
+ if (excess > 1)
+ in_buffer [1] = buffer[size - excess + 1];
+ in_buffer_count = excess;
+ size -= excess;
+ NS_ASSERTION (! ((size + i) % 3), "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ }
+
+ const uint8_t *in = (const uint8_t *)buffer;
+ const uint8_t *end = (const uint8_t *)(buffer + size);
+ MOZ_ASSERT((end - in + i) % 3 == 0, "Need a multiple of 3 bytes to decode");
+
+ // Populate the out_buffer with base64 data, one line at a time.
+ char out_buffer[80]; // Max line length will be 80, so this is safe.
+ RangedPtr<char> out(out_buffer);
+ while (in < end)
+ {
+ // Accumulate the input bits.
+ while (i < 3)
+ {
+ bits = (bits << 8) | *in++;
+ i++;
+ }
+ i = 0;
+
+ Base64EncodeBits(out, bits);
+
+ mCurrentColumn += 4;
+ if (mCurrentColumn >= 72)
+ {
+ // Do a linebreak before column 76. Flush out the line buffer.
+ mCurrentColumn = 0;
+ *out++ = '\x0D';
+ *out++ = '\x0A';
+ nsresult rv = mCallback(out_buffer, (out.get() - out_buffer), mClosure);
+ NS_ENSURE_SUCCESS(rv, rv);
+ out = out_buffer;
+ }
+ }
+
+ // Write out the unwritten portion of the last line buffer.
+ if (out.get() > out_buffer)
+ {
+ nsresult rv = mCallback(out_buffer, out.get() - out_buffer, mClosure);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsresult Base64Encoder::Flush()
+{
+ if (in_buffer_count == 0)
+ return NS_OK;
+
+ // Since we need to some buffering to get a multiple of three bytes on each
+ // block, there may be a few bytes left in the buffer after the last block has
+ // been written. We need to flush those out now.
+ char buf[4];
+ RangedPtr<char> out(buf);
+ uint32_t bits = ((uint32_t)in_buffer[0]) << 16;
+ if (in_buffer_count > 1)
+ bits |= (((uint32_t)in_buffer[1]) << 8);
+
+ Base64EncodeBits(out, bits);
+
+ // Pad with equal-signs.
+ if (in_buffer_count == 1)
+ buf[2] = '=';
+ buf[3] = '=';
+
+ return mCallback(buf, 4, mClosure);
+}
+
+void Base64Encoder::Base64EncodeBits(RangedPtr<char> &out, uint32_t bits)
+{
+ // Convert 3 bytes to 4 base64 bytes
+ for (int32_t j = 18; j >= 0; j -= 6)
+ {
+ unsigned int k = (bits >> j) & 0x3F;
+ if (k < 26) *out++ = k + 'A';
+ else if (k < 52) *out++ = k - 26 + 'a';
+ else if (k < 62) *out++ = k - 52 + '0';
+ else if (k == 62) *out++ = '+';
+ else if (k == 63) *out++ = '/';
+ else MOZ_CRASH("6 bits should only be between 0 and 64");
+ }
+}
+
+class QPEncoder : public MimeEncoder {
+public:
+ QPEncoder(OutputCallback callback, void *closure)
+ : MimeEncoder(callback, closure) {}
+ virtual ~QPEncoder() {}
+
+ virtual nsresult Write(const char *buffer, int32_t size) override;
+};
+
+nsresult QPEncoder::Write(const char *buffer, int32_t size)
+{
+ nsresult rv = NS_OK;
+ static const char *hexdigits = "0123456789ABCDEF";
+ char out_buffer[80];
+ RangedPtr<char> out(out_buffer);
+ bool white = false;
+
+ // Populate the out_buffer with quoted-printable data, one line at a time.
+ const uint8_t *in = (uint8_t *)buffer;
+ const uint8_t *end = in + size;
+ for (; in < end; in++)
+ {
+ if (*in == '\r' || *in == '\n')
+ {
+ // If it's CRLF, swallow two chars instead of one.
+ if (in + 1 < end && in[0] == '\r' && in[1] == '\n')
+ in++;
+
+ // Whitespace cannot be allowed to occur at the end of the line, so we
+ // back up and replace the whitespace with its code.
+ if (white)
+ {
+ out--;
+ char whitespace_char = *out;
+ *out++ = '=';
+ *out++ = hexdigits[whitespace_char >> 4];
+ *out++ = hexdigits[whitespace_char & 0xF];
+ }
+
+ // Now write out the newline.
+ *out++ = '\r';
+ *out++ = '\n';
+ white = false;
+
+ rv = mCallback(out_buffer, out.get() - out_buffer, mClosure);
+ NS_ENSURE_SUCCESS(rv, rv);
+ out = out_buffer;
+ mCurrentColumn = 0;
+ }
+ else if (mCurrentColumn == 0 && *in == '.')
+ {
+ // Just to be SMTP-safe, if "." appears in column 0, encode it.
+ goto HEX;
+ }
+ else if (mCurrentColumn == 0 && *in == 'F'
+ && (in >= end-1 || in[1] == 'r')
+ && (in >= end-2 || in[2] == 'o')
+ && (in >= end-3 || in[3] == 'm')
+ && (in >= end-4 || in[4] == ' '))
+ {
+ // If this line begins with "From " (or it could but we don't have enough
+ // data in the buffer to be certain), encode the 'F' in hex to avoid
+ // potential problems with BSD mailbox formats.
+ goto HEX;
+ }
+ else if ((*in >= 33 && *in <= 60) |
+ (*in >= 62 && *in <= 126)) // Printable characters except for '='
+ {
+ white = false;
+ *out++ = *in;
+ mCurrentColumn++;
+ }
+ else if (*in == ' ' || *in == '\t') // Whitespace
+ {
+ white = true;
+ *out++ = *in;
+ mCurrentColumn++;
+ }
+ else
+ {
+ // Encode the characters here
+HEX:
+ white = false;
+ *out++ = '=';
+ *out++ = hexdigits[*in >> 4];
+ *out++ = hexdigits[*in & 0xF];
+ mCurrentColumn += 3;
+ }
+
+ MOZ_ASSERT(mCurrentColumn <= 76, "Why haven't we added a line break yet?");
+
+ if (mCurrentColumn >= 73) // Soft line break for readability
+ {
+ *out++ = '=';
+ *out++ = '\r';
+ *out++ = '\n';
+
+ rv = mCallback(out_buffer, out.get() - out_buffer, mClosure);
+ NS_ENSURE_SUCCESS(rv, rv);
+ out = out_buffer;
+ white = false;
+ mCurrentColumn = 0;
+ }
+ }
+
+ // Write out the unwritten portion of the last line buffer.
+ if (out.get() != out_buffer)
+ {
+ rv = mCallback(out_buffer, out.get() - out_buffer, mClosure);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+MimeEncoder *MimeEncoder::GetBase64Encoder(OutputCallback callback,
+ void *closure)
+{
+ return new Base64Encoder(callback, closure);
+}
+
+MimeEncoder *MimeEncoder::GetQPEncoder(OutputCallback callback, void *closure)
+{
+ return new QPEncoder(callback, closure);
+}
+
+} // namespace mailnews
+} // namespace mozilla