mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-27 10:57:34 +09:00
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
This commit is contained in:
parent
23e0d82436
commit
e400f4130a
1564 changed files with 510348 additions and 0 deletions
719
mailnews/db/gloda/modules/mimemsg.js
Normal file
719
mailnews/db/gloda/modules/mimemsg.js
Normal file
|
|
@ -0,0 +1,719 @@
|
|||
/* 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/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['MsgHdrToMimeMessage',
|
||||
'MimeMessage', 'MimeContainer',
|
||||
'MimeBody', 'MimeUnknown',
|
||||
'MimeMessageAttachment'];
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cr = Components.results;
|
||||
var Cu = Components.utils;
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
var EMITTER_MIME_CODE = "application/x-js-mime-message";
|
||||
|
||||
/**
|
||||
* The URL listener is surplus because the CallbackStreamListener ends up
|
||||
* getting the same set of events, effectively.
|
||||
*/
|
||||
var dumbUrlListener = {
|
||||
OnStartRunningUrl: function (aUrl) {
|
||||
},
|
||||
OnStopRunningUrl: function (aUrl, aExitCode) {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Maintain a list of all active stream listeners so that we can cancel them all
|
||||
* during shutdown. If we don't cancel them, we risk calls into javascript
|
||||
* from C++ after the various XPConnect contexts have already begun their
|
||||
* teardown process.
|
||||
*/
|
||||
var activeStreamListeners = {};
|
||||
|
||||
var shutdownCleanupObserver = {
|
||||
_initialized: false,
|
||||
ensureInitialized: function mimemsg_shutdownCleanupObserver_init() {
|
||||
if (this._initialized)
|
||||
return;
|
||||
|
||||
Services.obs.addObserver(this, "quit-application", false);
|
||||
|
||||
this._initialized = true;
|
||||
},
|
||||
|
||||
observe: function mimemsg_shutdownCleanupObserver_observe(
|
||||
aSubject, aTopic, aData) {
|
||||
if (aTopic == "quit-application") {
|
||||
Services.obs.removeObserver(this, "quit-application");
|
||||
|
||||
for (let uri in activeStreamListeners) {
|
||||
let streamListener = activeStreamListeners[uri];
|
||||
if (streamListener._request)
|
||||
streamListener._request.cancel(Cr.NS_BINDING_ABORTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function CallbackStreamListener(aMsgHdr, aCallbackThis, aCallback) {
|
||||
this._msgHdr = aMsgHdr;
|
||||
let hdrURI = aMsgHdr.folder.getUriForMsg(aMsgHdr);
|
||||
this._request = null;
|
||||
this._stream = null;
|
||||
if (aCallback === undefined) {
|
||||
this._callbacksThis = [null];
|
||||
this._callbacks = [aCallbackThis];
|
||||
}
|
||||
else {
|
||||
this._callbacksThis = [aCallbackThis];
|
||||
this._callbacks =[aCallback];
|
||||
}
|
||||
activeStreamListeners[hdrURI] = this;
|
||||
}
|
||||
|
||||
CallbackStreamListener.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener]),
|
||||
|
||||
// nsIRequestObserver part
|
||||
onStartRequest: function (aRequest, aContext) {
|
||||
this._request = aRequest;
|
||||
},
|
||||
onStopRequest: function (aRequest, aContext, aStatusCode) {
|
||||
let msgURI = this._msgHdr.folder.getUriForMsg(this._msgHdr);
|
||||
delete activeStreamListeners[msgURI];
|
||||
|
||||
aContext.QueryInterface(Ci.nsIURI);
|
||||
let message = MsgHdrToMimeMessage.RESULT_RENDEVOUZ[aContext.spec];
|
||||
if (message === undefined)
|
||||
message = null;
|
||||
|
||||
delete MsgHdrToMimeMessage.RESULT_RENDEVOUZ[aContext.spec];
|
||||
|
||||
for (let i = 0; i < this._callbacksThis.length; i++) {
|
||||
try {
|
||||
this._callbacks[i].call(this._callbacksThis[i], this._msgHdr, message);
|
||||
} catch (e) {
|
||||
// Most of the time, exceptions will silently disappear into the endless
|
||||
// deeps of XPConnect, and never reach the surface ever again. At least
|
||||
// warn the user if he has dump enabled.
|
||||
dump("The MsgHdrToMimeMessage callback threw an exception: "+e+"\n");
|
||||
// That one will probably never make it to the original caller.
|
||||
throw(e);
|
||||
}
|
||||
}
|
||||
|
||||
this._msgHdr = null;
|
||||
this._request = null;
|
||||
this._stream = null;
|
||||
this._callbacksThis = null;
|
||||
this._callbacks = null;
|
||||
},
|
||||
|
||||
/* okay, our onDataAvailable should actually never be called. the stream
|
||||
converter is actually eating everything except the start and stop
|
||||
notification. */
|
||||
// nsIStreamListener part
|
||||
onDataAvailable: function (aRequest,aContext,aInputStream,aOffset,aCount) {
|
||||
dump("this should not be happening! arrgggggh!\n");
|
||||
if (this._stream === null) {
|
||||
this._stream = Cc["@mozilla.org/scriptableinputstream;1"].
|
||||
createInstance(Ci.nsIScriptableInputStream);
|
||||
this._stream.init(aInputStream);
|
||||
}
|
||||
this._stream.read(aCount);
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
var gMessenger = Cc["@mozilla.org/messenger;1"].
|
||||
createInstance(Ci.nsIMessenger);
|
||||
|
||||
function stripEncryptedParts(aPart) {
|
||||
if (aPart.parts && aPart.isEncrypted) {
|
||||
aPart.parts = []; // Show an empty container.
|
||||
} else if (aPart.parts) {
|
||||
aPart.parts = aPart.parts.map(stripEncryptedParts);
|
||||
}
|
||||
return aPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts retrieval of a MimeMessage instance for the given message header.
|
||||
* Your callback will be called with the message header you provide and the
|
||||
*
|
||||
* @param aMsgHdr The message header to retrieve the body for and build a MIME
|
||||
* representation of the message.
|
||||
* @param aCallbackThis The (optional) 'this' to use for your callback function.
|
||||
* @param aCallback The callback function to invoke on completion of message
|
||||
* parsing or failure. The first argument passed will be the nsIMsgDBHdr
|
||||
* you passed to this function. The second argument will be the MimeMessage
|
||||
* instance resulting from the processing on success, and null on failure.
|
||||
* @param [aAllowDownload=false] Should we allow the message to be downloaded
|
||||
* for this streaming request? The default is false, which means that we
|
||||
* require that the message be available offline. If false is passed and
|
||||
* the message is not available offline, we will propagate an exception
|
||||
* thrown by the underlying code.
|
||||
* @param [aOptions] Optional options.
|
||||
* @param [aOptions.saneBodySize] Limit body sizes to a 'reasonable' size in
|
||||
* order to combat corrupt offline/message stores creating pathological
|
||||
* situtations where we have erroneously multi-megabyte messages. This
|
||||
* also likely reduces the impact of legitimately ridiculously large
|
||||
* messages.
|
||||
* @param [aOptions.partsOnDemand] If this is a message stored on an IMAP
|
||||
* server, and for whatever reason, it isn't available locally, then setting
|
||||
* this option to true will make sure that attachments aren't downloaded.
|
||||
* This makes sure the message is available quickly.
|
||||
* @param [aOptions.examineEncryptedParts] By default, we won't reveal the
|
||||
* contents of multipart/encrypted parts to the consumers, unless explicitly
|
||||
* requested. In the case of MIME/PGP messages, for instance, the message
|
||||
* will appear as an empty multipart/encrypted container, unless this option
|
||||
* is used.
|
||||
*/
|
||||
function MsgHdrToMimeMessage(aMsgHdr, aCallbackThis, aCallback,
|
||||
aAllowDownload, aOptions) {
|
||||
shutdownCleanupObserver.ensureInitialized();
|
||||
|
||||
let requireOffline = !aAllowDownload;
|
||||
|
||||
let msgURI = aMsgHdr.folder.getUriForMsg(aMsgHdr);
|
||||
let msgService = gMessenger.messageServiceFromURI(msgURI);
|
||||
|
||||
MsgHdrToMimeMessage.OPTION_TUNNEL = aOptions;
|
||||
let partsOnDemandStr = (aOptions && aOptions.partsOnDemand)
|
||||
? "&fetchCompleteMessage=false"
|
||||
: "";
|
||||
// By default, Enigmail only decrypts a message streamed via libmime if it's
|
||||
// the one currently on display in the message reader. With this option, we're
|
||||
// letting Enigmail know that it should decrypt the message since the client
|
||||
// explicitly asked for it.
|
||||
let encryptedStr = (aOptions && aOptions.examineEncryptedParts)
|
||||
? "&examineEncryptedParts=true"
|
||||
: "";
|
||||
|
||||
// S/MIME, our other encryption backend, is not that smart, and always
|
||||
// decrypts data. In order to protect sensitive data (e.g. not index it in
|
||||
// Gloda), unless the client asked for encrypted data, we pass to the client
|
||||
// callback a stripped-down version of the MIME structure where encrypted
|
||||
// parts have been removed.
|
||||
let wrapCallback = function (aCallback, aCallbackThis) {
|
||||
if (aOptions && aOptions.examineEncryptedParts)
|
||||
return aCallback;
|
||||
else
|
||||
return ((aMsgHdr, aMimeMsg) =>
|
||||
aCallback.call(aCallbackThis, aMsgHdr, stripEncryptedParts(aMimeMsg))
|
||||
);
|
||||
};
|
||||
|
||||
// Apparently there used to be an old syntax where the callback was the second
|
||||
// argument...
|
||||
let callback = aCallback ? aCallback : aCallbackThis;
|
||||
let callbackThis = aCallback ? aCallbackThis : null;
|
||||
|
||||
// if we're already streaming this msg, just add the callback
|
||||
// to the listener.
|
||||
let listenerForURI = activeStreamListeners[msgURI];
|
||||
if (listenerForURI != undefined) {
|
||||
listenerForURI._callbacks.push(wrapCallback(callback, callbackThis));
|
||||
listenerForURI._callbacksThis.push(callbackThis);
|
||||
return;
|
||||
}
|
||||
let streamListener = new CallbackStreamListener(
|
||||
aMsgHdr,
|
||||
callbackThis,
|
||||
wrapCallback(callback, callbackThis)
|
||||
);
|
||||
|
||||
try {
|
||||
let streamURI = msgService.streamMessage(
|
||||
msgURI,
|
||||
streamListener, // consumer
|
||||
null, // nsIMsgWindow
|
||||
dumbUrlListener, // nsIUrlListener
|
||||
true, // have them create the converter
|
||||
// additional uri payload, note that "header=" is prepended automatically
|
||||
"filter&emitter=js"+partsOnDemandStr+encryptedStr,
|
||||
requireOffline);
|
||||
} catch (ex) {
|
||||
// If streamMessage throws an exception, we should make sure to clear the
|
||||
// activeStreamListener, or any subsequent attempt at sreaming this URI
|
||||
// will silently fail
|
||||
if (activeStreamListeners[msgURI]) {
|
||||
delete activeStreamListeners[msgURI];
|
||||
}
|
||||
MsgHdrToMimeMessage.OPTION_TUNNEL = null;
|
||||
throw(ex);
|
||||
}
|
||||
|
||||
MsgHdrToMimeMessage.OPTION_TUNNEL = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Let the jsmimeemitter provide us with results. The poor emitter (if I am
|
||||
* understanding things correctly) is evaluated outside of the C.u.import
|
||||
* world, so if we were to import him, we would not see him, but rather a new
|
||||
* copy of him. This goes for his globals, etc. (and is why we live in this
|
||||
* file right here). Also, it appears that the XPCOM JS wrappers aren't
|
||||
* magically unified so that we can try and pass data as expando properties
|
||||
* on things like the nsIUri instances either. So we have the jsmimeemitter
|
||||
* import us and poke things into RESULT_RENDEVOUZ. We put it here on this
|
||||
* function to try and be stealthy and avoid polluting the namespaces (or
|
||||
* encouraging bad behaviour) of our importers.
|
||||
*
|
||||
* If you can come up with a prettier way to shuttle this data, please do.
|
||||
*/
|
||||
MsgHdrToMimeMessage.RESULT_RENDEVOUZ = {};
|
||||
/**
|
||||
* Cram rich options here for the MimeMessageEmitter to grab from. We
|
||||
* leverage the known control-flow to avoid needing a whole dictionary here.
|
||||
* We set this immediately before constructing the emitter and clear it
|
||||
* afterwards. Control flow is never yielded during the process and reentrancy
|
||||
* cannot happen via any other means.
|
||||
*/
|
||||
MsgHdrToMimeMessage.OPTION_TUNNEL = null;
|
||||
|
||||
var HeaderHandlerBase = {
|
||||
/**
|
||||
* Look-up a header that should be present at most once.
|
||||
*
|
||||
* @param aHeaderName The header name to retrieve, case does not matter.
|
||||
* @param aDefaultValue The value to return if the header was not found, null
|
||||
* if left unspecified.
|
||||
* @return the value of the header if present, and the default value if not
|
||||
* (defaults to null). If the header was present multiple times, the first
|
||||
* instance of the header is returned. Use getAll if you want all of the
|
||||
* values for the multiply-defined header.
|
||||
*/
|
||||
get: function MimeMessage_get(aHeaderName, aDefaultValue) {
|
||||
if (aDefaultValue === undefined) {
|
||||
aDefaultValue = null;
|
||||
}
|
||||
let lowerHeader = aHeaderName.toLowerCase();
|
||||
if (lowerHeader in this.headers)
|
||||
// we require that the list cannot be empty if present
|
||||
return this.headers[lowerHeader][0];
|
||||
else
|
||||
return aDefaultValue;
|
||||
},
|
||||
/**
|
||||
* Look-up a header that can be present multiple times. Use get for headers
|
||||
* that you only expect to be present at most once.
|
||||
*
|
||||
* @param aHeaderName The header name to retrieve, case does not matter.
|
||||
* @return An array containing the values observed, which may mean a zero
|
||||
* length array.
|
||||
*/
|
||||
getAll: function MimeMessage_getAll(aHeaderName) {
|
||||
let lowerHeader = aHeaderName.toLowerCase();
|
||||
if (lowerHeader in this.headers)
|
||||
return this.headers[lowerHeader];
|
||||
else
|
||||
return [];
|
||||
},
|
||||
/**
|
||||
* @param aHeaderName Header name to test for its presence.
|
||||
* @return true if the message has (at least one value for) the given header
|
||||
* name.
|
||||
*/
|
||||
has: function MimeMessage_has(aHeaderName) {
|
||||
let lowerHeader = aHeaderName.toLowerCase();
|
||||
return lowerHeader in this.headers;
|
||||
},
|
||||
_prettyHeaderString: function MimeMessage__prettyHeaderString(aIndent) {
|
||||
if (aIndent === undefined)
|
||||
aIndent = "";
|
||||
let s = "";
|
||||
for (let header in this.headers) {
|
||||
let values = this.headers[header];
|
||||
s += "\n " + aIndent + header + ": " + values;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ivar partName The MIME part, ex "1.2.2.1". The partName of a (top-level)
|
||||
* message is "1", its first child is "1.1", its second child is "1.2",
|
||||
* its first child's first child is "1.1.1", etc.
|
||||
* @ivar headers Maps lower-cased header field names to a list of the values
|
||||
* seen for the given header. Use get or getAll as convenience helpers.
|
||||
* @ivar parts The list of the MIME part children of this message. Children
|
||||
* will be either MimeMessage instances, MimeMessageAttachment instances,
|
||||
* MimeContainer instances, or MimeUnknown instances. The latter two are
|
||||
* the result of limitations in the Javascript representation generation
|
||||
* at this time, combined with the need to most accurately represent the
|
||||
* MIME structure.
|
||||
*/
|
||||
function MimeMessage() {
|
||||
this.partName = null;
|
||||
this.headers = {};
|
||||
this.parts = [];
|
||||
this.isEncrypted = false;
|
||||
}
|
||||
|
||||
MimeMessage.prototype = {
|
||||
__proto__: HeaderHandlerBase,
|
||||
contentType: "message/rfc822",
|
||||
|
||||
/**
|
||||
* @return a list of all attachments contained in this message and all its
|
||||
* sub-messages. Only MimeMessageAttachment instances will be present in
|
||||
* the list (no sub-messages).
|
||||
*/
|
||||
get allAttachments() {
|
||||
let results = []; // messages are not attachments, don't include self
|
||||
for (let iChild = 0; iChild < this.parts.length; iChild++) {
|
||||
let child = this.parts[iChild];
|
||||
results = results.concat(child.allAttachments);
|
||||
}
|
||||
return results;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return a list of all attachments contained in this message, with
|
||||
* included/forwarded messages treated as real attachments. Attachments
|
||||
* contained in inner messages won't be shown.
|
||||
*/
|
||||
get allUserAttachments() {
|
||||
if (this.url)
|
||||
// The jsmimeemitter camouflaged us as a MimeAttachment
|
||||
return [this];
|
||||
else
|
||||
// Why is there no flatten method for arrays?
|
||||
return this.parts.map(child => child.allUserAttachments)
|
||||
.reduce((a, b) => a.concat(b), []);
|
||||
},
|
||||
|
||||
/**
|
||||
* @return the total size of this message, that is, the size of all subparts
|
||||
*/
|
||||
get size () {
|
||||
return this.parts.map(child => child.size)
|
||||
.reduce((a, b) => a + Math.max(b, 0), 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* In the case of attached messages, libmime considers them as attachments,
|
||||
* and if the body is, say, quoted-printable encoded, then libmime will start
|
||||
* counting bytes and notify the js mime emitter about it. The JS mime emitter
|
||||
* being a nice guy, it will try to set a size on us. While this is the
|
||||
* expected behavior for MimeMsgAttachments, we must make sure we can handle
|
||||
* that (failing to write a setter results in exceptions being thrown).
|
||||
*/
|
||||
set size (whatever) {
|
||||
// nop
|
||||
},
|
||||
|
||||
/**
|
||||
* @param aMsgFolder A message folder, any message folder. Because this is
|
||||
* a hack.
|
||||
* @return The concatenation of all of the body parts where parts
|
||||
* available as text/plain are pulled as-is, and parts only available
|
||||
* as text/html are converted to plaintext form first. In other words,
|
||||
* if we see a multipart/alternative with a text/plain, we take the
|
||||
* text/plain. If we see a text/html without an alternative, we convert
|
||||
* that to text.
|
||||
*/
|
||||
coerceBodyToPlaintext:
|
||||
function MimeMessage_coerceBodyToPlaintext(aMsgFolder) {
|
||||
let bodies = [];
|
||||
for (let part of this.parts) {
|
||||
// an undefined value for something not having the method is fine
|
||||
let body = part.coerceBodyToPlaintext &&
|
||||
part.coerceBodyToPlaintext(aMsgFolder);
|
||||
if (body)
|
||||
bodies.push(body);
|
||||
}
|
||||
if (bodies)
|
||||
return bodies.join("");
|
||||
else
|
||||
return "";
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert the message and its hierarchy into a "pretty string". The message
|
||||
* and each MIME part get their own line. The string never ends with a
|
||||
* newline. For a non-multi-part message, only a single line will be
|
||||
* returned.
|
||||
* Messages have their subject displayed, attachments have their filename and
|
||||
* content-type (ex: image/jpeg) displayed. "Filler" classes simply have
|
||||
* their class displayed.
|
||||
*/
|
||||
prettyString: function MimeMessage_prettyString(aVerbose, aIndent,
|
||||
aDumpBody) {
|
||||
if (aIndent === undefined)
|
||||
aIndent = "";
|
||||
let nextIndent = aIndent + " ";
|
||||
|
||||
let s = "Message "+(this.isEncrypted ? "[encrypted] " : "") +
|
||||
"(" + this.size + " bytes): " +
|
||||
"subject" in this.headers ? this.headers.subject : "";
|
||||
if (aVerbose)
|
||||
s += this._prettyHeaderString(nextIndent);
|
||||
|
||||
for (let iPart = 0; iPart < this.parts.length; iPart++) {
|
||||
let part = this.parts[iPart];
|
||||
s += "\n" + nextIndent + (iPart+1) + " " +
|
||||
part.prettyString(aVerbose, nextIndent, aDumpBody);
|
||||
}
|
||||
|
||||
return s;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ivar contentType The content-type of this container.
|
||||
* @ivar parts The parts held by this container. These can be instances of any
|
||||
* of the classes found in this file.
|
||||
*/
|
||||
function MimeContainer(aContentType) {
|
||||
this.partName = null;
|
||||
this.contentType = aContentType;
|
||||
this.headers = {};
|
||||
this.parts = [];
|
||||
this.isEncrypted = false;
|
||||
}
|
||||
|
||||
MimeContainer.prototype = {
|
||||
__proto__: HeaderHandlerBase,
|
||||
get allAttachments() {
|
||||
let results = [];
|
||||
for (let iChild = 0; iChild < this.parts.length; iChild++) {
|
||||
let child = this.parts[iChild];
|
||||
results = results.concat(child.allAttachments);
|
||||
}
|
||||
return results;
|
||||
},
|
||||
get allUserAttachments () {
|
||||
return this.parts.map(child => child.allUserAttachments)
|
||||
.reduce((a, b) => a.concat(b), []);
|
||||
},
|
||||
get size () {
|
||||
return this.parts.map(child => child.size)
|
||||
.reduce((a, b) => a + Math.max(b, 0), 0);
|
||||
},
|
||||
set size (whatever) {
|
||||
// nop
|
||||
},
|
||||
coerceBodyToPlaintext:
|
||||
function MimeContainer_coerceBodyToPlaintext(aMsgFolder) {
|
||||
if (this.contentType == "multipart/alternative") {
|
||||
let htmlPart;
|
||||
// pick the text/plain if we can find one, otherwise remember the HTML one
|
||||
for (let part of this.parts) {
|
||||
if (part.contentType == "text/plain")
|
||||
return part.body;
|
||||
if (part.contentType == "text/html")
|
||||
htmlPart = part;
|
||||
// text/enriched gets transformed into HTML, use it if we don't already
|
||||
// have an HTML part.
|
||||
else if (!htmlPart && part.contentType == "text/enriched")
|
||||
htmlPart = part;
|
||||
}
|
||||
// convert the HTML part if we have one
|
||||
if (htmlPart)
|
||||
return aMsgFolder.convertMsgSnippetToPlainText(htmlPart.body);
|
||||
}
|
||||
// if it's not alternative, recurse/aggregate using MimeMessage logic
|
||||
return MimeMessage.prototype.coerceBodyToPlaintext.call(this, aMsgFolder);
|
||||
},
|
||||
prettyString: function MimeContainer_prettyString(aVerbose, aIndent,
|
||||
aDumpBody) {
|
||||
let nextIndent = aIndent + " ";
|
||||
|
||||
let s = "Container "+(this.isEncrypted ? "[encrypted] " : "")+
|
||||
"(" + this.size + " bytes): " + this.contentType;
|
||||
if (aVerbose)
|
||||
s += this._prettyHeaderString(nextIndent);
|
||||
|
||||
for (let iPart = 0; iPart < this.parts.length; iPart++) {
|
||||
let part = this.parts[iPart];
|
||||
s += "\n" + nextIndent + (iPart+1) + " " +
|
||||
part.prettyString(aVerbose, nextIndent, aDumpBody);
|
||||
}
|
||||
|
||||
return s;
|
||||
},
|
||||
toString: function MimeContainer_toString() {
|
||||
return "Container: " + this.contentType;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Represents a body portion that we understand and do not believe to be
|
||||
* a proper attachment. This means text/plain or text/html and it has no
|
||||
* filename. (A filename suggests an attachment.)
|
||||
*
|
||||
* @ivar contentType The content type of this body materal; text/plain or
|
||||
* text/html.
|
||||
* @ivar body The actual body content.
|
||||
*/
|
||||
function MimeBody(aContentType) {
|
||||
this.partName = null;
|
||||
this.contentType = aContentType;
|
||||
this.headers = {};
|
||||
this.body = "";
|
||||
this.isEncrypted = false;
|
||||
}
|
||||
|
||||
MimeBody.prototype = {
|
||||
__proto__: HeaderHandlerBase,
|
||||
get allAttachments() {
|
||||
return []; // we are a leaf
|
||||
},
|
||||
get allUserAttachments() {
|
||||
return []; // we are a leaf
|
||||
},
|
||||
get size() {
|
||||
return this.body.length;
|
||||
},
|
||||
set size (whatever) {
|
||||
// nop
|
||||
},
|
||||
appendBody: function MimeBody_append(aBuf) {
|
||||
this.body += aBuf;
|
||||
},
|
||||
coerceBodyToPlaintext:
|
||||
function MimeBody_coerceBodyToPlaintext(aMsgFolder) {
|
||||
if (this.contentType == "text/plain")
|
||||
return this.body;
|
||||
// text/enriched gets transformed into HTML by libmime
|
||||
if (this.contentType == "text/html" ||
|
||||
this.contentType == "text/enriched")
|
||||
return aMsgFolder.convertMsgSnippetToPlainText(this.body);
|
||||
return "";
|
||||
},
|
||||
prettyString: function MimeBody_prettyString(aVerbose, aIndent, aDumpBody) {
|
||||
let s = "Body: "+(this.isEncrypted ? "[encrypted] " : "")+
|
||||
"" + this.contentType + " (" + this.body.length + " bytes" +
|
||||
(aDumpBody ? (": '" + this.body + "'") : "") + ")";
|
||||
if (aVerbose)
|
||||
s += this._prettyHeaderString(aIndent + " ");
|
||||
return s;
|
||||
},
|
||||
toString: function MimeBody_toString() {
|
||||
return "Body: " + this.contentType + " (" + this.body.length + " bytes)";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class A MIME Leaf node that doesn't have a filename so we assume it's not
|
||||
* intended to be an attachment proper. This is probably meant for inline
|
||||
* display or is the result of someone amusing themselves by composing messages
|
||||
* by hand or a bad client. This class should probably be renamed or we should
|
||||
* introduce a better named class that we try and use in preference to this
|
||||
* class.
|
||||
*
|
||||
* @ivar contentType The content type of this part.
|
||||
*/
|
||||
function MimeUnknown(aContentType) {
|
||||
this.partName = null;
|
||||
this.contentType = aContentType;
|
||||
this.headers = {};
|
||||
// Looks like libmime does not always intepret us as an attachment, which
|
||||
// means we'll have to have a default size. Returning undefined would cause
|
||||
// the recursive size computations to fail.
|
||||
this._size = 0;
|
||||
this.isEncrypted = false;
|
||||
// We want to make sure MimeUnknown has a part property: S/MIME encrypted
|
||||
// messages have a topmost MimeUnknown part, with the encrypted bit set to 1,
|
||||
// and we need to ensure all other encrypted parts are children of this
|
||||
// topmost part.
|
||||
this.parts = [];
|
||||
}
|
||||
|
||||
MimeUnknown.prototype = {
|
||||
__proto__: HeaderHandlerBase,
|
||||
get allAttachments() {
|
||||
return this.parts.map(child => child.allAttachments)
|
||||
.reduce((a, b) => a.concat(b), []);
|
||||
},
|
||||
get allUserAttachments() {
|
||||
return this.parts.map(child => child.allUserAttachments)
|
||||
.reduce((a, b) => a.concat(b), []);
|
||||
},
|
||||
get size() {
|
||||
return this._size + this.parts.map(child => child.size)
|
||||
.reduce((a, b) => a + Math.max(b, 0), 0);
|
||||
},
|
||||
set size(aSize) {
|
||||
this._size = aSize;
|
||||
},
|
||||
prettyString: function MimeUnknown_prettyString(aVerbose, aIndent,
|
||||
aDumpBody) {
|
||||
let nextIndent = aIndent + " ";
|
||||
|
||||
let s = "Unknown: "+(this.isEncrypted ? "[encrypted] " : "")+
|
||||
"" + this.contentType + " (" + this.size + " bytes)";
|
||||
if (aVerbose)
|
||||
s += this._prettyHeaderString(aIndent + " ");
|
||||
|
||||
for (let iPart = 0; iPart < this.parts.length; iPart++) {
|
||||
let part = this.parts[iPart];
|
||||
s += "\n" + nextIndent + (iPart+1) + " " +
|
||||
(part ? part.prettyString(aVerbose, nextIndent, aDumpBody) : "NULL");
|
||||
}
|
||||
return s;
|
||||
},
|
||||
toString: function MimeUnknown_toString() {
|
||||
return "Unknown: " + this.contentType;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class An attachment proper. We think it's an attachment because it has a
|
||||
* filename that libmime was able to figure out.
|
||||
*
|
||||
* @ivar partName @see{MimeMessage.partName}
|
||||
* @ivar name The filename of this attachment.
|
||||
* @ivar contentType The MIME content type of this part.
|
||||
* @ivar url The URL to stream if you want the contents of this part.
|
||||
* @ivar isExternal Is the attachment stored someplace else than in the message?
|
||||
* @ivar size The size of the attachment if available, -1 otherwise (size is set
|
||||
* after initialization by jsmimeemitter.js)
|
||||
*/
|
||||
function MimeMessageAttachment(aPartName, aName, aContentType, aUrl,
|
||||
aIsExternal) {
|
||||
this.partName = aPartName;
|
||||
this.name = aName;
|
||||
this.contentType = aContentType;
|
||||
this.url = aUrl;
|
||||
this.isExternal = aIsExternal;
|
||||
this.headers = {};
|
||||
this.isEncrypted = false;
|
||||
// parts is copied over from the part instance that preceded us
|
||||
// headers is copied over from the part instance that preceded us
|
||||
// isEncrypted is copied over from the part instance that preceded us
|
||||
}
|
||||
|
||||
MimeMessageAttachment.prototype = {
|
||||
__proto__: HeaderHandlerBase,
|
||||
// This is a legacy property.
|
||||
get isRealAttachment() {
|
||||
return true;
|
||||
},
|
||||
get allAttachments() {
|
||||
return [this]; // we are a leaf, so just us.
|
||||
},
|
||||
get allUserAttachments() {
|
||||
return [this];
|
||||
},
|
||||
prettyString: function MimeMessageAttachment_prettyString(aVerbose, aIndent,
|
||||
aDumpBody) {
|
||||
let s = "Attachment "+(this.isEncrypted ? "[encrypted] " : "")+
|
||||
"(" + this.size+" bytes): "
|
||||
+ this.name + ", " + this.contentType;
|
||||
if (aVerbose)
|
||||
s += this._prettyHeaderString(aIndent + " ");
|
||||
return s;
|
||||
},
|
||||
toString: function MimeMessageAttachment_toString() {
|
||||
return this.prettyString(false, "");
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue