diff options
Diffstat (limited to 'mailnews/mime/src/mimeParser.jsm')
-rw-r--r-- | mailnews/mime/src/mimeParser.jsm | 258 |
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 000000000..904675f10 --- /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"; + } + }, +}; |