summaryrefslogtreecommitdiff
path: root/mailnews/mime/src/mimeParser.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/mime/src/mimeParser.jsm')
-rw-r--r--mailnews/mime/src/mimeParser.jsm258
1 files changed, 258 insertions, 0 deletions
diff --git a/mailnews/mime/src/mimeParser.jsm b/mailnews/mime/src/mimeParser.jsm
new file mode 100644
index 0000000000..904675f103
--- /dev/null
+++ b/mailnews/mime/src/mimeParser.jsm
@@ -0,0 +1,258 @@
+/* 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/. */
+// vim:set ts=2 sw=2 sts=2 et ft=javascript:
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource:///modules/jsmime.jsm");
+
+var EXPORTED_SYMBOLS = ["MimeParser"];
+
+// Emitter helpers, for internal functions later on.
+var ExtractHeadersEmitter = {
+ startPart: function (partNum, headers) {
+ if (partNum == '') {
+ this.headers = headers;
+ }
+ }
+};
+
+var ExtractHeadersAndBodyEmitter = {
+ body: '',
+ startPart: ExtractHeadersEmitter.startPart,
+ deliverPartData: function (partNum, data) {
+ if (partNum == '')
+ this.body += data;
+ }
+};
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
+/// Sets appropriate default options for chrome-privileged environments
+function setDefaultParserOptions(opts) {
+ if (!("onerror" in opts)) {
+ opts.onerror = Components.utils.reportError;
+ }
+}
+
+var MimeParser = {
+ /**
+ * Triggers an asynchronous parse of the given input.
+ *
+ * The input is an input stream; the stream will be read until EOF and then
+ * closed upon completion. Both blocking and nonblocking streams are
+ * supported by this implementation, but it is still guaranteed that the first
+ * callback will not happen before this method returns.
+ *
+ * @param input An input stream of text to parse.
+ * @param emitter The emitter to receive callbacks on.
+ * @param opts A set of options for the parser.
+ */
+ parseAsync: function MimeParser_parseAsync(input, emitter, opts) {
+ // Normalize the input into an input stream.
+ if (!(input instanceof Ci.nsIInputStream)) {
+ throw new Error("input is not a recognizable type!");
+ }
+
+ // We need a pump for the listener
+ var pump = Cc["@mozilla.org/network/input-stream-pump;1"]
+ .createInstance(Ci.nsIInputStreamPump);
+ pump.init(input, -1, -1, 0, 0, true);
+
+ // Make a stream listener with the given emitter and use it to read from
+ // the pump.
+ var parserListener = MimeParser.makeStreamListenerParser(emitter, opts);
+ pump.asyncRead(parserListener, null);
+ },
+
+ /**
+ * Triggers an synchronous parse of the given input.
+ *
+ * The input is a string that is immediately parsed, calling all functions on
+ * the emitter before this function returns.
+ *
+ * @param input A string or input stream of text to parse.
+ * @param emitter The emitter to receive callbacks on.
+ * @param opts A set of options for the parser.
+ */
+ parseSync: function MimeParser_parseSync(input, emitter, opts) {
+ // We only support string parsing if we are trying to do this parse
+ // synchronously.
+ if (typeof input != "string") {
+ throw new Error("input is not a recognizable type!");
+ }
+ setDefaultParserOptions(opts);
+ var parser = new jsmime.MimeParser(emitter, opts);
+ parser.deliverData(input);
+ parser.deliverEOF();
+ return;
+ },
+
+ /**
+ * Returns a stream listener that feeds data into a parser.
+ *
+ * In addition to the functions on the emitter that the parser may use, the
+ * generated stream listener will also make calls to onStartRequest and
+ * onStopRequest on the emitter (if they exist).
+ *
+ * @param emitter The emitter to receive callbacks on.
+ * @param opts A set of options for the parser.
+ */
+ makeStreamListenerParser: function MimeParser_makeSLParser(emitter, opts) {
+ var StreamListener = {
+ onStartRequest: function SLP_onStartRequest(aRequest, aContext) {
+ try {
+ if ("onStartRequest" in emitter)
+ emitter.onStartRequest(aRequest, aContext);
+ } finally {
+ this._parser.resetParser();
+ }
+ },
+ onStopRequest: function SLP_onStopRequest(aRequest, aContext, aStatus) {
+ this._parser.deliverEOF();
+ if ("onStopRequest" in emitter)
+ emitter.onStopRequest(aRequest, aContext, aStatus);
+ },
+ onDataAvailable: function SLP_onData(aRequest, aContext, aStream,
+ aOffset, aCount) {
+ var scriptIn = Cc["@mozilla.org/scriptableinputstream;1"]
+ .createInstance(Ci.nsIScriptableInputStream);
+ scriptIn.init(aStream);
+ // Use readBytes instead of read to handle embedded NULs properly.
+ this._parser.deliverData(scriptIn.readBytes(aCount));
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener,
+ Ci.nsIRequestObserver])
+ };
+ setDefaultParserOptions(opts);
+ StreamListener._parser = new jsmime.MimeParser(emitter, opts);
+ return StreamListener;
+ },
+
+ /**
+ * Returns a new raw MIME parser.
+ *
+ * Prefer one of the other methods where possible, since the input here must
+ * be driven manually.
+ *
+ * @param emitter The emitter to receive callbacks on.
+ * @param opts A set of options for the parser.
+ */
+ makeParser: function MimeParser_makeParser(emitter, opts) {
+ setDefaultParserOptions(opts);
+ return new jsmime.MimeParser(emitter, opts);
+ },
+
+ /**
+ * Returns a dictionary of headers for the given input.
+ *
+ * The input is any type of input that would be accepted by parseSync. What
+ * is returned is a JS object that represents the headers of the entire
+ * envelope as would be received by startPart when partNum is the empty
+ * string.
+ *
+ * @param input A string of text to parse.
+ */
+ extractHeaders: function MimeParser_extractHeaders(input) {
+ var emitter = Object.create(ExtractHeadersEmitter);
+ MimeParser.parseSync(input, emitter, {pruneat: '', bodyformat: 'none'});
+ return emitter.headers;
+ },
+
+ /**
+ * Returns the headers and body for the given input message.
+ *
+ * The return value is an array whose first element is the dictionary of
+ * headers (as would be returned by extractHeaders) and whose second element
+ * is a binary string of the entire body of the message.
+ *
+ * @param input A string of text to parse.
+ */
+ extractHeadersAndBody: function MimeParser_extractHeaders(input) {
+ var emitter = Object.create(ExtractHeadersAndBodyEmitter);
+ MimeParser.parseSync(input, emitter, {pruneat: '', bodyformat: 'raw'});
+ return [emitter.headers, emitter.body];
+ },
+
+ // Parameters for parseHeaderField
+
+ /**
+ * Parse the header as if it were unstructured.
+ *
+ * This results in the same string if no other options are specified. If other
+ * options are specified, this causes the string to be modified appropriately.
+ */
+ HEADER_UNSTRUCTURED: 0x00,
+ /**
+ * Parse the header as if it were in the form text; attr=val; attr=val.
+ *
+ * Such headers include Content-Type, Content-Disposition, and most other
+ * headers used by MIME as opposed to messages.
+ */
+ HEADER_PARAMETER: 0x02,
+ /**
+ * Parse the header as if it were a sequence of mailboxes.
+ */
+ HEADER_ADDRESS: 0x03,
+
+ /**
+ * This decodes parameter values according to RFC 2231.
+ *
+ * This flag means nothing if HEADER_PARAMETER is not specified.
+ */
+ HEADER_OPTION_DECODE_2231: 0x10,
+ /**
+ * This decodes the inline encoded-words that are in RFC 2047.
+ */
+ HEADER_OPTION_DECODE_2047: 0x20,
+ /**
+ * This converts the header from a raw string to proper Unicode.
+ */
+ HEADER_OPTION_ALLOW_RAW: 0x40,
+
+ /// Convenience for all three of the above.
+ HEADER_OPTION_ALL_I18N: 0x70,
+
+ /**
+ * Parse a header field according to the specification given by flags.
+ *
+ * Permissible flags begin with one of the HEADER_* flags, which may be or'd
+ * with any of the HEADER_OPTION_* flags to modify the result appropriately.
+ *
+ * If the option HEADER_OPTION_ALLOW_RAW is passed, the charset parameter, if
+ * present, is the charset to fallback to if the header is not decodable as
+ * UTF-8 text. If HEADER_OPTION_ALLOW_RAW is passed but the charset parameter
+ * is not provided, then no fallback decoding will be done. If
+ * HEADER_OPTION_ALLOW_RAW is not passed, then no attempt will be made to
+ * convert charsets.
+ *
+ * @param text The value of a MIME or message header to parse.
+ * @param flags A set of flags that controls interpretation of the header.
+ * @param charset A default charset to assume if no information may be found.
+ */
+ parseHeaderField: function MimeParser_parseHeaderField(text, flags, charset) {
+ // If we have a raw string, convert it to Unicode first
+ if (flags & MimeParser.HEADER_OPTION_ALLOW_RAW)
+ text = jsmime.headerparser.convert8BitHeader(text, charset);
+
+ // The low 4 bits indicate the type of the header we are parsing. All of the
+ // higher-order bits are flags.
+ switch (flags & 0x0f) {
+ case MimeParser.HEADER_UNSTRUCTURED:
+ if (flags & MimeParser.HEADER_OPTION_DECODE_2047)
+ text = jsmime.headerparser.decodeRFC2047Words(text);
+ return text;
+ case MimeParser.HEADER_PARAMETER:
+ return jsmime.headerparser.parseParameterHeader(text,
+ (flags & MimeParser.HEADER_OPTION_DECODE_2047) != 0,
+ (flags & MimeParser.HEADER_OPTION_DECODE_2231) != 0);
+ case MimeParser.HEADER_ADDRESS:
+ return jsmime.headerparser.parseAddressingHeader(text,
+ (flags & MimeParser.HEADER_OPTION_DECODE_2047) != 0);
+ default:
+ throw "Illegal type of header field";
+ }
+ },
+};