--- a/mailnews/db/gloda/components/jsmimeemitter.js
+++ b/mailnews/db/gloda/components/jsmimeemitter.js
@@ -375,18 +375,17 @@ MimeMessageEmitter.prototype = {
if (this._parentMsg.partName == "")
this._curPart.partName = "1";
else
this._curPart.partName = this._curMsg.partName + ".1";
this._placePart(this._curPart);
},
writeBody: function mime_emitter_writeBody(aBuf, aSize, aOutAmountWritten) {
- if (this._curBodyPart)
- this._curBodyPart.body += aBuf;
+ this._curBodyPart.body += aBuf;
},
endBody: function mime_emitter_endBody() {
this._messageStack.pop();
this._parentMsg = this._messageStack[this._messageStack.length - 1];
},
// ----- Generic Write (confusing)
--- a/mailnews/db/gloda/modules/indexer.js
+++ b/mailnews/db/gloda/modules/indexer.js
@@ -2328,20 +2328,22 @@ var GlodaIndexer = {
_indexMessage: function gloda_indexMessage(aMsgHdr, aCallbackHandle) {
this._log.debug("*** Indexing message: " + aMsgHdr.messageKey + " : " +
aMsgHdr.subject);
MsgHdrToMimeMessage(aMsgHdr, aCallbackHandle.callbackThis,
aCallbackHandle.callback);
let [,aMimeMsg] = yield this.kWorkAsync;
- if (aMimeMsg)
- this._log.debug(" * Got Mime Message!");
- else
- this._log.debug(" * Did not get body!");
+ if (this._unitTestSuperVerbose) {
+ if (aMimeMsg)
+ this._log.debug(" * Got Mime " + aMimeMsg.prettyString());
+ else
+ this._log.debug(" * NO MIME MESSAGE!!!\n");
+ }
// -- Find/create the conversation the message belongs to.
// Our invariant is that all messages that exist in the database belong to
// a conversation.
// - See if any of the ancestors exist and have a conversationID...
// (references are ordered from old [0] to new [n-1])
let references = [aMsgHdr.getStringReference(i) for each
--- a/mailnews/db/gloda/test/resources/glodaTestHelper.js
+++ b/mailnews/db/gloda/test/resources/glodaTestHelper.js
@@ -215,16 +215,18 @@ function imsInit() {
prefSvc.setBoolPref("mail.biff.show_alert", false);
prefSvc.setBoolPref("mail.biff.show_tray_icon", false);
prefSvc.setBoolPref("mail.biff.animate_dock_icon", false);
Gloda.addIndexerListener(messageIndexerListener.onIndexNotification);
ims.catchAllCollection = Gloda._wildcardCollection(Gloda.NOUN_MESSAGE);
ims.catchAllCollection.listener = messageCollectionListener;
+ // Make the indexer be more verbose about indexing for us...
+ GlodaIndexer._unitTestSuperVerbose = true;
// The indexer doesn't need to worry about load; zero his rescheduling time.
GlodaIndexer._indexInterval = 0;
// And it doesn't need to adjust its performance, either.
GlodaIndexer._PERF_SAMPLE_RATE_MS = 24 * 60 * 60 * 1000;
if (ims.injectMechanism == INJECT_FAKE_SERVER) {
// set up POP3 fakeserver to feed things in...
[ims.daemon, ims.server] = setupServerDaemon();
@@ -808,16 +810,17 @@ QueryExpectationListener.prototype = {
// is, so let's become explicit to avoid related troubles.
aCollection.becomeExplicit();
// expectedSet should now be empty
for each (let [key, value] in this.expectedSet) {
do_throw("Query should have returned " + key + "(" + value + ")");
}
+ dump(">>> queryCompleted, advancing to next test\n");
next_test();
},
}
/**
* Execute the given query, verifying that the result set contains exactly the
* contents of the expected set; no more, no less. Since we expect that the
* query will result in gloda objects, but your expectations will not be posed
@@ -910,18 +913,30 @@ function notifyWhenDatastoreDone(aCallba
var glodaHelperTests = [];
var glodaHelperIterator = null;
function _gh_test_iterator() {
do_test_pending();
for (let iTest=0; iTest < glodaHelperTests.length; iTest++) {
- dump("====== Test function: " + glodaHelperTests[iTest].name + "\n");
- yield glodaHelperTests[iTest]();
+ let test = glodaHelperTests[iTest];
+ // deal with parameterized tests (via parameterizeTest)
+ if (test.length) {
+ let [testFunc, parameters] = test;
+ for each (let [, parameter] in Iterator(parameters)) {
+ dump("====== Test function: " + testFunc.name + " Parameter: " +
+ parameter.name + "\n");
+ yield testFunc(parameter);
+ }
+ }
+ else {
+ dump("====== Test function: " + test.name + "\n");
+ yield test();
+ }
}
if (indexMessageState.injectMechanism == INJECT_FAKE_SERVER) {
killFakeServer();
}
do_test_finished();
@@ -948,16 +963,28 @@ function next_test() {
do_throw("Caught an exception during execution of next_test: " + ex);
}
_next_test_currently_in_test = false;
}
DEFAULT_LONGEST_TEST_RUN_CONCEIVABLE_SECS = 180;
/**
+ * Purely decorative function to help explain to people reading lists of tests
+ * using glodaHelperRunTests what is going on. We just return a tuple of our
+ * arguments and _gh_test_iterator understands what to do with this, namely
+ * to run the test once for each element in the aParameters list. If the
+ * elements in the aParameters list have a 'name' attribute, it will get
+ * printed out to help figure out what is actually happening.
+ */
+function parameterizeTest(aTestFunc, aParameters) {
+ return [aTestFunc, aParameters];
+}
+
+/**
* Test driving logic that takes a list of tests to run. Every completed test
* needs to call (or cause to be called) next_test.
*
* @param aTests A list of test functions to call.
* @param aLongestTestRunTimeConceivableInSecs Optional parameter
*/
function glodaHelperRunTests(aTests, aLongestTestRunTimeConceivableInSecs) {
if (aLongestTestRunTimeConceivableInSecs == null)
--- a/mailnews/db/gloda/test/resources/messageGenerator.js
+++ b/mailnews/db/gloda/test/resources/messageGenerator.js
@@ -1,16 +1,16 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
- *
+ *
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Thunderbird Global Database.
*
* The Initial Developer of the Original Code is
@@ -93,25 +93,130 @@ const SUBJECT_NOUNS = [
* your additions don't break the secret Monty Python reference!
*/
const SUBJECT_SUFFIXES = [
"Today", "Tomorrow", "Yesterday", "In a Fortnight",
"Needs Attention", "Very Important", "Highest Priority", "Full Of Eels",
"In The Lobby", "On Your Desk", "In Your Car", "Hiding Behind The Door",
];
+/**
+ * Base class for MIME Part representation.
+ */
+function SyntheticPart(aProperties) {
+ if (aProperties) {
+ if (aProperties.charset)
+ this._charset = aProperties.charset;
+ if (aProperties.format)
+ this._format = aProperties.format;
+ if (aProperties.filename)
+ this._filename = aProperties.filename;
+ if (aProperties.boundary)
+ this._boundary = aProperties.boundary;
+ }
+}
+SyntheticPart.prototype = {
+ get contentTypeHeaderValue() {
+ s = this._contentType;
+ if (this._charset)
+ s += '; charset=' + this._charset;
+ if (this._format)
+ s += '; format=' + this._format;
+ if (this._filename)
+ s += ';\r\n name="' + this._filename +'"'
+ if (this._boundary)
+ s += ';\r\n boundary="' + this._boundary + '"';
+ return s;
+ },
+ get hasTransferEncoding() {
+ return this._encoding;
+ },
+ get contentTransferEncodingHeaderValue() {
+ return this._encoding;
+ },
+ get hasDisposition() {
+ return this._filename;
+ },
+ get contentDispositionHeaderValue() {
+ s = '';
+ if (this._filename)
+ s += 'attachment;\r\n filename="' + this._filename + '"';
+ return s;
+ },
+};
+
+/**
+ * Leaf MIME part, defaulting to text/plain.
+ */
+function SyntheticPartLeaf(aBody, aProperties) {
+ SyntheticPart.call(this, aProperties);
+ this.body = aBody;
+}
+SyntheticPartLeaf.prototype = {
+ __proto__: SyntheticPart.prototype,
+ _contentType: 'text/plain',
+ _charset: 'ISO-8859-1',
+ _format: 'flowed',
+ _encoding: '7bit',
+ toMessageString: function() {
+ return this.body;
+ }
+}
+
+/**
+ * Multipart (multipart/*) MIME part base class.
+ */
+function SyntheticPartMulti(aParts, aProperties) {
+ SyntheticPart.call(this, aProperties);
+
+ this._boundary = '--------------CHOPCHOP' + this.BOUNDARY_COUNTER;
+ this.__proto__.BOUNDARY_COUNTER += 1;
+ this.parts = (aParts != null) ? aParts : [];
+}
+SyntheticPartMulti.prototype = {
+ __proto__: SyntheticPart.prototype,
+ BOUNDARY_COUNTER: 0,
+ toMessageString: function() {
+ s = "This is a multi-part message in MIME format.\r\n";
+ for (let [,part] in Iterator(this.parts)) {
+ s += "--" + this._boundary + "\r\n";
+ s += "Content-Type: " + part.contentTypeHeaderValue + '\r\n';
+ if (part.hasTransferEncoding)
+ s += 'Content-Transfer-Encoding: ' +
+ part.contentTransferEncodingHeaderValue + '\r\n';
+ if (part.hasDisposition)
+ s += 'Content-Disposition: ' + part.contentDispositionHeaderValue +
+ '\r\n';
+ s += '\r\n';
+ s += part.toMessageString() + '\r\n\r\n';
+ }
+ s += "--" + this._boundary + '--';
+ return s;
+ },
+};
+
+/**
+ * Multipart mixed (multipart/mixed) MIME part.
+ */
+function SyntheticPartMultiMixed() {
+ SyntheticPartMulti.apply(this, arguments);
+}
+SyntheticPartMultiMixed.prototype = {
+ __proto__: SyntheticPartMulti.prototype,
+ _contentType: 'multipart/mixed',
+}
/**
* A synthetic message, created by the MessageGenerator. Captures both the
* ingredients that went into the synthetic message as well as the rfc822 form
* of the message.
*/
-function SyntheticMessage(aHeaders, aBody) {
+function SyntheticMessage(aHeaders, aBodyPart) {
this.headers = aHeaders || {};
- this.body = aBody || "";
+ this.bodyPart = aBodyPart || new SyntheticPartLeaf("");
}
SyntheticMessage.prototype = {
/** @returns the Message-Id header value. */
get messageId() { return this._messageId; },
/**
* Sets the Message-Id header value.
*
@@ -236,31 +341,39 @@ SyntheticMessage.prototype = {
*/
set cc(aNameAndAddresses) {
this._cc = aNameAndAddresses;
this.headers["Cc"] = this._commaize(
[this._formatMailFromNameAndAddress(nameAndAddr)
for each (nameAndAddr in aNameAndAddresses)]);
},
+ get bodyPart() {
+ return this._bodyPart;
+ },
+ set bodyPart(aBodyPart) {
+ this._bodyPart = aBodyPart;
+ this.headers["Content-Type"] = this._bodyPart.contentTypeHeaderValue;
+ },
+
/**
* Normalizes header values, which may be strings or arrays of strings, into
* a suitable string suitable for appending to the header name/key.
*
* @returns a normalized string representation of the header value(s), which
* may include spanning multiple lines.
*/
_formatHeaderValues: function(aHeaderValues) {
// may not be an array
if (!(aHeaderValues instanceof Array))
return aHeaderValues;
// it's an array!
if (aHeaderValues.length == 1)
return aHeaderValues[0];
- return aHeaderValues.join("\n\t");
+ return aHeaderValues.join("\r\n\t");
},
/**
* @returns a string uniquely identifying this message, at least as long as
* the messageId is set and unique.
*/
toString: function() {
return "msg:" + this._messageId;
@@ -268,17 +381,18 @@ SyntheticMessage.prototype = {
/**
* @returns this messages in rfc822 format, or something close enough.
*/
toMessageString: function() {
let lines = [headerKey + ": " + this._formatHeaderValues(headerValues)
for each ([headerKey, headerValues] in Iterator(this.headers))];
- return lines.join("\n") + "\n\n" + this.body + "\n";
+ return lines.join("\r\n") + "\r\n\r\n" + this.bodyPart.toMessageString() +
+ "\r\n";
},
/**
* @returns this message in rfc822 format in a string stream.
*/
toStream: function () {
let stream = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
@@ -287,17 +401,18 @@ SyntheticMessage.prototype = {
return stream;
},
/**
* Writes this message to an mbox stream. This means adding a "From " line
* and making sure we've got a trailing newline.
*/
writeToMboxStream: function (aStream) {
- let str = "From " + this._from[1] + "\n" + this.toMessageString() + "\n";
+ let str = "From " + this._from[1] + "\r\n" + this.toMessageString() +
+ "\r\n";
aStream.write(str, str.length);
}
}
/**
* Write a list of messages in mbox format to a file
*
* @param aMessages The list of SyntheticMessages instances to write.
@@ -361,36 +476,36 @@ MessageGenerator.prototype = {
*
* @param aNameNumber The 'number' of the name you want which must be less
* than MAX_VALID_NAMES.
* @returns The unique name corresponding to the name number.
*/
makeName: function(aNameNumber) {
let iFirst = aNameNumber % FIRST_NAMES.length;
let iLast = (iFirst + Math.floor(aNameNumber / FIRST_NAMES.length)) %
- LAST_NAMES.length;
-
+ LAST_NAMES.length;
+
return FIRST_NAMES[iFirst] + " " + LAST_NAMES[iLast];
},
/**
* Generate a consistently determined (and reversible) e-mail address from
* a unique value; intended to work in parallel with makeName. Currently
* up to 26*26 unique addresses can be generated, but if your code cares,
* check against MAX_VALID_MAIL_ADDRESSES.
*
* @param aNameNumber The 'number' of the mail address you want which must be
* less than MAX_VALID_MAIL_ADDRESSES.
* @returns The unique name corresponding to the name mail address.
*/
makeMailAddress: function(aNameNumber) {
let iFirst = aNameNumber % FIRST_NAMES.length;
let iLast = (iFirst + Math.floor(aNameNumber / FIRST_NAMES.length)) %
- LAST_NAMES.length;
-
+ LAST_NAMES.length;
+
return FIRST_NAMES[iFirst].toLowerCase() + "@" +
LAST_NAMES[iLast].toLowerCase() + ".nul";
},
/**
* Generate a pair of name and e-mail address.
*
* @param aNameNumber The optional 'number' of the name and mail address you
@@ -483,34 +598,44 @@ MessageGenerator.prototype = {
},
/**
* Create a SyntheticMessage. All arguments are optional, but allow
* additional control. With no arguments specified, a new name/address will
* be generated that has not been used before, and sent to a new name/address
* that has not been used before.
*
- * @param aInReplyTo the SyntheticMessage this message should be in reply-to.
- * If that message was in reply to another message, we will appropriately
- * compensate for that.
* @param aArgs An object with any of the following attributes provided:
+ * attachments: A list of dictionaries suitable for passing to
+ * syntheticPartLeaf, plus a 'body' attribute that has already been
+ * encoded. Line chopping is on you FOR NOW.
+ * body: A dictionary suitable for passing to SyntheticPart plus a 'body'
+ * attribute that has already been encoded (if encoding is required).
+ * Line chopping is on you FOR NOW.
+ * callerData: A value to propagate to the callerData attribute on the
+ * resulting message.
+ * inReplyTo: the SyntheticMessage this message should be in reply-to.
+ * If that message was in reply to another message, we will
+ * appropriately compensate for that.
* replyAll: a boolean indicating whether this should be a reply-to-all or
* just to the author of the message. (er, to-only, not cc.)
+ * subject: The subject to use; you are responsible for doing any encoding
+ * before passing it in.
* toCount: the number of people who the message should be to.
* @returns a SyntheticMessage fashioned just to your liking.
*/
- makeMessage: function(aInReplyTo, aArgs) {
+ makeMessage: function(aArgs) {
aArgs = aArgs || {};
let msg = new SyntheticMessage();
- if (aInReplyTo) {
- msg.parent = aInReplyTo;
+ if (aArgs.inReplyTo) {
+ msg.parent = aArgs.inReplyTo;
msg.parent.children.push(msg);
- let srcMsg = aInReplyTo;
+ let srcMsg = aArgs.inReplyTo;
msg.subject = (srcMsg.subject.substring(0, 4) == "Re: ") ? srcMsg.subject
: ("Re: " + srcMsg.subject);
if (aArgs.replyAll)
msg.to = [srcMsg.from].concat(srcMsg.to.slice(1));
else
msg.to = [srcMsg.from];
msg.from = srcMsg.to[0];
@@ -518,26 +643,45 @@ MessageGenerator.prototype = {
// we want the <>'s.
msg.headers["In-Reply-To"] = srcMsg.headers["Message-Id"];
msg.headers["References"] = (srcMsg.headers["References"] || []).concat(
[srcMsg.headers["Message-Id"]]);
}
else {
msg.parent = null;
- msg.subject = this.makeSubject();
+ msg.subject = aArgs.subject || this.makeSubject();
msg.from = this.makeNameAndAddress();
msg.to = this.makeNamesAndAddresses(aArgs.toCount || 1);
}
msg.children = [];
msg.messageId = this.makeMessageId(msg);
msg.date = this.makeDate();
- msg.body = "I am an e-mail.";
+ let bodyPart;
+ if (aArgs.bodyPart)
+ bodyPart = aArgs.bodyPart;
+ else if (aArgs.body)
+ bodyPart = new SyntheticPartLeaf(aArgs.body.body, aArgs.body);
+ else
+ bodyPart = new SyntheticPartLeaf("I am an e-mail.");
+
+ // if it has any attachments, create a multipart/mixed to be the body and
+ // have it be the parent of the existing body and all the attachments
+ if (aArgs.attachments) {
+ let parts = [bodyPart];
+ for each (let [,attachDesc] in Iterator(aArgs.attachments))
+ parts.push(new SyntheticPartLeaf(attachDesc.body, attachDesc));
+ bodyPart = new SyntheticPartMultiMixed(parts);
+ }
+
+ msg.bodyPart = bodyPart;
+
+ msg.callerData = aArgs.callerData;
return msg;
}
}
/**
* Repository of generative message scenarios. Uses the magic bindMethods
* function below to allow you to reference methods/attributes without worrying
@@ -560,49 +704,49 @@ function MessageScenarioFactory(aMessage
}
MessageScenarioFactory.prototype = {
/** Create a chain of direct-reply messages of the given length. */
directReply: function(aNumMessages) {
aNumMessages = aNumMessages || 2;
let messages = [this._msgGen.makeMessage()];
for (let i = 1; i < aNumMessages; i++) {
- messages.push(msgGen.makeMessage(messages[i-1]));
+ messages.push(this._msgGen.makeMessage({inReplyTo: messages[i-1]}));
}
return messages;
},
/** Two siblings (present), one parent (missing). */
siblingsMissingParent: function() {
let missingParent = this._msgGen.makeMessage();
- let msg1 = this._msgGen.makeMessage(missingParent);
- let msg2 = this._msgGen.makeMessage(missingParent);
+ let msg1 = this._msgGen.makeMessage({inReplyTo: missingParent});
+ let msg2 = this._msgGen.makeMessage({inReplyTo: missingParent});
return [msg1, msg2];
},
/** Present parent, missing child, present grand-child. */
missingIntermediary: function() {
let msg1 = this._msgGen.makeMessage();
- let msg2 = this._msgGen.makeMessage(msg1);
- let msg3 = this._msgGen.makeMessage(msg2);
+ let msg2 = this._msgGen.makeMessage({inReplyTo: msg1});
+ let msg3 = this._msgGen.makeMessage({inReplyTo: msg2});
return [msg1, msg3];
},
/**
* The root message and all non-leaf nodes have aChildrenPerParent children,
* for a total of aHeight layers. (If aHeight is 1, we have just the root;
* if aHeight is 2, the root and his aChildrePerParent children.)
*/
fullPyramid: function(aChildrenPerParent, aHeight) {
let msgGen = this._msgGen;
let root = msgGen.makeMessage();
let messages = [root];
function helper(aParent, aRemDepth) {
for (let iChild = 0; iChild < aChildrenPerParent; iChild++) {
- let child = msgGen.makeMessage(aParent);
+ let child = msgGen.makeMessage({inReplyTo: aParent});
messages.push(child);
if (aRemDepth)
helper(child, aRemDepth - 1);
}
}
if (aHeight > 1)
helper(root, aHeight - 2);
return messages;
new file mode 100644
--- /dev/null
+++ b/mailnews/db/gloda/test/unit/test_intl.js
@@ -0,0 +1,93 @@
+/*
+ * Test that i18n goes through das pipes acceptably. Currently this means:
+ * - Subject, Body, and Attachment names are properly indexed.
+ */
+
+do_import_script("../mailnews/db/gloda/test/resources/messageGenerator.js");
+
+//these are imported by glodaTestHelper's import of head_maillocal
+// do_import_script("../mailnews/test/resources/mailDirService.js");
+// do_import_script("../mailnews/test/resources/mailTestUtils.js");
+do_import_script("../mailnews/db/gloda/test/resources/glodaTestHelper.js");
+
+// Create a message generator
+var msgGen = new MessageGenerator();
+
+/* ===== Tests ===== */
+
+var intlPhrases = [
+ {
+ name: "Vending Machine",
+ actual: '\u81ea\u52d5\u552e\u8ca8\u6a5f',
+ encodings: {
+ 'utf-8': ['=?utf-8?b?6Ieq5YuV5ZSu6LKo5qmf?=',
+ '\xe8\x87\xaa\xe5\x8b\x95\xe5\x94\xae\xe8\xb2\xa8\xe6\xa9\x9f'],
+ 'euc-jp': ['=?shift-jis?b?jqmTrppTid2LQA==?=',
+ '\xbc\xab\xc6\xb0\xd3\xb4\xb2\xdf\xb5\xa1'],
+ 'shift-jis': ['=?shift-jis?b?jqmTrppTid2LQA==?=',
+ '\x8e\xa9\x93\xae\x9aS\x89\xdd\x8b@']
+ }
+ }
+];
+
+/**
+ * For each phrase in the intlPhrases array (we are parameterized over it using
+ * parameterizeTest in the 'tests' declaration), create a message where the
+ * subject, body, and attachment name are populated using the encodings in
+ * the phrase's "encodings" attribute, one encoding per message. Make sure
+ * that the strings as exposed by the gloda representation are equal to the
+ * expected/actual value.
+ */
+function test_index(aPhrase) {
+ // create a synthetic message for each of the delightful encoding types
+ let messages = [];
+ for each (let [charset, encodings] in Iterator(aPhrase.encodings)) {
+ let [quoted, bodyEncoded] = encodings;
+
+ let smsg = msgGen.makeMessage({
+ subject: quoted,
+ body: {charset: charset, encoding: "8bit", body: bodyEncoded},
+ attachments: [
+ {filename: quoted, body: "gabba gabba hey"},
+ ],
+ // save off the actual value for checking
+ callerData: [charset, aPhrase.actual]
+ });
+
+ messages.push(smsg);
+ }
+
+ indexMessages(messages, verify_index, next_test);
+}
+
+/**
+ * Does the per-message verification for test_index. Knows what is right for
+ * each message because of the callerData attribute on the synthetic message.
+ */
+function verify_index(smsg, gmsg) {
+ let [charset, actual] = smsg.callerData;
+ let subject = gmsg.subject;
+ let indexedBodyText = gmsg.indexedBodyText.trim();
+ let attachmentName = gmsg.attachmentNames[0];
+ LOG.debug("using character set: " + charset + " actual: " + actual);
+ LOG.debug("subject: " + subject + " (len: " + subject.length + ")");
+ do_check_eq(actual, subject);
+ LOG.debug("body: " + indexedBodyText +
+ " (len: " + indexedBodyText.length + ")");
+ do_check_eq(actual, indexedBodyText);
+ LOG.debug("attachment name:" + attachmentName +
+ " (len: " + attachmentName.length + ")");
+ do_check_eq(actual, attachmentName);
+}
+
+/* ===== Driver ===== */
+
+var tests = [
+ parameterizeTest(test_index, intlPhrases),
+];
+
+function run_test() {
+ // use mbox injection because the fake server chokes sometimes right now
+ injectMessagesUsing(INJECT_MBOX);
+ glodaHelperRunTests(tests);
+}
--- a/mailnews/db/gloda/test/unit/test_query_messages.js
+++ b/mailnews/db/gloda/test/unit/test_query_messages.js
@@ -68,21 +68,23 @@ function categorizeMessage(aSynthMessage
* Generate messages in a single folder, categorizing them as we go.
*/
function generateFolderMessages() {
let messages = [];
let iAuthor = 0;
for (let iMessage = 0; iMessage < world.MESSAGES_PER_FOLDER; iMessage++) {
let iConvo = iMessage % world.NUM_CONVERSATIONS;
- let smsg = msgGen.makeMessage(world.lastMessagesInConvos[iConvo]);
+ let smsg = msgGen.makeMessage({
+ inReplyTo: world.lastMessagesInConvos[iConvo]
+ });
// we need missing messages to create ghosts, so periodically add an extra
// unknown into the equation
if ((iMessage % 3) == 0)
- smsg = msgGen.makeMessage(smsg);
+ smsg = msgGen.makeMessage({inReplyTo: smsg});
// makeMessage is not exceedingly clever right now, we need to overwrite
// From and To...
smsg.from = world.peoples[iAuthor];
iAuthor = (iAuthor + iConvo + 1) % world.NUM_AUTHORS;
// so, everyone is talking to everyone for this stuff
smsg.to = world.peoples;
world.lastMessagesInConvos[iConvo] = smsg;
--- a/mailnews/mime/emitters/src/nsMimeBaseEmitter.cpp
+++ b/mailnews/mime/emitters/src/nsMimeBaseEmitter.cpp
@@ -144,17 +144,18 @@ nsMimeBaseEmitter::~nsMimeBaseEmitter(vo
{
for (i=0; i<mAttachArray->Count(); i++)
{
attachmentInfoType *attachInfo = (attachmentInfoType *)mAttachArray->ElementAt(i);
if (!attachInfo)
continue;
PR_FREEIF(attachInfo->contentType);
- PR_FREEIF(attachInfo->displayName);
+ if (attachInfo->displayName)
+ NS_Free(attachInfo->displayName);
PR_FREEIF(attachInfo->urlSpec);
PR_FREEIF(attachInfo);
}
delete mAttachArray;
}
// Cleanup allocated header arrays...
CleanupHeaderArray(mHeaderArray);
@@ -383,26 +384,28 @@ nsMimeBaseEmitter::GetOutputListener(nsI
NS_IF_ADDREF(*listener);
}
return NS_OK;
}
// Attachment handling routines
nsresult
-nsMimeBaseEmitter::StartAttachment(const char *name, const char *contentType, const char *url,
+nsMimeBaseEmitter::StartAttachment(const nsACString &name,
+ const char *contentType,
+ const char *url,
PRBool aIsExternalAttachment)
{
// Ok, now we will setup the attachment info
mCurrentAttachment = (attachmentInfoType *) PR_NEWZAP(attachmentInfoType);
if ( (mCurrentAttachment) && mAttachArray)
{
++mAttachCount;
- mCurrentAttachment->displayName = strdup(name);
+ mCurrentAttachment->displayName = ToNewCString(name);
mCurrentAttachment->urlSpec = strdup(url);
mCurrentAttachment->contentType = strdup(contentType);
mCurrentAttachment->isExternalAttachment = aIsExternalAttachment;
}
return NS_OK;
}
@@ -429,49 +432,51 @@ NS_IMETHODIMP
nsMimeBaseEmitter::AddAttachmentField(const char *field, const char *value)
{
return NS_OK;
}
NS_IMETHODIMP
nsMimeBaseEmitter::UtilityWrite(const char *buf)
{
- PRInt32 tmpLen = strlen(buf);
PRUint32 written;
+ Write(nsDependentCString(buf), &written);
+ return NS_OK;
+}
- Write(buf, tmpLen, &written);
-
+NS_IMETHODIMP
+nsMimeBaseEmitter::UtilityWrite(const nsACString &buf)
+{
+ PRUint32 written;
+ Write(buf, &written);
return NS_OK;
}
NS_IMETHODIMP
nsMimeBaseEmitter::UtilityWriteCRLF(const char *buf)
{
- PRInt32 tmpLen = strlen(buf);
PRUint32 written;
-
- Write(buf, tmpLen, &written);
- Write(CRLF, 2, &written);
-
+ Write(nsDependentCString(buf), &written);
+ Write(NS_LITERAL_CSTRING(CRLF), &written);
return NS_OK;
}
NS_IMETHODIMP
-nsMimeBaseEmitter::Write(const char *buf, PRUint32 size, PRUint32 *amountWritten)
+nsMimeBaseEmitter::Write(const nsACString &buf, PRUint32 *amountWritten)
{
unsigned int written = 0;
nsresult rv = NS_OK;
PRUint32 needToWrite;
#ifdef DEBUG_BenB
// If you want to see libmime output...
printf("%s", buf);
#endif
- PR_LOG(gMimeEmitterLogModule, PR_LOG_ALWAYS, (buf));
+ PR_LOG(gMimeEmitterLogModule, PR_LOG_ALWAYS, (PromiseFlatCString(buf).get()));
//
// Make sure that the buffer we are "pushing" into has enough room
// for the write operation. If not, we have to buffer, return, and get
// it on the next time through
//
*amountWritten = 0;
needToWrite = mBufferMgr->GetSize();
@@ -483,30 +488,32 @@ nsMimeBaseEmitter::Write(const char *buf
mTotalWritten += written;
mBufferMgr->ReduceBuffer(written);
*amountWritten = written;
// if we couldn't write all the old data, buffer the new data
// and return
if (mBufferMgr->GetSize() > 0)
{
- mBufferMgr->IncreaseBuffer(buf, size);
+ mBufferMgr->IncreaseBuffer(buf.BeginReading(), buf.Length());
return rv;
}
}
// if we get here, we are dealing with new data...try to write
// and then do the right thing...
- rv = WriteHelper(buf, size, &written);
+ rv = WriteHelper(buf.BeginReading(), buf.Length(), &written);
*amountWritten = written;
mTotalWritten += written;
- if (written < size)
- mBufferMgr->IncreaseBuffer(buf+written, (size-written));
+ if (written < buf.Length()) {
+ const nsACString &remainder = Substring(buf, written);
+ mBufferMgr->IncreaseBuffer(remainder.BeginReading(), remainder.Length());
+ }
return rv;
}
nsresult
nsMimeBaseEmitter::WriteHelper(const char *buf, PRUint32 count, PRUint32 *countWritten)
{
nsresult rv;
@@ -922,17 +929,17 @@ nsMimeBaseEmitter::Complete()
{
// If we are here and still have data to write, we should try
// to flush it...if we try and fail, we should probably return
// an error!
PRUint32 written;
nsresult rv = NS_OK;
while ( NS_SUCCEEDED(rv) && (mBufferMgr) && (mBufferMgr->GetSize() > 0))
- rv = Write("", 0, &written);
+ rv = Write(EmptyCString(), &written);
if (mOutListener)
{
PRUint32 bytesInStream = 0;
nsresult rv2 = mInputStream->Available(&bytesInStream);
NS_ASSERTION(NS_SUCCEEDED(rv2), "Available failed");
if (bytesInStream)
{
@@ -958,17 +965,17 @@ nsMimeBaseEmitter::EndHeader()
// body handling routines
NS_IMETHODIMP
nsMimeBaseEmitter::StartBody(PRBool bodyOnly, const char *msgID, const char *outCharset)
{
return NS_OK;
}
NS_IMETHODIMP
-nsMimeBaseEmitter::WriteBody(const char *buf, PRUint32 size, PRUint32 *amountWritten)
+nsMimeBaseEmitter::WriteBody(const nsACString &buf, PRUint32 *amountWritten)
{
return NS_OK;
}
NS_IMETHODIMP
nsMimeBaseEmitter::EndBody()
{
return NS_OK;
--- a/mailnews/mime/emitters/src/nsMimeBaseEmitter.h
+++ b/mailnews/mime/emitters/src/nsMimeBaseEmitter.h
@@ -91,16 +91,17 @@ public:
// nsISupports interface
NS_DECL_ISUPPORTS
NS_DECL_NSIMIMEEMITTER
NS_DECL_NSIINTERFACEREQUESTOR
// Utility output functions...
+ NS_IMETHOD UtilityWrite(const nsACString &buf);
NS_IMETHOD UtilityWriteCRLF(const char *buf);
// For string bundle usage...
char *MimeGetStringByName(const char *aHeaderName);
char *MimeGetStringByID(PRInt32 aID);
char *LocalizeHeaderName(const char *aHeaderName, const char *aDefaultName);
// For header processing...
--- a/mailnews/mime/emitters/src/nsMimeHtmlEmitter.cpp
+++ b/mailnews/mime/emitters/src/nsMimeHtmlEmitter.cpp
@@ -409,17 +409,17 @@ nsMimeHtmlDisplayEmitter::EndHeader()
UtilityWriteCRLF("<body>");
}
WriteHTMLHeaders();
return NS_OK;
}
nsresult
-nsMimeHtmlDisplayEmitter::StartAttachment(const char *name,
+nsMimeHtmlDisplayEmitter::StartAttachment(const nsACString &name,
const char *contentType,
const char *url,
PRBool aIsExternalAttachment)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIMsgHeaderSink> headerSink;
rv = GetHeaderSink(getter_AddRefs(headerSink));
@@ -435,33 +435,21 @@ nsMimeHtmlDisplayEmitter::StartAttachmen
nsCOMPtr<nsINntpUrl> nntpUrl (do_QueryInterface(mURL, &rv));
if (NS_SUCCEEDED(rv) && nntpUrl)
rv = msgurl->GetOriginalSpec(getter_Copies(uriString));
else
rv = msgurl->GetUri(getter_Copies(uriString));
}
// we need to convert the attachment name from UTF-8 to unicode before
- // we emit it...
+ // we emit it. The attachment name has already been rfc2047 processed
+ // upstream of us. (Namely, mime_decode_filename has been called, deferring
+ // to nsIMimeHeaderParam.decodeParameter.)
nsString unicodeHeaderValue;
-
- rv = NS_ERROR_FAILURE; // use failure to mean that we couldn't decode
- if (mUnicodeConverter)
- rv = mUnicodeConverter->DecodeMimeHeader(name, nsnull, PR_FALSE, PR_TRUE,
- unicodeHeaderValue);
-
- if (NS_FAILED(rv))
- {
- CopyUTF8toUTF16(nsDependentCString(name), unicodeHeaderValue);
-
- // but it's not really a failure if we didn't have a converter
- // in the first place
- if ( !mUnicodeConverter )
- rv = NS_OK;
- }
+ CopyUTF8toUTF16(nsDependentCString(name), unicodeHeaderValue);
headerSink->HandleAttachment(contentType, url /* was escapedUrl */,
unicodeHeaderValue.get(), uriString.get(),
aIsExternalAttachment);
mSkipAttachment = PR_TRUE;
}
else if (mFormat == nsMimeOutput::nsMimeMessagePrintOutput)
@@ -481,17 +469,19 @@ nsMimeHtmlDisplayEmitter::StartAttachmen
}
// Attachment handling routines
// Ok, we are changing the way we handle these now...It used to be that we output
// HTML to make a clickable link, etc... but now, this should just be informational
// and only show up during printing
// XXX should they also show up during quoting?
nsresult
-nsMimeHtmlDisplayEmitter::StartAttachmentInBody(const char *name, const char *contentType, const char *url)
+nsMimeHtmlDisplayEmitter::StartAttachmentInBody(const nsACString &name,
+ const char *contentType,
+ const char *url)
{
mSkipAttachment = PR_FALSE;
if ( (contentType) &&
((!strcmp(contentType, APPLICATION_XPKCS7_MIME)) ||
(!strcmp(contentType, APPLICATION_XPKCS7_SIGNATURE)) ||
(!strcmp(contentType, TEXT_VCARD)))
) {
@@ -587,19 +577,20 @@ nsMimeHtmlDisplayEmitter::EndAllAttachme
nsCOMPtr<nsIMsgHeaderSink> headerSink;
rv = GetHeaderSink(getter_AddRefs(headerSink));
if (headerSink)
headerSink->OnEndAllAttachments();
return rv;
}
nsresult
-nsMimeHtmlDisplayEmitter::WriteBody(const char *buf, PRUint32 size, PRUint32 *amountWritten)
+nsMimeHtmlDisplayEmitter::WriteBody(const nsACString &buf,
+ PRUint32 *amountWritten)
{
- Write(buf, size, amountWritten);
+ Write(buf, amountWritten);
return NS_OK;
}
nsresult
nsMimeHtmlDisplayEmitter::EndBody()
{
if (mFormat != nsMimeOutput::nsMimeMessageFilterSniffer)
{
--- a/mailnews/mime/emitters/src/nsMimeHtmlEmitter.h
+++ b/mailnews/mime/emitters/src/nsMimeHtmlEmitter.h
@@ -56,40 +56,42 @@ public:
nsresult Init();
virtual ~nsMimeHtmlDisplayEmitter (void);
// Header handling routines.
NS_IMETHOD EndHeader();
// Attachment handling routines
- NS_IMETHOD StartAttachment(const char *name, const char *contentType, const char *url,
+ NS_IMETHOD StartAttachment(const nsACString &name,
+ const char *contentType, const char *url,
PRBool aIsExternalAttachment);
NS_IMETHOD AddAttachmentField(const char *field, const char *value);
NS_IMETHOD EndAttachment();
NS_IMETHOD EndAllAttachments();
// Body handling routines
- NS_IMETHOD WriteBody(const char *buf, PRUint32 size, PRUint32 *amountWritten);
+ NS_IMETHOD WriteBody(const nsACString &buf, PRUint32 *amountWritten);
NS_IMETHOD EndBody();
NS_IMETHOD WriteHTMLHeaders();
virtual nsresult WriteHeaderFieldHTMLPrefix();
virtual nsresult WriteHeaderFieldHTML(const char *field, const char *value);
virtual nsresult WriteHeaderFieldHTMLPostfix();
protected:
PRBool mFirst; // Attachment flag...
PRBool mSkipAttachment; // attachments we shouldn't show...
nsCOMPtr<nsIMsgHeaderSink> mHeaderSink;
nsresult GetHeaderSink(nsIMsgHeaderSink ** aHeaderSink);
PRBool BroadCastHeadersAndAttachments();
- nsresult StartAttachmentInBody(const char *name, const char *contentType, const char *url);
+ nsresult StartAttachmentInBody(const nsACString &name,
+ const char *contentType, const char *url);
nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
nsresult GenerateDateString(const char * dateString, nsACString& formattedDate);
nsresult BroadcastHeaders(nsIMsgHeaderSink * aHeaderSink, PRInt32 aHeaderMode, PRBool aFromNewsgroup);
};
#endif /* _nsMimeHtmlEmitter_h_ */
--- a/mailnews/mime/emitters/src/nsMimePlainEmitter.cpp
+++ b/mailnews/mime/emitters/src/nsMimePlainEmitter.cpp
@@ -83,14 +83,14 @@ nsMimePlainEmitter::AddHeaderField(const
nsresult
nsMimePlainEmitter::EndHeader()
{
UtilityWriteCRLF("");
return NS_OK;
}
NS_IMETHODIMP
-nsMimePlainEmitter::WriteBody(const char *buf, PRUint32 size, PRUint32 *amountWritten)
+nsMimePlainEmitter::WriteBody(const nsACString &buf, PRUint32 *amountWritten)
{
- Write(buf, size, amountWritten);
+ Write(buf, amountWritten);
return NS_OK;
}
--- a/mailnews/mime/emitters/src/nsMimePlainEmitter.h
+++ b/mailnews/mime/emitters/src/nsMimePlainEmitter.h
@@ -52,12 +52,12 @@ public:
virtual ~nsMimePlainEmitter (void);
// Header handling routines.
NS_IMETHOD StartHeader(PRBool rootMailHeader, PRBool headerOnly, const char *msgID,
const char *outCharset);
NS_IMETHOD AddHeaderField(const char *field, const char *value);
NS_IMETHOD EndHeader();
- NS_IMETHOD WriteBody(const char *buf, PRUint32 size, PRUint32 *amountWritten);
+ NS_IMETHOD WriteBody(const nsACString &buf, PRUint32 *amountWritten);
};
#endif /* _nsMimePlainEmitter_h_ */
--- a/mailnews/mime/emitters/src/nsMimeRawEmitter.cpp
+++ b/mailnews/mime/emitters/src/nsMimeRawEmitter.cpp
@@ -53,14 +53,14 @@ nsMimeRawEmitter::nsMimeRawEmitter()
}
nsMimeRawEmitter::~nsMimeRawEmitter(void)
{
}
NS_IMETHODIMP
-nsMimeRawEmitter::WriteBody(const char *buf, PRUint32 size, PRUint32 *amountWritten)
+nsMimeRawEmitter::WriteBody(const nsACString &buf, PRUint32 *amountWritten)
{
- Write(buf, size, amountWritten);
+ Write(buf, amountWritten);
return NS_OK;
}
--- a/mailnews/mime/emitters/src/nsMimeRawEmitter.h
+++ b/mailnews/mime/emitters/src/nsMimeRawEmitter.h
@@ -46,15 +46,15 @@
#include "nsIURI.h"
#include "nsIChannel.h"
class nsMimeRawEmitter : public nsMimeBaseEmitter {
public:
nsMimeRawEmitter ();
virtual ~nsMimeRawEmitter (void);
- NS_IMETHOD WriteBody(const char *buf, PRUint32 size, PRUint32 *amountWritten);
+ NS_IMETHOD WriteBody(const nsACString &buf, PRUint32 *amountWritten);
protected:
};
#endif /* _nsMimeRawEmitter_h_ */
--- a/mailnews/mime/emitters/src/nsMimeXmlEmitter.cpp
+++ b/mailnews/mime/emitters/src/nsMimeXmlEmitter.cpp
@@ -178,27 +178,29 @@ nsMimeXmlEmitter::EndHeader()
{
UtilityWrite("</mailheader>");
return NS_OK;
}
// Attachment handling routines
nsresult
-nsMimeXmlEmitter::StartAttachment(const char *name, const char *contentType, const char *url,
+nsMimeXmlEmitter::StartAttachment(const nsACString &name,
+ const char *contentType,
+ const char *url,
PRBool aIsExternalAttachment)
{
char buf[128];
++mAttachCount;
sprintf(buf, "<mailattachment id=\"%d\">", mAttachCount);
UtilityWrite(buf);
- AddAttachmentField(HEADER_PARM_FILENAME, name);
+ AddAttachmentField(HEADER_PARM_FILENAME, PromiseFlatCString(name).get());
return NS_OK;
}
nsresult
nsMimeXmlEmitter::AddAttachmentField(const char *field, const char *value)
{
WriteXMLTag(field, value);
return NS_OK;
--- a/mailnews/mime/emitters/src/nsMimeXmlEmitter.h
+++ b/mailnews/mime/emitters/src/nsMimeXmlEmitter.h
@@ -55,17 +55,18 @@ public:
// Header handling routines.
NS_IMETHOD StartHeader(PRBool rootMailHeader, PRBool headerOnly, const char *msgID,
const char *outCharset);
NS_IMETHOD AddHeaderField(const char *field, const char *value);
NS_IMETHOD EndHeader();
// Attachment handling routines
- NS_IMETHOD StartAttachment(const char *name, const char *contentType, const char *url,
+ NS_IMETHOD StartAttachment(const nsACString &name,
+ const char *contentType, const char *url,
PRBool aIsExternalAttachment);
NS_IMETHOD AddAttachmentField(const char *field, const char *value);
NS_IMETHOD EndAttachment();
NS_IMETHOD WriteXMLHeader(const char *msgID);
NS_IMETHOD WriteXMLTag(const char *tagName, const char *value);
protected:
--- a/mailnews/mime/public/nsIMimeEmitter.idl
+++ b/mailnews/mime/public/nsIMimeEmitter.idl
@@ -50,17 +50,17 @@ interface nsMimeHeaderDisplayTypes
const long NormalHeaders = 1;
const long AllHeaders = 2;
};
%{C++
#define NS_IMIME_MISC_STATUS_KEY "@mozilla.org/MimeMiscStatus;1?type="
%}
-[scriptable, uuid(c470b05e-9006-43a4-aa84-0da080360675)]
+[scriptable, uuid(7a57166f-2891-4122-9a74-6c3fab0caac3)]
interface nsIMimeEmitter : nsISupports {
// Output listener to allow access to it from mime.
attribute nsIStreamListener outputListener;
// These will be called to start and stop the total operation.
void initialize(in nsIURI url, in nsIChannel aChannel, in long aFormat);
void complete();
@@ -73,26 +73,27 @@ interface nsIMimeEmitter : nsISupports {
[const] in string msgID, [const] in string outCharset);
void addHeaderField([const] in string field, [const] in string value);
void addAllHeaders(in ACString allheaders);
void writeHTMLHeaders(); // Book case this with an EndHeader call.
void endHeader();
void updateCharacterSet([const] in string aCharset);
// Attachment handling routines.
- void startAttachment([const] in string name, [const] in string contentType,
+ void startAttachment([const] in AUTF8String name,
+ [const] in string contentType,
[const] in string url, in PRBool aNotDownloaded);
void addAttachmentField([const] in string field, [const] in string value);
void endAttachment();
void endAllAttachments();
// Body handling routines.
void startBody(in PRBool bodyOnly, [const] in string msgID, [const] in string outCharset);
- void writeBody([const] in string buf, in PRUint32 size, out PRUint32 amountWritten);
+ void writeBody([const] in AUTF8String buf, out PRUint32 amountWritten);
void endBody();
// Generic write routine. This is necessary for output that
// libmime needs to pass through without any particular parsing
// involved (i.e. decoded images, HTML Body Text, etc...
- void write([const] in string buf, in PRUint32 size, out PRUint32 amountWritten);
+ void write([const] in ACString buf, out PRUint32 amountWritten);
void utilityWrite([const] in string buf);
};
--- a/mailnews/mime/src/mimemoz2.cpp
+++ b/mailnews/mime/src/mimemoz2.cpp
@@ -888,24 +888,25 @@ mime_output_fn(const char *buf, PRInt32
// Now, write to the WriteBody method if this is a message body and not
// a part retrevial
if (!msd->options->part_to_load || msd->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
{
if (msd->output_emitter)
{
- msd->output_emitter->WriteBody(buf, (PRUint32) size, &written);
+ msd->output_emitter->WriteBody(Substring(buf, buf+size),
+ &written);
}
}
else
{
if (msd->output_emitter)
{
- msd->output_emitter->Write(buf, (PRUint32) size, &written);
+ msd->output_emitter->Write(Substring(buf, buf+size), &written);
}
}
return written;
}
extern "C" int
mime_display_stream_write (nsMIMESession *stream,
const char* buf,
@@ -1794,17 +1795,18 @@ mimeEmitterStartAttachment(MimeDisplayOp
mime_stream_data *msd = GetMSD(opt);
if (!msd)
return NS_ERROR_FAILURE;
if (msd->output_emitter)
{
nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
- return emitter->StartAttachment(name, contentType, url, aIsExternalAttachment);
+ return emitter->StartAttachment(nsDependentCString(name), contentType, url,
+ aIsExternalAttachment);
}
return NS_ERROR_FAILURE;
}
extern "C" nsresult
mimeEmitterEndAttachment(MimeDisplayOptions *opt)
{
--- a/mailnews/mime/src/nsStreamConverter.cpp
+++ b/mailnews/mime/src/nsStreamConverter.cpp
@@ -884,17 +884,17 @@ const char output[] = "\
nsCAutoString url;
if (NS_FAILED(mURI->GetSpec(url)))
return NS_ERROR_FAILURE;
PR_snprintf(outBuf, sizeof(outBuf), output, url.get(), url.get());
if (mEmitter)
- mEmitter->Write(outBuf, strlen(outBuf), &written);
+ mEmitter->Write(nsDependentCString(outBuf), &written);
// rhp: will this stop the stream???? Not sure.
return NS_ERROR_FAILURE;
}
char *buf = (char *)PR_Malloc(aLength);
if (!buf)
return NS_ERROR_OUT_OF_MEMORY; /* we couldn't allocate the object */
@@ -925,17 +925,19 @@ const char output[] = "\
}
readLen = writePtr - buf;
}
if (mOutputType == nsMimeOutput::nsMimeMessageSource)
{
rc = NS_OK;
if (mEmitter)
- rc = mEmitter->Write(buf, readLen, &written);
+ {
+ rc = mEmitter->Write(Substring(buf, buf+readLen), &written);
+ }
}
else if (mBridgeStream)
{
nsMIMESession *tSession = (nsMIMESession *) mBridgeStream;
rc = tSession->put_block((nsMIMESession *)mBridgeStream, buf, readLen);
}
PR_FREEIF(buf);