Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1

This commit is contained in:
Matt A. Tobin 2019-11-03 00:17:46 -04:00 • committed by Roy Tam
commit e400f4130a
1564 changed files with 510348 additions and 0 deletions

View file

@ -0,0 +1,229 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "mozilla/mailnews/MimeHeaderParser.h"
#include "mozilla/mailnews/Services.h"
#include "mozilla/DebugOnly.h"
#include "nsMemory.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsIMimeConverter.h"
#include "nsIMsgHeaderParser.h"
namespace mozilla {
namespace mailnews {
void detail::DoConversion(const nsTArray<nsString> &aUTF16Array,
nsTArray<nsCString> &aUTF8Array)
{
uint32_t count = aUTF16Array.Length();
aUTF8Array.SetLength(count);
for (uint32_t i = 0; i < count; ++i)
CopyUTF16toUTF8(aUTF16Array[i], aUTF8Array[i]);
}
void MakeMimeAddress(const nsACString &aName, const nsACString &aEmail,
nsACString &full)
{
nsAutoString utf16Address;
MakeMimeAddress(NS_ConvertUTF8toUTF16(aName), NS_ConvertUTF8toUTF16(aEmail),
utf16Address);
CopyUTF16toUTF8(utf16Address, full);
}
void MakeMimeAddress(const nsAString &aName, const nsAString &aEmail,
nsAString &full)
{
nsCOMPtr<nsIMsgHeaderParser> headerParser(services::GetHeaderParser());
nsCOMPtr<msgIAddressObject> address;
headerParser->MakeMailboxObject(aName, aEmail, getter_AddRefs(address));
msgIAddressObject *obj = address;
headerParser->MakeMimeHeader(&obj, 1, full);
}
void MakeDisplayAddress(const nsAString &aName, const nsAString &aEmail,
nsAString &full)
{
nsCOMPtr<nsIMsgHeaderParser> headerParser(services::GetHeaderParser());
nsCOMPtr<msgIAddressObject> object;
headerParser->MakeMailboxObject(aName, aEmail, getter_AddRefs(object));
object->ToString(full);
}
void RemoveDuplicateAddresses(const nsACString &aHeader,
const nsACString &aOtherEmails,
nsACString &result)
{
nsCOMPtr<nsIMsgHeaderParser> headerParser(services::GetHeaderParser());
headerParser->RemoveDuplicateAddresses(aHeader, aOtherEmails, result);
}
/////////////////////////////////////////////
// These are the core shim methods we need //
/////////////////////////////////////////////
nsCOMArray<msgIAddressObject> DecodedHeader(const nsAString &aHeader)
{
nsCOMArray<msgIAddressObject> retval;
if (aHeader.IsEmpty()) {
return retval;
}
nsCOMPtr<nsIMsgHeaderParser> headerParser(services::GetHeaderParser());
msgIAddressObject **addresses = nullptr;
uint32_t length;
nsresult rv = headerParser->ParseDecodedHeader(aHeader, false,
&length, &addresses);
MOZ_ASSERT(NS_SUCCEEDED(rv), "Javascript jsmime returned an error!");
if (NS_SUCCEEDED(rv) && length > 0 && addresses) {
retval.Adopt(addresses, length);
}
return retval;
}
nsCOMArray<msgIAddressObject> EncodedHeader(const nsACString &aHeader,
const char *aCharset)
{
nsCOMArray<msgIAddressObject> retval;
if (aHeader.IsEmpty()) {
return retval;
}
nsCOMPtr<nsIMsgHeaderParser> headerParser(services::GetHeaderParser());
msgIAddressObject **addresses = nullptr;
uint32_t length;
nsresult rv = headerParser->ParseEncodedHeader(aHeader, aCharset,
false, &length, &addresses);
MOZ_ASSERT(NS_SUCCEEDED(rv), "This should never fail!");
if (NS_SUCCEEDED(rv) && length > 0 && addresses) {
retval.Adopt(addresses, length);
}
return retval;
}
void ExtractAllAddresses(const nsCOMArray<msgIAddressObject> &aHeader,
nsTArray<nsString> &names, nsTArray<nsString> &emails)
{
uint32_t count = aHeader.Length();
// Prefill arrays before we start
names.SetLength(count);
emails.SetLength(count);
for (uint32_t i = 0; i < count; i++)
{
aHeader[i]->GetName(names[i]);
aHeader[i]->GetEmail(emails[i]);
}
if (count == 1 && names[0].IsEmpty() && emails[0].IsEmpty())
{
names.Clear();
emails.Clear();
}
}
void ExtractDisplayAddresses(const nsCOMArray<msgIAddressObject> &aHeader,
nsTArray<nsString> &displayAddrs)
{
uint32_t count = aHeader.Length();
displayAddrs.SetLength(count);
for (uint32_t i = 0; i < count; i++)
aHeader[i]->ToString(displayAddrs[i]);
if (count == 1 && displayAddrs[0].IsEmpty())
displayAddrs.Clear();
}
/////////////////////////////////////////////////
// All of these are based on the above methods //
/////////////////////////////////////////////////
void ExtractEmails(const nsCOMArray<msgIAddressObject> &aHeader,
nsTArray<nsString> &emails)
{
nsTArray<nsString> names;
ExtractAllAddresses(aHeader, names, emails);
}
void ExtractEmail(const nsCOMArray<msgIAddressObject> &aHeader,
nsACString &email)
{
AutoTArray<nsString, 1> names;
AutoTArray<nsString, 1> emails;
ExtractAllAddresses(aHeader, names, emails);
if (emails.Length() > 0)
CopyUTF16toUTF8(emails[0], email);
else
email.Truncate();
}
void ExtractFirstAddress(const nsCOMArray<msgIAddressObject> &aHeader,
nsACString &name, nsACString &email)
{
AutoTArray<nsString, 1> names, emails;
ExtractAllAddresses(aHeader, names, emails);
if (names.Length() > 0)
{
CopyUTF16toUTF8(names[0], name);
CopyUTF16toUTF8(emails[0], email);
}
else
{
name.Truncate();
email.Truncate();
}
}
void ExtractFirstAddress(const nsCOMArray<msgIAddressObject> &aHeader,
nsAString &name, nsACString &email)
{
AutoTArray<nsString, 1> names, emails;
ExtractAllAddresses(aHeader, names, emails);
if (names.Length() > 0)
{
name = names[0];
CopyUTF16toUTF8(emails[0], email);
}
else
{
name.Truncate();
email.Truncate();
}
}
void ExtractName(const nsCOMArray<msgIAddressObject> &aHeader, nsACString &name)
{
nsCString email;
ExtractFirstAddress(aHeader, name, email);
if (name.IsEmpty())
name = email;
}
void ExtractName(const nsCOMArray<msgIAddressObject> &aHeader, nsAString &name)
{
AutoTArray<nsString, 1> names;
AutoTArray<nsString, 1> emails;
ExtractAllAddresses(aHeader, names, emails);
if (names.Length() > 0)
{
if (names[0].IsEmpty())
name = emails[0];
else
name = names[0];
}
else
{
name.Truncate();
}
}
} // namespace mailnews
} // namespace mozilla

View file

@ -0,0 +1,108 @@
/* -*- 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 "comi18n.h"
#include "nsIStringCharsetDetector.h"
#include "nsMsgUtils.h"
#include "nsICharsetConverterManager.h"
#include "nsIMIMEHeaderParam.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsMsgMimeCID.h"
#include "nsIMimeConverter.h"
////////////////////////////////////////////////////////////////////////////////
// BEGIN PUBLIC INTERFACE
extern "C" {
void MIME_DecodeMimeHeader(const char *header, const char *default_charset,
bool override_charset, bool eatContinuations,
nsACString &result)
{
nsresult rv;
nsCOMPtr <nsIMimeConverter> mimeConverter =
do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
result.Truncate();
return;
}
mimeConverter->DecodeMimeHeaderToUTF8(nsDependentCString(header),
default_charset, override_charset,
eatContinuations, result);
}
// UTF-8 utility functions.
//detect charset soly based on aBuf. return in aCharset
nsresult
MIME_detect_charset(const char *aBuf, int32_t aLength, const char** aCharset)
{
nsresult res = NS_ERROR_UNEXPECTED;
nsString detector_name;
*aCharset = nullptr;
NS_GetLocalizedUnicharPreferenceWithDefault(nullptr, "intl.charset.detector", EmptyString(), detector_name);
if (!detector_name.IsEmpty()) {
nsAutoCString detector_contractid;
detector_contractid.AssignLiteral(NS_STRCDETECTOR_CONTRACTID_BASE);
detector_contractid.Append(NS_ConvertUTF16toUTF8(detector_name));
nsCOMPtr<nsIStringCharsetDetector> detector = do_CreateInstance(detector_contractid.get(), &res);
if (NS_SUCCEEDED(res)) {
nsDetectionConfident oConfident;
res = detector->DoIt(aBuf, aLength, aCharset, oConfident);
if (NS_SUCCEEDED(res) && (eBestAnswer == oConfident || eSureAnswer == oConfident)) {
return NS_OK;
}
}
}
return res;
}
//Get unicode decoder(from inputcharset to unicode) for aInputCharset
nsresult
MIME_get_unicode_decoder(const char* aInputCharset, nsIUnicodeDecoder **aDecoder)
{
nsresult res;
// get charset converters.
nsCOMPtr<nsICharsetConverterManager> ccm =
do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
if (NS_SUCCEEDED(res)) {
// create a decoder (conv to unicode), ok if failed if we do auto detection
if (!*aInputCharset || !PL_strcasecmp("us-ascii", aInputCharset))
res = ccm->GetUnicodeDecoderRaw("ISO-8859-1", aDecoder);
else
// GetUnicodeDecoderInternal in order to support UTF-7 messages
//
// XXX this means that even HTML messages in UTF-7 will be decoded
res = ccm->GetUnicodeDecoderInternal(aInputCharset, aDecoder);
}
return res;
}
//Get unicode encoder(from unicode to inputcharset) for aOutputCharset
nsresult
MIME_get_unicode_encoder(const char* aOutputCharset, nsIUnicodeEncoder **aEncoder)
{
nsresult res;
// get charset converters.
nsCOMPtr<nsICharsetConverterManager> ccm =
do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
if (NS_SUCCEEDED(res) && *aOutputCharset) {
// create a encoder (conv from unicode)
res = ccm->GetUnicodeEncoder(aOutputCharset, aEncoder);
}
return res;
}
} /* end of extern "C" */
// END PUBLIC INTERFACE

View file

@ -0,0 +1,42 @@
/* -*- 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/. */
#ifndef _COMI18N_LOADED_H_
#define _COMI18N_LOADED_H_
#include "msgCore.h"
class nsIUnicodeDecoder;
class nsIUnicodeEncoder;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* Decode MIME header to UTF-8.
* Uses MIME_ConvertCharset if the decoded string needs a conversion.
*
*
* @param header [IN] A header to decode.
* @param default_charset [IN] Default charset to apply to ulabeled non-UTF-8 8bit data
* @param override_charset [IN] If true, default_charset used instead of any charset labeling other than UTF-8
* @param eatContinuations [IN] If true, unfold headers
* @param result [OUT] Decoded buffer
*/
void MIME_DecodeMimeHeader(const char *header, const char *default_charset,
bool override_charset, bool eatContinuations,
nsACString &result);
nsresult MIME_detect_charset(const char *aBuf, int32_t aLength, const char** aCharset);
nsresult MIME_get_unicode_decoder(const char* aInputCharset, nsIUnicodeDecoder **aDecoder);
nsresult MIME_get_unicode_encoder(const char* aOutputCharset, nsIUnicodeEncoder **aEncoder);
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif // _COMI18N_LOADED_H_

View file

@ -0,0 +1,29 @@
/* 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/. */
function parseNewsgroups(headers) {
let ng = [];
for (let header of headers) {
ng = ng.concat(header.split(/\s*,\s*/));
}
return ng;
}
function emitNewsgroups(groups) {
// Don't encode the newsgroups names in RFC 2047...
if (groups.length == 1)
this.addText(groups[0], false);
else {
this.addText(groups[0], false);
for (let i = 1; i < groups.length; i++) {
this.addText(",", false); // only comma, no space!
this.addText(groups[i], false);
}
}
}
jsmime.headerparser.addStructuredDecoder("Newsgroups", parseNewsgroups);
jsmime.headerparser.addStructuredDecoder("Followup-To", parseNewsgroups);
jsmime.headeremitter.addStructuredEncoder("Newsgroups", emitNewsgroups);
jsmime.headeremitter.addStructuredEncoder("Followup-To", emitNewsgroups);

View file

@ -0,0 +1,90 @@
/* 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/Services.jsm");
/**
* This file exports the JSMime code, polyfilling code as appropriate for use in
* Gecko.
*/
// Load the core MIME parser. Since it doesn't define EXPORTED_SYMBOLS, we must
// use the subscript loader instead.
Services.scriptloader.loadSubScript("resource:///modules/jsmime/jsmime.js");
var EXPORTED_SYMBOLS = ["jsmime"];
// A polyfill to support non-encoding-spec charsets. Since the only converter
// available to us from JavaScript has a very, very weak and inflexible API, we
// choose to rely on the regular text decoder unless absolutely necessary.
// support non-encoding-spec charsets.
function FakeTextDecoder(label="UTF-8", options = {}) {
this._reset(label);
// So nsIScriptableUnicodeConverter only gives us fatal=false, unless we are
// using UTF-8, where we only get fatal=true. The internals of said class tell
// us to use a C++-only class if we need better behavior.
}
FakeTextDecoder.prototype = {
_reset: function (label) {
this._encoder = Components.classes[
"@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
this._encoder.isInternal = true;
let manager = Components.classes[
"@mozilla.org/charset-converter-manager;1"]
.createInstance(Components.interfaces.nsICharsetConverterManager);
this._encoder.charset = manager.getCharsetAlias(label);
},
get encoding() { return this._encoder.charset; },
decode: function (input, options = {}) {
let more = 'stream' in options ? options.stream : false;
let result = "";
if (input !== undefined) {
let data = new Uint8Array(input);
result = this._encoder.convertFromByteArray(data, data.length);
}
// This isn't quite right--it won't handle errors if there are a few
// remaining bytes in the buffer, but it's the best we can do.
if (!more)
this._reset(this.encoding);
return result;
},
};
var RealTextDecoder = TextDecoder;
function FallbackTextDecoder(charset, options) {
try {
return new RealTextDecoder(charset, options);
} catch (e) {
return new FakeTextDecoder(charset, options);
}
}
TextDecoder = FallbackTextDecoder;
// The following code loads custom MIME encoders.
var CATEGORY_NAME = "custom-mime-encoder";
Services.obs.addObserver(function (subject, topic, data) {
subject = subject.QueryInterface(Components.interfaces.nsISupportsCString)
.data;
if (data == CATEGORY_NAME) {
let url = catman.getCategoryEntry(CATEGORY_NAME, subject);
Services.scriptloader.loadSubScript(url, {}, "UTF-8");
}
}, "xpcom-category-entry-added", false);
var catman = Components.classes["@mozilla.org/categorymanager;1"]
.getService(Components.interfaces.nsICategoryManager);
var entries = catman.enumerateCategory(CATEGORY_NAME);
while (entries.hasMoreElements()) {
let string = entries.getNext()
.QueryInterface(Components.interfaces.nsISupportsCString)
.data;
let url = catman.getCategoryEntry(CATEGORY_NAME, string);
Services.scriptloader.loadSubScript(url, {}, "UTF-8");
}

View file

@ -0,0 +1,7 @@
; 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/.
LIBRARY mime.dll
EXPORTS

View file

@ -0,0 +1,512 @@
/* 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/. */
Components.utils.import("resource://gre/modules/Deprecated.jsm");
Components.utils.import("resource:///modules/jsmime.jsm");
Components.utils.import("resource:///modules/mimeParser.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function HeaderHandler() {
this.value = "";
this.deliverData = function (str) { this.value += str; };
this.deliverEOF = function () {};
}
function StringEnumerator(iterator) {
this._iterator = iterator;
this._next = undefined;
}
StringEnumerator.prototype = {
QueryInterface: XPCOMUtils.generateQI([
Components.interfaces.nsIUTF8StringEnumerator]),
_setNext: function () {
if (this._next !== undefined)
return;
this._next = this._iterator.next();
},
hasMore: function () {
this._setNext();
return !this._next.done;
},
getNext: function () {
this._setNext();
let result = this._next;
this._next = undefined;
if (result.done)
throw Components.results.NS_ERROR_UNEXPECTED;
return result.value;
}
};
/**
* If we get XPConnect-wrapped objects for msgIAddressObjects, we will have
* properties defined for 'group' that throws off jsmime. This function converts
* the addresses into the form that jsmime expects.
*/
function fixXpconnectAddresses(addrs) {
return addrs.map((addr) => {
// This is ideally !addr.group, but that causes a JS strict warning, if
// group is not in addr, since that's enabled in all chrome code now.
if (!('group' in addr) || addr.group === undefined || addr.group === null) {
return MimeAddressParser.prototype.makeMailboxObject(addr.name,
addr.email);
} else {
return MimeAddressParser.prototype.makeGroupObject(addr.name,
fixXpconnectAddresses(addr.group));
}
});
}
/**
* This is a base handler for supporting msgIStructuredHeaders, since we have
* two implementations that need the readable aspects of the interface.
*/
function MimeStructuredHeaders() {
}
MimeStructuredHeaders.prototype = {
getHeader: function (aHeaderName) {
let name = aHeaderName.toLowerCase();
return this._headers.get(name);
},
hasHeader: function (aHeaderName) {
return this._headers.has(aHeaderName.toLowerCase());
},
getUnstructuredHeader: function (aHeaderName) {
let result = this.getHeader(aHeaderName);
if (result === undefined || typeof result == "string")
return result;
throw Components.results.NS_ERROR_ILLEGAL_VALUE;
},
getAddressingHeader: function (aHeaderName, aPreserveGroups, count) {
let addrs = this.getHeader(aHeaderName);
if (addrs === undefined) {
addrs = [];
} else if (!Array.isArray(addrs)) {
throw Components.results.NS_ERROR_ILLEGAL_VALUE;
}
return fixArray(addrs, aPreserveGroups, count);
},
getRawHeader: function (aHeaderName) {
let result = this.getHeader(aHeaderName);
if (result === undefined)
return result;
let value = jsmime.headeremitter.emitStructuredHeader(aHeaderName,
result, {});
// Strip off the header name and trailing whitespace before returning...
value = value.substring(aHeaderName.length + 2).trim();
// ... as well as embedded newlines.
value = value.replace(/\r\n/g, '');
return value;
},
get headerNames() {
return new StringEnumerator(this._headers.keys());
},
buildMimeText: function () {
if (this._headers.size == 0) {
return "";
}
let handler = new HeaderHandler();
let emitter = jsmime.headeremitter.makeStreamingEmitter(handler, {
useASCII: true
});
for (let [value, header] of this._headers) {
emitter.addStructuredHeader(value, header);
}
emitter.finish();
return handler.value;
},
};
function MimeHeaders() {
}
MimeHeaders.prototype = {
__proto__: MimeStructuredHeaders.prototype,
classDescription: "Mime headers implementation",
classID: Components.ID("d1258011-f391-44fd-992e-c6f4b461a42f"),
contractID: "@mozilla.org/messenger/mimeheaders;1",
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIMimeHeaders,
Components.interfaces.msgIStructuredHeaders]),
initialize: function MimeHeaders_initialize(allHeaders) {
this._headers = MimeParser.extractHeaders(allHeaders);
},
extractHeader: function MimeHeaders_extractHeader(header, getAll) {
if (!this._headers)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
// Canonicalized to lower-case form
header = header.toLowerCase();
if (!this._headers.has(header))
return null;
var values = this._headers.getRawHeader(header);
if (getAll)
return values.join(",\r\n\t");
else
return values[0];
},
get allHeaders() {
return this._headers.rawHeaderText;
}
};
function MimeWritableStructuredHeaders() {
this._headers = new Map();
}
MimeWritableStructuredHeaders.prototype = {
__proto__: MimeStructuredHeaders.prototype,
classID: Components.ID("c560806a-425f-4f0f-bf69-397c58c599a7"),
QueryInterface: XPCOMUtils.generateQI([
Components.interfaces.msgIStructuredHeaders,
Components.interfaces.msgIWritableStructuredHeaders]),
setHeader: function (aHeaderName, aValue) {
this._headers.set(aHeaderName.toLowerCase(), aValue);
},
deleteHeader: function (aHeaderName) {
this._headers.delete(aHeaderName.toLowerCase());
},
addAllHeaders: function (aHeaders) {
let headerList = aHeaders.headerNames;
while (headerList.hasMore()) {
let header = headerList.getNext();
this.setHeader(header, aHeaders.getHeader(header));
}
},
setUnstructuredHeader: function (aHeaderName, aValue) {
this.setHeader(aHeaderName, aValue);
},
setAddressingHeader: function (aHeaderName, aAddresses, aCount) {
this.setHeader(aHeaderName, fixXpconnectAddresses(aAddresses));
},
setRawHeader: function (aHeaderName, aValue, aCharset) {
aValue = jsmime.headerparser.convert8BitHeader(aValue, aCharset);
try {
this.setHeader(aHeaderName,
jsmime.headerparser.parseStructuredHeader(aHeaderName, aValue));
} catch (e) {
// This means we don't have a structured encoder. Just assume it's a raw
// string value then.
this.setHeader(aHeaderName, aValue);
}
}
};
// These are prototypes for nsIMsgHeaderParser implementation
var Mailbox = {
toString: function () {
return this.name ? this.name + " <" + this.email + ">" : this.email;
}
};
var EmailGroup = {
toString: function () {
return this.name + ": " + this.group.map(x => x.toString()).join(", ");
}
};
// A helper method for parse*Header that takes into account the desire to
// preserve group and also tweaks the output to support the prototypes for the
// XPIDL output.
function fixArray(addresses, preserveGroups, count) {
function resetPrototype(obj, prototype) {
let prototyped = Object.create(prototype);
for (let key of Object.getOwnPropertyNames(obj)) {
if (typeof obj[key] == "string") {
prototyped[key] = obj[key].replace(/\x00/g, '');
} else {
prototyped[key] = obj[key];
}
}
return prototyped;
}
let outputArray = [];
for (let element of addresses) {
if ('group' in element) {
// Fix up the prototypes of the group and the list members
element = resetPrototype(element, EmailGroup);
element.group = element.group.map(e => resetPrototype(e, Mailbox));
// Add to the output array
if (preserveGroups)
outputArray.push(element);
else
outputArray = outputArray.concat(element.group);
} else {
element = resetPrototype(element, Mailbox);
outputArray.push(element);
}
}
if (count)
count.value = outputArray.length;
return outputArray;
}
function MimeAddressParser() {
}
MimeAddressParser.prototype = {
classID: Components.ID("96bd8769-2d0e-4440-963d-22b97fb3ba77"),
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIMsgHeaderParser]),
parseEncodedHeader: function (aHeader, aCharset, aPreserveGroups, count) {
aHeader = aHeader || "";
let value = MimeParser.parseHeaderField(aHeader,
MimeParser.HEADER_ADDRESS | MimeParser.HEADER_OPTION_ALL_I18N, aCharset);
return fixArray(value, aPreserveGroups, count);
},
parseDecodedHeader: function (aHeader, aPreserveGroups, count) {
aHeader = aHeader || "";
let value = MimeParser.parseHeaderField(aHeader, MimeParser.HEADER_ADDRESS);
return fixArray(value, aPreserveGroups, count);
},
makeMimeHeader: function (addresses, length) {
addresses = fixXpconnectAddresses(addresses);
// Don't output any necessary continuations, so make line length as large as
// possible first.
let options = {
softMargin: 900,
hardMargin: 900,
useASCII: false // We don't want RFC 2047 encoding here.
};
let handler = new HeaderHandler();
let emitter = new jsmime.headeremitter.makeStreamingEmitter(handler,
options);
emitter.addAddresses(addresses);
emitter.finish(true);
return handler.value.replace(/\r\n( |$)/g, '');
},
extractFirstName: function (aHeader) {
let address = this.parseDecodedHeader(aHeader, false)[0];
return address.name || address.email;
},
removeDuplicateAddresses: function (aAddrs, aOtherAddrs) {
// This is actually a rather complicated algorithm, especially if we want to
// preserve group structure. Basically, we use a set to identify which
// headers we have seen and therefore want to remove. To work in several
// various forms of edge cases, we need to normalize the entries in that
// structure.
function normalize(email) {
// XXX: This algorithm doesn't work with IDN yet. It looks like we have to
// convert from IDN then do lower case, but I haven't confirmed yet.
return email.toLowerCase();
}
// The filtration function, which removes email addresses that are
// duplicates of those we have already seen.
function filterAccept(e) {
if ('email' in e) {
// If we've seen the address, don't keep this one; otherwise, add it to
// the list.
let key = normalize(e.email);
if (allAddresses.has(key))
return false;
allAddresses.add(key);
} else {
// Groups -> filter out all the member addresses.
e.group = e.group.filter(filterAccept);
}
return true;
}
// First, collect all of the emails to forcibly delete.
let allAddresses = new Set();
for (let element of this.parseDecodedHeader(aOtherAddrs, false)) {
allAddresses.add(normalize(element.email));
}
// The actual data to filter
let filtered = this.parseDecodedHeader(aAddrs, true).filter(filterAccept);
return this.makeMimeHeader(filtered);
},
makeMailboxObject: function (aName, aEmail) {
let object = Object.create(Mailbox);
object.name = aName;
object.email = aEmail ? aEmail.trim() : aEmail;
return object;
},
makeGroupObject: function (aName, aMembers) {
let object = Object.create(EmailGroup);
object.name = aName;
object.group = aMembers;
return object;
},
makeFromDisplayAddress: function (aDisplay, count) {
// The basic idea is to split on every comma, so long as there is a
// preceding @.
let output = [];
while (aDisplay.length) {
let at = aDisplay.indexOf('@');
let comma = aDisplay.indexOf(',', at + 1);
let addr;
if (comma > 0) {
addr = aDisplay.substr(0, comma);
aDisplay = aDisplay.substr(comma + 1);
} else {
addr = aDisplay;
aDisplay = "";
}
output.push(this._makeSingleAddress(addr.trimLeft()));
}
if (count)
count.value = output.length;
return output;
},
/// Construct a single email address from a name <local@domain> token.
_makeSingleAddress: function (aDisplayName) {
if (aDisplayName.includes('<')) {
let lbracket = aDisplayName.lastIndexOf('<');
let rbracket = aDisplayName.lastIndexOf('>');
return this.makeMailboxObject(
lbracket == 0 ? '' : aDisplayName.slice(0, lbracket).trim(),
aDisplayName.slice(lbracket + 1, rbracket));
} else {
return this.makeMailboxObject('', aDisplayName);
}
},
// What follows is the deprecated API that will be removed shortly.
parseHeadersWithArray: function (aHeader, aAddrs, aNames, aFullNames) {
let addrs = [], names = [], fullNames = [];
// Parse header, but without HEADER_OPTION_ALLOW_RAW.
let value = MimeParser.parseHeaderField(aHeader || "",
MimeParser.HEADER_ADDRESS |
MimeParser.HEADER_OPTION_DECODE_2231 |
MimeParser.HEADER_OPTION_DECODE_2047,
undefined);
let allAddresses = fixArray(value, false);
// Don't index the dummy empty address.
if (aHeader.trim() == "")
allAddresses = [];
for (let address of allAddresses) {
addrs.push(address.email);
names.push(address.name || null);
fullNames.push(address.toString());
}
aAddrs.value = addrs;
aNames.value = names;
aFullNames.value = fullNames;
return allAddresses.length;
},
extractHeaderAddressMailboxes: function (aLine) {
return this.parseDecodedHeader(aLine).map(addr => addr.email).join(", ");
},
extractHeaderAddressNames: function (aLine) {
return this.parseDecodedHeader(aLine).map(addr => addr.name || addr.email)
.join(", ");
},
extractHeaderAddressName: function (aLine) {
let addrs = this.parseDecodedHeader(aLine).map(addr =>
addr.name || addr.email);
return addrs.length == 0 ? "" : addrs[0];
},
makeMimeAddress: function (aName, aEmail) {
let object = this.makeMailboxObject(aName, aEmail);
return this.makeMimeHeader([object]);
},
};
function MimeConverter() {
}
MimeConverter.prototype = {
classID: Components.ID("93f8c049-80ed-4dda-9000-94ad8daba44c"),
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIMimeConverter]),
encodeMimePartIIStr_UTF8: function (aHeader, aStructured, aCharset,
aFieldNameLen, aLineLength) {
// The JSMime encoder only works in UTF-8, so if someone requests to not do
// it, they need to change their code.
if (aCharset.toLowerCase() != "utf-8") {
Deprecated.warning("Encoding to non-UTF-8 values is obsolete",
"http://bugzilla.mozilla.org/show_bug.cgi?id=790855");
}
// Compute the encoding options. The way our API is structured in this
// method is really horrendous and does not align with the way that JSMime
// handles it. Instead, we'll need to create a fake header to take into
// account the aFieldNameLen parameter.
let fakeHeader = '-'.repeat(aFieldNameLen);
let options = {
softMargin: aLineLength,
useASCII: true,
};
let handler = new HeaderHandler();
let emitter = new jsmime.headeremitter.makeStreamingEmitter(handler,
options);
// Add the text to the be encoded.
emitter.addHeaderName(fakeHeader);
if (aStructured) {
// Structured really means "this is an addressing header"
let addresses = MimeParser.parseHeaderField(aHeader,
MimeParser.HEADER_ADDRESS | MimeParser.HEADER_OPTION_DECODE_2047);
// This happens in one of our tests if there is a "bare" email but no
// @ sign. Without it, the result disappears since our emission code
// assumes that an empty email is not worth emitting.
if (addresses.length === 1 && addresses[0].email === "" &&
addresses[0].name !== "") {
addresses[0].email = addresses[0].name;
addresses[0].name = "";
}
emitter.addAddresses(addresses);
} else {
emitter.addUnstructured(aHeader);
}
// Compute the output. We need to strip off the fake prefix added earlier
// and the extra CRLF at the end.
emitter.finish(true);
let value = handler.value;
value = value.replace(new RegExp(fakeHeader + ":\\s*"), "");
return value.substring(0, value.length - 2);
},
decodeMimeHeader: function (aHeader, aDefaultCharset, aOverride, aUnfold) {
let value = MimeParser.parseHeaderField(aHeader,
MimeParser.HEADER_UNSTRUCTURED | MimeParser.HEADER_OPTION_ALL_I18N,
aDefaultCharset);
if (aUnfold) {
value = value.replace(/[\r\n]\t/g, ' ')
.replace(/[\r\n]/g, '');
}
return value;
},
// This is identical to the above, except for factors that are handled by the
// xpconnect conversion process
decodeMimeHeaderToUTF8: function () {
return this.decodeMimeHeader.apply(this, arguments);
},
};
var components = [MimeHeaders, MimeWritableStructuredHeaders, MimeAddressParser,
MimeConverter];
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

View file

@ -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";
}
},
};

View file

@ -0,0 +1,150 @@
/* -*- 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/. */
/* Most of this code is copied from mimethsa. If you find a bug here, check that class, too. */
/* This runs the entire HTML document through the Mozilla HTML parser, and
then outputs it as string again. This ensures that the HTML document is
syntactically correct and complete and all tags and attributes are closed.
That prevents "MIME in the middle" attacks like efail.de.
The base problem is that we concatenate different MIME parts in the output
and render them all together as a single HTML document in the display.
The better solution would be to put each MIME part into its own <iframe type="content">.
during rendering. Unfortunately, we'd need <iframe seamless> for that.
That would remove the need for this workaround, and stop even more attack classes.
*/
#include "mimeTextHTMLParsed.h"
#include "prmem.h"
#include "prlog.h"
#include "msgCore.h"
#include "nsIDOMParser.h"
#include "nsIDOMDocument.h"
#include "nsIDocument.h"
#include "nsIDocumentEncoder.h"
#include "mozilla/ErrorResult.h"
#include "nsIPrefBranch.h"
#include "mimethtm.h"
#define MIME_SUPERCLASS mimeInlineTextHTMLClass
MimeDefClass(MimeInlineTextHTMLParsed, MimeInlineTextHTMLParsedClass,
mimeInlineTextHTMLParsedClass, &MIME_SUPERCLASS);
static int MimeInlineTextHTMLParsed_parse_line(const char *, int32_t,
MimeObject *);
static int MimeInlineTextHTMLParsed_parse_begin(MimeObject *obj);
static int MimeInlineTextHTMLParsed_parse_eof(MimeObject *, bool);
static void MimeInlineTextHTMLParsed_finalize(MimeObject *obj);
static int
MimeInlineTextHTMLParsedClassInitialize(MimeInlineTextHTMLParsedClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *)clazz;
NS_ASSERTION(!oclass->class_initialized, "problem with superclass");
oclass->parse_line = MimeInlineTextHTMLParsed_parse_line;
oclass->parse_begin = MimeInlineTextHTMLParsed_parse_begin;
oclass->parse_eof = MimeInlineTextHTMLParsed_parse_eof;
oclass->finalize = MimeInlineTextHTMLParsed_finalize;
return 0;
}
static int
MimeInlineTextHTMLParsed_parse_begin(MimeObject *obj)
{
MimeInlineTextHTMLParsed *me = (MimeInlineTextHTMLParsed *)obj;
me->complete_buffer = new nsString();
int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
if (status < 0)
return status;
return 0;
}
static int
MimeInlineTextHTMLParsed_parse_eof(MimeObject *obj, bool abort_p)
{
if (obj->closed_p)
return 0;
int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0)
return status;
MimeInlineTextHTMLParsed *me = (MimeInlineTextHTMLParsed *)obj;
// We have to cache all lines and parse the whole document at once.
// There's a useful sounding function parseFromStream(), but it only allows XML
// mimetypes, not HTML. Methinks that's because the HTML soup parser
// needs the entire doc to make sense of the gibberish that people write.
if (!me || !me->complete_buffer)
return 0;
nsString& rawHTML = *(me->complete_buffer);
if (rawHTML.IsEmpty())
return 0;
nsString parsed;
nsresult rv;
// Parse the HTML source.
nsCOMPtr<nsIDOMDocument> document;
nsCOMPtr<nsIDOMParser> parser = do_GetService(NS_DOMPARSER_CONTRACTID);
rv = parser->ParseFromString(rawHTML.get(), "text/html",
getter_AddRefs(document));
NS_ENSURE_SUCCESS(rv, -1);
// Serialize it back to HTML source again.
nsCOMPtr<nsIDocumentEncoder> encoder = do_CreateInstance(
"@mozilla.org/layout/documentEncoder;1?type=text/html");
uint32_t aFlags = nsIDocumentEncoder::OutputRaw |
nsIDocumentEncoder::OutputDisallowLineBreaking;
rv = encoder->Init(document, NS_LITERAL_STRING("text/html"), aFlags);
NS_ENSURE_SUCCESS(rv, -1);
rv = encoder->EncodeToString(parsed);
NS_ENSURE_SUCCESS(rv, -1);
// Write it out.
NS_ConvertUTF16toUTF8 resultCStr(parsed);
MimeInlineTextHTML_insert_lang_div(obj, resultCStr);
MimeInlineTextHTML_remove_plaintext_tag(obj, resultCStr);
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_line(
resultCStr.BeginWriting(), resultCStr.Length(), obj);
rawHTML.Truncate();
return status;
}
void
MimeInlineTextHTMLParsed_finalize(MimeObject *obj)
{
MimeInlineTextHTMLParsed *me = (MimeInlineTextHTMLParsed *)obj;
if (me && me->complete_buffer)
{
obj->clazz->parse_eof(obj, false);
delete me->complete_buffer;
me->complete_buffer = NULL;
}
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj);
}
static int
MimeInlineTextHTMLParsed_parse_line(const char *line, int32_t length,
MimeObject *obj)
{
MimeInlineTextHTMLParsed *me = (MimeInlineTextHTMLParsed *)obj;
if (!me || !(me->complete_buffer))
return -1;
nsCString linestr(line, length);
NS_ConvertUTF8toUTF16 line_ucs2(linestr.get());
if (length && line_ucs2.IsEmpty())
CopyASCIItoUTF16(linestr, line_ucs2);
(me->complete_buffer)->Append(line_ucs2);
return 0;
}

View file

@ -0,0 +1,28 @@
/* -*- 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/. */
#ifndef _MIMETEXTHTMLPARSED_H_
#define _MIMETEXTHTMLPARSED_H_
#include "mimethtm.h"
typedef struct MimeInlineTextHTMLParsedClass MimeInlineTextHTMLParsedClass;
typedef struct MimeInlineTextHTMLParsed MimeInlineTextHTMLParsed;
struct MimeInlineTextHTMLParsedClass {
MimeInlineTextHTMLClass html;
};
extern MimeInlineTextHTMLParsedClass mimeInlineTextHTMLParsedClass;
struct MimeInlineTextHTMLParsed {
MimeInlineTextHTML html;
nsString *complete_buffer; // Gecko parser expects wide strings
};
#define MimeInlineTextHTMLParsedClassInitializer(ITYPE,CSUPER) \
{ MimeInlineTextHTMLClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMETEXTHTMLPARSED_H_ */

View file

@ -0,0 +1,249 @@
/* -*- 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/.
* This Original Code has been modified by IBM Corporation. Modifications made by IBM
* described herein are Copyright (c) International Business Machines Corporation, 2000.
* Modifications to Mozilla code or documentation identified per MPL Section 3.3
*
* Date Modified by Description of modification
* 04/20/2000 IBM Corp. OS/2 VisualAge build.
*/
/*
* mimebuf.c - libmsg like buffer handling routines for libmime
*/
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "msgCore.h"
#include "nsMimeStringResources.h"
extern "C" int
mime_GrowBuffer (uint32_t desired_size, uint32_t element_size, uint32_t quantum,
char **buffer, int32_t *size)
{
if ((uint32_t) *size <= desired_size)
{
char *new_buf;
uint32_t increment = desired_size - *size;
if (increment < quantum) /* always grow by a minimum of N bytes */
increment = quantum;
new_buf = (*buffer
? (char *) PR_Realloc (*buffer, (*size + increment)
* (element_size / sizeof(char)))
: (char *) PR_MALLOC ((*size + increment)
* (element_size / sizeof(char))));
if (! new_buf)
return MIME_OUT_OF_MEMORY;
*buffer = new_buf;
*size += increment;
}
return 0;
}
/* The opposite of mime_LineBuffer(): takes small buffers and packs them
up into bigger buffers before passing them along.
Pass in a desired_buffer_size 0 to tell it to flush (for example, in
in the very last call to this function.)
*/
extern "C" int
mime_ReBuffer (const char *net_buffer, int32_t net_buffer_size,
uint32_t desired_buffer_size,
char **bufferP, int32_t *buffer_sizeP, uint32_t *buffer_fpP,
int32_t (*per_buffer_fn) (char *buffer, uint32_t buffer_size,
void *closure),
void *closure)
{
int status = 0;
if (desired_buffer_size >= (uint32_t) (*buffer_sizeP))
{
status = mime_GrowBuffer (desired_buffer_size, sizeof(char), 1024,
bufferP, buffer_sizeP);
if (status < 0) return status;
}
do
{
int32_t size = *buffer_sizeP - *buffer_fpP;
if (size > net_buffer_size)
size = net_buffer_size;
if (size > 0)
{
memcpy ((*bufferP) + (*buffer_fpP), net_buffer, size);
(*buffer_fpP) += size;
net_buffer += size;
net_buffer_size -= size;
}
if (*buffer_fpP > 0 &&
*buffer_fpP >= desired_buffer_size)
{
status = (*per_buffer_fn) ((*bufferP), (*buffer_fpP), closure);
*buffer_fpP = 0;
if (status < 0) return status;
}
}
while (net_buffer_size > 0);
return 0;
}
static int
convert_and_send_buffer(char* buf, int length, bool convert_newlines_p,
int32_t (* per_line_fn) (char *line,
uint32_t line_length,
void *closure),
void *closure)
{
/* Convert the line terminator to the native form.
*/
char* newline;
#if (MSG_LINEBREAK_LEN == 2)
/***
* This is a patch to support a mail DB corruption cause by earlier version that lead to a crash.
* What happened is that the line terminator is CR+NULL+LF. Therefore, we first process a line
* terminated by CR then a second line that contains only NULL+LF. We need to ignore this second
* line. See bug http://bugzilla.mozilla.org/show_bug.cgi?id=61412 for more information.
***/
if (length == 2 && buf[0] == 0x00 && buf[1] == '\n')
return 0;
#endif
NS_ASSERTION(buf && length > 0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (!buf || length <= 0) return -1;
newline = buf + length;
NS_ASSERTION(newline[-1] == '\r' || newline[-1] == '\n', "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (newline[-1] != '\r' && newline[-1] != '\n') return -1;
if (!convert_newlines_p)
{
}
#if (MSG_LINEBREAK_LEN == 1)
else if ((newline - buf) >= 2 &&
newline[-2] == '\r' &&
newline[-1] == '\n')
{
/* CRLF -> CR or LF */
buf [length - 2] = MSG_LINEBREAK[0];
length--;
}
else if (newline > buf + 1 &&
newline[-1] != MSG_LINEBREAK[0])
{
/* CR -> LF or LF -> CR */
buf [length - 1] = MSG_LINEBREAK[0];
}
#else
else if (((newline - buf) >= 2 && newline[-2] != '\r') ||
((newline - buf) >= 1 && newline[-1] != '\n'))
{
/* LF -> CRLF or CR -> CRLF */
length++;
buf[length - 2] = MSG_LINEBREAK[0];
buf[length - 1] = MSG_LINEBREAK[1];
}
#endif
return (*per_line_fn)(buf, length, closure);
}
extern "C" int
mime_LineBuffer (const char *net_buffer, int32_t net_buffer_size,
char **bufferP, int32_t *buffer_sizeP, uint32_t *buffer_fpP,
bool convert_newlines_p,
int32_t (* per_line_fn) (char *line, uint32_t line_length,
void *closure),
void *closure)
{
int status = 0;
if (*buffer_fpP > 0 && *bufferP && (*bufferP)[*buffer_fpP - 1] == '\r' &&
net_buffer_size > 0 && net_buffer[0] != '\n') {
/* The last buffer ended with a CR. The new buffer does not start
with a LF. This old buffer should be shipped out and discarded. */
NS_ASSERTION((uint32_t) *buffer_sizeP > *buffer_fpP, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if ((uint32_t) *buffer_sizeP <= *buffer_fpP) return -1;
status = convert_and_send_buffer(*bufferP, *buffer_fpP,
convert_newlines_p,
per_line_fn, closure);
if (status < 0) return status;
*buffer_fpP = 0;
}
while (net_buffer_size > 0)
{
const char *net_buffer_end = net_buffer + net_buffer_size;
const char *newline = 0;
const char *s;
for (s = net_buffer; s < net_buffer_end; s++)
{
/* Move forward in the buffer until the first newline.
Stop when we see CRLF, CR, or LF, or the end of the buffer.
*But*, if we see a lone CR at the *very end* of the buffer,
treat this as if we had reached the end of the buffer without
seeing a line terminator. This is to catch the case of the
buffers splitting a CRLF pair, as in "FOO\r\nBAR\r" "\nBAZ\r\n".
*/
if (*s == '\r' || *s == '\n')
{
newline = s;
if (newline[0] == '\r')
{
if (s == net_buffer_end - 1)
{
/* CR at end - wait for the next character. */
newline = 0;
break;
}
else if (newline[1] == '\n')
/* CRLF seen; swallow both. */
newline++;
}
newline++;
break;
}
}
/* Ensure room in the net_buffer and append some or all of the current
chunk of data to it. */
{
const char *end = (newline ? newline : net_buffer_end);
uint32_t desired_size = (end - net_buffer) + (*buffer_fpP) + 1;
if (desired_size >= (uint32_t) (*buffer_sizeP))
{
status = mime_GrowBuffer (desired_size, sizeof(char), 1024,
bufferP, buffer_sizeP);
if (status < 0) return status;
}
memcpy ((*bufferP) + (*buffer_fpP), net_buffer, (end - net_buffer));
(*buffer_fpP) += (end - net_buffer);
(*bufferP)[*buffer_fpP] = '\0';
}
/* Now *bufferP contains either a complete line, or as complete
a line as we have read so far.
If we have a line, process it, and then remove it from `*bufferP'.
Then go around the loop again, until we drain the incoming data.
*/
if (!newline)
return 0;
status = convert_and_send_buffer(*bufferP, *buffer_fpP,
convert_newlines_p,
per_line_fn, closure);
if (status < 0)
return status;
net_buffer_size -= (newline - net_buffer);
net_buffer = newline;
(*buffer_fpP) = 0;
}
return 0;
}

View file

@ -0,0 +1,39 @@
/* -*- 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/.
* This Original Code has been modified by IBM Corporation. Modifications made by IBM
* described herein are Copyright (c) International Business Machines Corporation, 2000.
* Modifications to Mozilla code or documentation identified per MPL Section 3.3
*
* Date Modified by Description of modification
* 04/20/2000 IBM Corp. OS/2 VisualAge build.
*/
#ifndef _MIMEBUF_H_
#define _MIMEBUF_H_
extern "C" int mime_GrowBuffer (uint32_t desired_size,
uint32_t element_size, uint32_t quantum,
char **buffer, int32_t *size);
extern "C" int mime_LineBuffer (const char *net_buffer, int32_t net_buffer_size,
char **bufferP, int32_t *buffer_sizeP,
int32_t *buffer_fpP,
bool convert_newlines_p,
int32_t (* per_line_fn) (char *line, int32_t
line_length, void *closure),
void *closure);
extern "C" int mime_ReBuffer (const char *net_buffer, int32_t net_buffer_size,
uint32_t desired_buffer_size,
char **bufferP, uint32_t *buffer_sizeP,
uint32_t *buffer_fpP,
int32_t (*per_buffer_fn) (char *buffer,
uint32_t buffer_size,
void *closure),
void *closure);
#endif /* _MIMEBUF_H_ */

View file

@ -0,0 +1,716 @@
/* -*- Mode: C++; tab-width: 2; 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 "nsICMSMessage.h"
#include "nsICMSMessage2.h"
#include "nsICMSMessageErrors.h"
#include "nsICMSDecoder.h"
#include "mimecms.h"
#include "mimemsig.h"
#include "nspr.h"
#include "mimemsg.h"
#include "mimemoz2.h"
#include "nsIURI.h"
#include "nsIMsgWindow.h"
#include "nsIMsgMailNewsUrl.h"
#include "nsIMimeMiscStatus.h"
#include "nsIMsgSMIMEHeaderSink.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsIX509Cert.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsProxyRelease.h"
#include "mozilla/mailnews/MimeHeaderParser.h"
using namespace mozilla::mailnews;
#define MIME_SUPERCLASS mimeEncryptedClass
MimeDefClass(MimeEncryptedCMS, MimeEncryptedCMSClass,
mimeEncryptedCMSClass, &MIME_SUPERCLASS);
static void *MimeCMS_init(MimeObject *, int (*output_fn) (const char *, int32_t, void *), void *);
static int MimeCMS_write (const char *, int32_t, void *);
static int MimeCMS_eof (void *, bool);
static char * MimeCMS_generate (void *);
static void MimeCMS_free (void *);
extern int SEC_ERROR_CERT_ADDR_MISMATCH;
static int MimeEncryptedCMSClassInitialize(MimeEncryptedCMSClass *clazz)
{
#ifdef DEBUG
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
NS_ASSERTION(!oclass->class_initialized, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
#endif
MimeEncryptedClass *eclass = (MimeEncryptedClass *) clazz;
eclass->crypto_init = MimeCMS_init;
eclass->crypto_write = MimeCMS_write;
eclass->crypto_eof = MimeCMS_eof;
eclass->crypto_generate_html = MimeCMS_generate;
eclass->crypto_free = MimeCMS_free;
return 0;
}
typedef struct MimeCMSdata
{
int (*output_fn) (const char *buf, int32_t buf_size, void *output_closure);
void *output_closure;
nsCOMPtr<nsICMSDecoder> decoder_context;
nsCOMPtr<nsICMSMessage> content_info;
bool ci_is_encrypted;
char *sender_addr;
bool decoding_failed;
uint32_t decoded_bytes;
MimeObject *self;
bool parent_is_encrypted_p;
bool parent_holds_stamp_p;
nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink;
MimeCMSdata()
:output_fn(nullptr),
output_closure(nullptr),
ci_is_encrypted(false),
sender_addr(nullptr),
decoding_failed(false),
decoded_bytes(0),
self(nullptr),
parent_is_encrypted_p(false),
parent_holds_stamp_p(false)
{
}
~MimeCMSdata()
{
if(sender_addr)
PR_Free(sender_addr);
// Do an orderly release of nsICMSDecoder and nsICMSMessage //
if (decoder_context)
{
nsCOMPtr<nsICMSMessage> cinfo;
decoder_context->Finish(getter_AddRefs(cinfo));
}
}
} MimeCMSdata;
/* SEC_PKCS7DecoderContentCallback for SEC_PKCS7DecoderStart() */
static void MimeCMS_content_callback (void *arg, const char *buf, unsigned long length)
{
int status;
MimeCMSdata *data = (MimeCMSdata *) arg;
if (!data) return;
if (!data->output_fn)
return;
PR_SetError(0,0);
status = data->output_fn (buf, length, data->output_closure);
if (status < 0)
{
PR_SetError(status, 0);
data->output_fn = 0;
return;
}
data->decoded_bytes += length;
}
bool MimeEncryptedCMS_encrypted_p (MimeObject *obj)
{
bool encrypted;
if (!obj) return false;
if (mime_typep(obj, (MimeObjectClass *) &mimeEncryptedCMSClass))
{
MimeEncrypted *enc = (MimeEncrypted *) obj;
MimeCMSdata *data = (MimeCMSdata *) enc->crypto_closure;
if (!data || !data->content_info) return false;
data->content_info->ContentIsEncrypted(&encrypted);
return encrypted;
}
return false;
}
bool MimeCMSHeadersAndCertsMatch(nsICMSMessage *content_info,
nsIX509Cert *signerCert,
const char *from_addr,
const char *from_name,
const char *sender_addr,
const char *sender_name,
bool *signing_cert_without_email_address)
{
nsCString cert_addr;
bool match = true;
bool foundFrom = false;
bool foundSender = false;
/* Find the name and address in the cert.
*/
if (content_info)
{
// Extract any address contained in the cert.
// This will be used for testing, whether the cert contains no addresses at all.
content_info->GetSignerEmailAddress (getter_Copies(cert_addr));
}
if (signing_cert_without_email_address)
*signing_cert_without_email_address = cert_addr.IsEmpty();
/* Now compare them --
consider it a match if the address in the cert matches the
address in the From field (or as a fallback, the Sender field)
*/
/* If there is no addr in the cert at all, it can not match and we fail. */
if (cert_addr.IsEmpty())
{
match = false;
}
else
{
if (signerCert)
{
if (from_addr && *from_addr)
{
NS_ConvertASCIItoUTF16 ucs2From(from_addr);
if (NS_FAILED(signerCert->ContainsEmailAddress(ucs2From, &foundFrom)))
{
foundFrom = false;
}
}
else if (sender_addr && *sender_addr)
{
NS_ConvertASCIItoUTF16 ucs2Sender(sender_addr);
if (NS_FAILED(signerCert->ContainsEmailAddress(ucs2Sender, &foundSender)))
{
foundSender = false;
}
}
}
if (!foundSender && !foundFrom)
{
match = false;
}
}
return match;
}
class nsSMimeVerificationListener : public nsISMimeVerificationListener
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISMIMEVERIFICATIONLISTENER
nsSMimeVerificationListener(const char *aFromAddr, const char *aFromName,
const char *aSenderAddr, const char *aSenderName,
nsIMsgSMIMEHeaderSink *aHeaderSink, int32_t aMimeNestingLevel);
protected:
virtual ~nsSMimeVerificationListener() {}
/**
* It is safe to declare this implementation as thread safe,
* despite not using a lock to protect the members.
* Because of the way the object will be used, we don't expect a race.
* After construction, the object is passed to another thread,
* but will no longer be accessed on the original thread.
* The other thread is unable to access/modify self's data members.
* When the other thread is finished, it will call into the "Notify"
* callback. Self's members will be accessed on the other thread,
* but this is fine, because there is no race with the original thread.
* Race-protection for XPCOM reference counting is sufficient.
*/
bool mSinkIsNull;
nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> mHeaderSink;
int32_t mMimeNestingLevel;
nsCString mFromAddr;
nsCString mFromName;
nsCString mSenderAddr;
nsCString mSenderName;
};
class SignedStatusRunnable : public mozilla::Runnable
{
public:
SignedStatusRunnable(const nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> &aSink, int32_t aNestingLevel,
int32_t aSignatureStatus, nsIX509Cert *aSignerCert);
NS_DECL_NSIRUNNABLE
protected:
nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> m_sink;
int32_t m_nestingLevel;
int32_t m_signatureStatus;
nsCOMPtr<nsIX509Cert> m_signerCert;
};
SignedStatusRunnable::SignedStatusRunnable(const nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> &aSink,
int32_t aNestingLevel,
int32_t aSignatureStatus,
nsIX509Cert *aSignerCert) :
m_sink(aSink), m_nestingLevel(aNestingLevel),
m_signatureStatus(aSignatureStatus), m_signerCert(aSignerCert)
{
}
NS_IMETHODIMP SignedStatusRunnable::Run()
{
return m_sink->SignedStatus(m_nestingLevel, m_signatureStatus, m_signerCert);
}
nsresult ProxySignedStatus(const nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> &aSink,
int32_t aNestingLevel,
int32_t aSignatureStatus,
nsIX509Cert *aSignerCert)
{
RefPtr<SignedStatusRunnable> signedStatus =
new SignedStatusRunnable(aSink, aNestingLevel, aSignatureStatus, aSignerCert);
return NS_DispatchToMainThread(signedStatus, NS_DISPATCH_SYNC);
}
NS_IMPL_ISUPPORTS(nsSMimeVerificationListener, nsISMimeVerificationListener)
nsSMimeVerificationListener::nsSMimeVerificationListener(const char *aFromAddr, const char *aFromName,
const char *aSenderAddr, const char *aSenderName,
nsIMsgSMIMEHeaderSink *aHeaderSink, int32_t aMimeNestingLevel)
{
mHeaderSink = new nsMainThreadPtrHolder<nsIMsgSMIMEHeaderSink>(aHeaderSink);
mSinkIsNull = !aHeaderSink;
mMimeNestingLevel = aMimeNestingLevel;
mFromAddr = aFromAddr;
mFromName = aFromName;
mSenderAddr = aSenderAddr;
mSenderName = aSenderName;
}
NS_IMETHODIMP nsSMimeVerificationListener::Notify(nsICMSMessage2 *aVerifiedMessage,
nsresult aVerificationResultCode)
{
// Only continue if we have a valid pointer to the UI
NS_ENSURE_FALSE(mSinkIsNull, NS_OK);
NS_ENSURE_TRUE(aVerifiedMessage, NS_ERROR_FAILURE);
nsCOMPtr<nsICMSMessage> msg = do_QueryInterface(aVerifiedMessage);
NS_ENSURE_TRUE(msg, NS_ERROR_FAILURE);
nsCOMPtr<nsIX509Cert> signerCert;
msg->GetSignerCert(getter_AddRefs(signerCert));
int32_t signature_status = nsICMSMessageErrors::GENERAL_ERROR;
if (NS_FAILED(aVerificationResultCode))
{
if (NS_ERROR_MODULE_SECURITY == NS_ERROR_GET_MODULE(aVerificationResultCode))
signature_status = NS_ERROR_GET_CODE(aVerificationResultCode);
else if (NS_ERROR_NOT_IMPLEMENTED == aVerificationResultCode)
signature_status = nsICMSMessageErrors::VERIFY_ERROR_PROCESSING;
}
else
{
bool signing_cert_without_email_address;
bool good_p = MimeCMSHeadersAndCertsMatch(msg, signerCert,
mFromAddr.get(), mFromName.get(),
mSenderAddr.get(), mSenderName.get(),
&signing_cert_without_email_address);
if (!good_p)
{
if (signing_cert_without_email_address)
signature_status = nsICMSMessageErrors::VERIFY_CERT_WITHOUT_ADDRESS;
else
signature_status = nsICMSMessageErrors::VERIFY_HEADER_MISMATCH;
}
else
signature_status = nsICMSMessageErrors::SUCCESS;
}
ProxySignedStatus(mHeaderSink, mMimeNestingLevel, signature_status, signerCert);
return NS_OK;
}
int MIMEGetRelativeCryptoNestLevel(MimeObject *obj)
{
/*
the part id of any mimeobj is mime_part_address(obj)
our currently displayed crypto part is obj
the part shown as the toplevel object in the current window is
obj->options->part_to_load
possibly stored in the toplevel object only ???
but hopefully all nested mimeobject point to the same displayooptions
we need to find out the nesting level of our currently displayed crypto object
wrt the shown part in the toplevel window
*/
// if we are showing the toplevel message, aTopMessageNestLevel == 0
int aTopMessageNestLevel = 0;
MimeObject *aTopShownObject = nullptr;
if (obj && obj->options->part_to_load) {
bool aAlreadyFoundTop = false;
for (MimeObject *walker = obj; walker; walker = walker->parent) {
if (aAlreadyFoundTop) {
if (!mime_typep(walker, (MimeObjectClass *) &mimeEncryptedClass)
&& !mime_typep(walker, (MimeObjectClass *) &mimeMultipartSignedClass)) {
++aTopMessageNestLevel;
}
}
if (!aAlreadyFoundTop && !strcmp(mime_part_address(walker), walker->options->part_to_load)) {
aAlreadyFoundTop = true;
aTopShownObject = walker;
}
if (!aAlreadyFoundTop && !walker->parent) {
// The mime part part_to_load is not a parent of the
// the crypto mime part passed in to this function as parameter obj.
// That means the crypto part belongs to another branch of the mime tree.
return -1;
}
}
}
bool CryptoObjectIsChildOfTopShownObject = false;
if (!aTopShownObject) {
// no sub part specified, top message is displayed, and
// our crypto object is definitively a child of it
CryptoObjectIsChildOfTopShownObject = true;
}
// if we are the child of the topmost message, aCryptoPartNestLevel == 1
int aCryptoPartNestLevel = 0;
if (obj) {
for (MimeObject *walker = obj; walker; walker = walker->parent) {
// Crypto mime objects are transparent wrt nesting.
if (!mime_typep(walker, (MimeObjectClass *) &mimeEncryptedClass)
&& !mime_typep(walker, (MimeObjectClass *) &mimeMultipartSignedClass)) {
++aCryptoPartNestLevel;
}
if (aTopShownObject && walker->parent == aTopShownObject) {
CryptoObjectIsChildOfTopShownObject = true;
}
}
}
if (!CryptoObjectIsChildOfTopShownObject) {
return -1;
}
return aCryptoPartNestLevel - aTopMessageNestLevel;
}
static void *MimeCMS_init(MimeObject *obj,
int (*output_fn) (const char *buf, int32_t buf_size, void *output_closure),
void *output_closure)
{
MimeCMSdata *data;
nsresult rv;
if (!(obj && obj->options && output_fn)) return 0;
data = new MimeCMSdata;
if (!data) return 0;
data->self = obj;
data->output_fn = output_fn;
data->output_closure = output_closure;
PR_SetError(0, 0);
data->decoder_context = do_CreateInstance(NS_CMSDECODER_CONTRACTID, &rv);
if (NS_FAILED(rv))
{
delete data;
return 0;
}
rv = data->decoder_context->Start(MimeCMS_content_callback, data);
if (NS_FAILED(rv))
{
delete data;
return 0;
}
// XXX Fix later XXX //
data->parent_holds_stamp_p =
(obj->parent &&
(mime_crypto_stamped_p(obj->parent) ||
mime_typep(obj->parent, (MimeObjectClass *) &mimeEncryptedClass)));
data->parent_is_encrypted_p =
(obj->parent && MimeEncryptedCMS_encrypted_p (obj->parent));
/* If the parent of this object is a crypto-blob, then it's the grandparent
who would have written out the headers and prepared for a stamp...
(This shit sucks.)
*/
if (data->parent_is_encrypted_p &&
!data->parent_holds_stamp_p &&
obj->parent && obj->parent->parent)
data->parent_holds_stamp_p =
mime_crypto_stamped_p (obj->parent->parent);
mime_stream_data *msd = (mime_stream_data *) (data->self->options->stream_closure);
if (msd)
{
nsIChannel *channel = msd->channel; // note the lack of ref counting...
if (channel)
{
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIMsgWindow> msgWindow;
nsCOMPtr<nsIMsgHeaderSink> headerSink;
nsCOMPtr<nsIMsgMailNewsUrl> msgurl;
nsCOMPtr<nsISupports> securityInfo;
channel->GetURI(getter_AddRefs(uri));
if (uri)
{
nsAutoCString urlSpec;
rv = uri->GetSpec(urlSpec);
// We only want to update the UI if the current mime transaction
// is intended for display.
// If the current transaction is intended for background processing,
// we can learn that by looking at the additional header=filter
// string contained in the URI.
//
// If we find something, we do not set smimeHeaderSink,
// which will prevent us from giving UI feedback.
//
// If we do not find header=filter, we assume the result of the
// processing will be shown in the UI.
if (!strstr(urlSpec.get(), "?header=filter") &&
!strstr(urlSpec.get(), "&header=filter") &&
!strstr(urlSpec.get(), "?header=attach") &&
!strstr(urlSpec.get(), "&header=attach"))
{
msgurl = do_QueryInterface(uri);
if (msgurl)
msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
if (msgWindow)
msgWindow->GetMsgHeaderSink(getter_AddRefs(headerSink));
if (headerSink)
headerSink->GetSecurityInfo(getter_AddRefs(securityInfo));
if (securityInfo)
data->smimeHeaderSink = do_QueryInterface(securityInfo);
}
}
} // if channel
} // if msd
return data;
}
static int
MimeCMS_write (const char *buf, int32_t buf_size, void *closure)
{
MimeCMSdata *data = (MimeCMSdata *) closure;
nsresult rv;
if (!data || !data->output_fn || !data->decoder_context) return -1;
PR_SetError(0, 0);
rv = data->decoder_context->Update(buf, buf_size);
data->decoding_failed = NS_FAILED(rv);
return 0;
}
void MimeCMSGetFromSender(MimeObject *obj,
nsCString &from_addr,
nsCString &from_name,
nsCString &sender_addr,
nsCString &sender_name)
{
MimeHeaders *msg_headers = 0;
/* Find the headers of the MimeMessage which is the parent (or grandparent)
of this object (remember, crypto objects nest.) */
MimeObject *o2 = obj;
msg_headers = o2->headers;
while (o2 &&
o2->parent &&
!mime_typep(o2->parent, (MimeObjectClass *) &mimeMessageClass))
{
o2 = o2->parent;
msg_headers = o2->headers;
}
if (!msg_headers)
return;
/* Find the names and addresses in the From and/or Sender fields.
*/
nsCString s;
/* Extract the name and address of the "From:" field. */
s.Adopt(MimeHeaders_get(msg_headers, HEADER_FROM, false, false));
if (!s.IsEmpty())
ExtractFirstAddress(EncodedHeader(s), from_name, from_addr);
/* Extract the name and address of the "Sender:" field. */
s.Adopt(MimeHeaders_get(msg_headers, HEADER_SENDER, false, false));
if (!s.IsEmpty())
ExtractFirstAddress(EncodedHeader(s), sender_name, sender_addr);
}
void MimeCMSRequestAsyncSignatureVerification(nsICMSMessage *aCMSMsg,
const char *aFromAddr, const char *aFromName,
const char *aSenderAddr, const char *aSenderName,
nsIMsgSMIMEHeaderSink *aHeaderSink, int32_t aMimeNestingLevel,
unsigned char* item_data, uint32_t item_len)
{
nsCOMPtr<nsICMSMessage2> msg2 = do_QueryInterface(aCMSMsg);
if (!msg2)
return;
RefPtr<nsSMimeVerificationListener> listener =
new nsSMimeVerificationListener(aFromAddr, aFromName, aSenderAddr, aSenderName,
aHeaderSink, aMimeNestingLevel);
if (!listener)
return;
if (item_data)
msg2->AsyncVerifyDetachedSignature(listener, item_data, item_len);
else
msg2->AsyncVerifySignature(listener);
}
static int
MimeCMS_eof (void *crypto_closure, bool abort_p)
{
MimeCMSdata *data = (MimeCMSdata *) crypto_closure;
nsresult rv;
int32_t status = nsICMSMessageErrors::SUCCESS;
if (!data || !data->output_fn || !data->decoder_context) {
return -1;
}
int aRelativeNestLevel = MIMEGetRelativeCryptoNestLevel(data->self);
/* Hand an EOF to the crypto library. It may call data->output_fn.
(Today, the crypto library has no flushing to do, but maybe there
will be someday.)
We save away the value returned and will use it later to emit a
blurb about whether the signature validation was cool.
*/
PR_SetError(0, 0);
rv = data->decoder_context->Finish(getter_AddRefs(data->content_info));
if (NS_FAILED(rv))
status = nsICMSMessageErrors::GENERAL_ERROR;
data->decoder_context = nullptr;
nsCOMPtr<nsIX509Cert> certOfInterest;
if (!data->smimeHeaderSink)
return 0;
if (aRelativeNestLevel < 0)
return 0;
int32_t maxNestLevel = 0;
data->smimeHeaderSink->MaxWantedNesting(&maxNestLevel);
if (aRelativeNestLevel > maxNestLevel)
return 0;
if (data->decoding_failed)
status = nsICMSMessageErrors::GENERAL_ERROR;
if (!data->content_info)
{
if (!data->decoded_bytes)
{
// We were unable to decode any data.
status = nsICMSMessageErrors::GENERAL_ERROR;
}
else
{
// Some content got decoded, but we failed to decode
// the final summary, probably we got truncated data.
status = nsICMSMessageErrors::ENCRYPT_INCOMPLETE;
}
// Although a CMS message could be either encrypted or opaquely signed,
// what we see is most likely encrypted, because if it were
// signed only, we probably would have been able to decode it.
data->ci_is_encrypted = true;
}
else
{
rv = data->content_info->ContentIsEncrypted(&data->ci_is_encrypted);
if (NS_SUCCEEDED(rv) && data->ci_is_encrypted) {
data->content_info->GetEncryptionCert(getter_AddRefs(certOfInterest));
}
else {
// Existing logic in mimei assumes, if !ci_is_encrypted, then it is signed.
// Make sure it indeed is signed.
bool testIsSigned;
rv = data->content_info->ContentIsSigned(&testIsSigned);
if (NS_FAILED(rv) || !testIsSigned) {
// Neither signed nor encrypted?
// We are unable to understand what we got, do not try to indicate S/Mime status.
return 0;
}
nsCString from_addr;
nsCString from_name;
nsCString sender_addr;
nsCString sender_name;
MimeCMSGetFromSender(data->self,
from_addr, from_name,
sender_addr, sender_name);
MimeCMSRequestAsyncSignatureVerification(data->content_info,
from_addr.get(), from_name.get(),
sender_addr.get(), sender_name.get(),
data->smimeHeaderSink, aRelativeNestLevel,
nullptr, 0);
}
}
if (data->ci_is_encrypted)
{
data->smimeHeaderSink->EncryptionStatus(
aRelativeNestLevel,
status,
certOfInterest
);
}
return 0;
}
static void
MimeCMS_free (void *crypto_closure)
{
MimeCMSdata *data = (MimeCMSdata *) crypto_closure;
if (!data) return;
delete data;
}
static char *
MimeCMS_generate (void *crypto_closure)
{
return nullptr;
}

View file

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef _MIMECMS_H_
#define _MIMECMS_H_
#include "mimecryp.h"
class nsICMSMessage;
/* The MimeEncryptedCMS class implements a type of MIME object where the
object is passed through a CMS decryption engine to decrypt or verify
signatures. That module returns a new MIME object, which is then presented
to the user. See mimecryp.h for details of the general mechanism on which
this is built.
*/
typedef struct MimeEncryptedCMSClass MimeEncryptedCMSClass;
typedef struct MimeEncryptedCMS MimeEncryptedCMS;
struct MimeEncryptedCMSClass {
MimeEncryptedClass encrypted;
};
extern MimeEncryptedCMSClass mimeEncryptedCMSClass;
struct MimeEncryptedCMS {
MimeEncrypted encrypted; /* superclass variables */
};
#define MimeEncryptedCMSClassInitializer(ITYPE,CSUPER) \
{ MimeEncryptedClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEPKCS_H_ */

View file

@ -0,0 +1,74 @@
/* -*- 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 "mimei.h"
#include "mimeobj.h" /* MimeObject (abstract) */
#include "mimecont.h" /* |--- MimeContainer (abstract) */
#include "mimemult.h" /* | |--- MimeMultipart (abstract) */
#include "mimemsig.h" /* | | |--- MimeMultipartSigned (abstract)*/
#include "mimetext.h" /* | |--- MimeInlineText (abstract) */
#include "mimecryp.h"
#include "mimecth.h"
/*
* These calls are necessary to expose the object class hierarchy
* to externally developed content type handlers.
*/
extern "C" void *
XPCOM_GetmimeInlineTextClass(void)
{
return (void *) &mimeInlineTextClass;
}
extern "C" void *
XPCOM_GetmimeLeafClass(void)
{
return (void *) &mimeLeafClass;
}
extern "C" void *
XPCOM_GetmimeObjectClass(void)
{
return (void *) &mimeObjectClass;
}
extern "C" void *
XPCOM_GetmimeContainerClass(void)
{
return (void *) &mimeContainerClass;
}
extern "C" void *
XPCOM_GetmimeMultipartClass(void)
{
return (void *) &mimeMultipartClass;
}
extern "C" void *
XPCOM_GetmimeMultipartSignedClass(void)
{
return (void *) &mimeMultipartSignedClass;
}
extern "C" void *
XPCOM_GetmimeEncryptedClass(void)
{
return (void *) &mimeEncryptedClass;
}
extern "C" int
XPCOM_MimeObject_write(void *mimeObject,
char *data,
int32_t length,
bool user_visible_p)
{
return MIME_MimeObject_write((MimeObject *)mimeObject, data,
length, user_visible_p);
}
extern "C" void *
XPCOM_Mime_create(char *content_type, void* hdrs, void* opts)
{
return mime_create(content_type, (MimeHeaders *)hdrs, (MimeDisplayOptions *)opts);
}

View file

@ -0,0 +1,38 @@
/* -*- 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/. */
/*
* XP-COM Bridges for C function calls
*/
#ifndef _MIMECOM_H_
#define _MIMECOM_H_
#include <stdint.h>
/*
* These functions are exposed by libmime to be used by content type
* handler plugins for processing stream data.
*/
/*
* This is the write call for outputting processed stream data.
*/
extern "C" int XPCOM_MimeObject_write(void *mimeObject, const char *data,
int32_t length,
bool user_visible_p);
/*
* The following group of calls expose the pointers for the object
* system within libmime.
*/
extern "C" void *XPCOM_GetmimeInlineTextClass(void);
extern "C" void *XPCOM_GetmimeLeafClass(void);
extern "C" void *XPCOM_GetmimeObjectClass(void);
extern "C" void *XPCOM_GetmimeContainerClass(void);
extern "C" void *XPCOM_GetmimeMultipartClass(void);
extern "C" void *XPCOM_GetmimeMultipartSignedClass(void);
extern "C" void *XPCOM_GetmimeEncryptedClass(void);
extern "C" void *XPCOM_Mime_create(char *content_type, void* hdrs, void* opts);
#endif /* _MIMECOM_H_ */

View file

@ -0,0 +1,218 @@
/* -*- 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 "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "prio.h"
#include "mimecont.h"
#include "nsMimeStringResources.h"
#define MIME_SUPERCLASS mimeObjectClass
MimeDefClass(MimeContainer, MimeContainerClass,
mimeContainerClass, &MIME_SUPERCLASS);
static int MimeContainer_initialize (MimeObject *);
static void MimeContainer_finalize (MimeObject *);
static int MimeContainer_add_child (MimeObject *, MimeObject *);
static int MimeContainer_parse_eof (MimeObject *, bool);
static int MimeContainer_parse_end (MimeObject *, bool);
static bool MimeContainer_displayable_inline_p (MimeObjectClass *clazz,
MimeHeaders *hdrs);
#if defined(DEBUG) && defined(XP_UNIX)
static int MimeContainer_debug_print (MimeObject *, PRFileDesc *, int32_t depth);
#endif
static int
MimeContainerClassInitialize(MimeContainerClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) &clazz->object;
NS_ASSERTION(!oclass->class_initialized, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
oclass->initialize = MimeContainer_initialize;
oclass->finalize = MimeContainer_finalize;
oclass->parse_eof = MimeContainer_parse_eof;
oclass->parse_end = MimeContainer_parse_end;
oclass->displayable_inline_p = MimeContainer_displayable_inline_p;
clazz->add_child = MimeContainer_add_child;
#if defined(DEBUG) && defined(XP_UNIX)
oclass->debug_print = MimeContainer_debug_print;
#endif
return 0;
}
static int
MimeContainer_initialize (MimeObject *object)
{
/* This is an abstract class; it shouldn't be directly instantiated. */
NS_ASSERTION(object->clazz != (MimeObjectClass *) &mimeContainerClass, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
}
static void
MimeContainer_finalize (MimeObject *object)
{
MimeContainer *cont = (MimeContainer *) object;
/* Do this first so that children have their parse_eof methods called
in forward order (0-N) but are destroyed in backward order (N-0)
*/
if (!object->closed_p)
object->clazz->parse_eof (object, false);
if (!object->parsed_p)
object->clazz->parse_end (object, false);
if (cont->children)
{
int i;
for (i = cont->nchildren-1; i >= 0; i--)
{
MimeObject *kid = cont->children[i];
if (kid)
mime_free(kid);
cont->children[i] = 0;
}
PR_FREEIF(cont->children);
cont->nchildren = 0;
}
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
}
static int
MimeContainer_parse_eof (MimeObject *object, bool abort_p)
{
MimeContainer *cont = (MimeContainer *) object;
int status;
/* We must run all of this object's parent methods first, to get all the
data flushed down its stream, so that the children's parse_eof methods
can access it. We do not access *this* object again after doing this,
only its children.
*/
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(object, abort_p);
if (status < 0) return status;
if (cont->children)
{
int i;
for (i = 0; i < cont->nchildren; i++)
{
MimeObject *kid = cont->children[i];
if (kid && !kid->closed_p)
{
int lstatus = kid->clazz->parse_eof(kid, abort_p);
if (lstatus < 0) return lstatus;
}
}
}
return 0;
}
static int
MimeContainer_parse_end (MimeObject *object, bool abort_p)
{
MimeContainer *cont = (MimeContainer *) object;
int status;
/* We must run all of this object's parent methods first, to get all the
data flushed down its stream, so that the children's parse_eof methods
can access it. We do not access *this* object again after doing this,
only its children.
*/
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_end(object, abort_p);
if (status < 0) return status;
if (cont->children)
{
int i;
for (i = 0; i < cont->nchildren; i++)
{
MimeObject *kid = cont->children[i];
if (kid && !kid->parsed_p)
{
int lstatus = kid->clazz->parse_end(kid, abort_p);
if (lstatus < 0) return lstatus;
}
}
}
return 0;
}
static int
MimeContainer_add_child (MimeObject *parent, MimeObject *child)
{
MimeContainer *cont = (MimeContainer *) parent;
MimeObject **old_kids, **new_kids;
NS_ASSERTION(parent && child, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (!parent || !child) return -1;
old_kids = cont->children;
new_kids = (MimeObject **)PR_MALLOC(sizeof(MimeObject *) * (cont->nchildren + 1));
if (!new_kids) return MIME_OUT_OF_MEMORY;
if (cont->nchildren > 0)
memcpy(new_kids, old_kids, sizeof(MimeObject *) * cont->nchildren);
new_kids[cont->nchildren] = child;
PR_Free(old_kids);
cont->children = new_kids;
cont->nchildren++;
child->parent = parent;
/* Copy this object's options into the child. */
child->options = parent->options;
return 0;
}
static bool
MimeContainer_displayable_inline_p (MimeObjectClass *clazz, MimeHeaders *hdrs)
{
return true;
}
#if defined(DEBUG) && defined(XP_UNIX)
static int
MimeContainer_debug_print (MimeObject *obj, PRFileDesc *stream, int32_t depth)
{
MimeContainer *cont = (MimeContainer *) obj;
int i;
char *addr = mime_part_address(obj);
for (i=0; i < depth; i++)
PR_Write(stream, " ", 2);
/*
PR_Write(stream, "<%s %s (%d kid%s) 0x%08X>\n",
obj->clazz->class_name,
addr ? addr : "???",
cont->nchildren, (cont->nchildren == 1 ? "" : "s"),
(uint32_t) cont);
*/
PR_FREEIF(addr);
/*
if (cont->nchildren > 0)
fprintf(stream, "\n");
*/
for (i = 0; i < cont->nchildren; i++)
{
MimeObject *kid = cont->children[i];
int status = kid->clazz->debug_print (kid, stream, depth+1);
if (status < 0) return status;
}
/*
if (cont->nchildren > 0)
fprintf(stream, "\n");
*/
return 0;
}
#endif

View file

@ -0,0 +1,43 @@
/* -*- 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/. */
#ifndef _MIMECONT_H_
#define _MIMECONT_H_
#include "mimeobj.h"
/* MimeContainer is the class for the objects representing all MIME
types which can contain other MIME objects within them. In addition
to the methods inherited from MimeObject, it provides one method:
int add_child (MimeObject *parent, MimeObject *child)
Given a parent (a subclass of MimeContainer) this method adds the
child (any MIME object) to the parent's list of children.
The MimeContainer `finalize' method will finalize the children as well.
*/
typedef struct MimeContainerClass MimeContainerClass;
typedef struct MimeContainer MimeContainer;
struct MimeContainerClass {
MimeObjectClass object;
int (*add_child) (MimeObject *parent, MimeObject *child);
};
extern MimeContainerClass mimeContainerClass;
struct MimeContainer {
MimeObject object; /* superclass variables */
MimeObject **children; /* list of contained objects */
int32_t nchildren; /* how many */
};
#define MimeContainerClassInitializer(ITYPE,CSUPER) \
{ MimeObjectClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMECONT_H_ */

View file

@ -0,0 +1,571 @@
/* -*- Mode: C++; tab-width: 2; 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 "mimecryp.h"
#include "mimemoz2.h"
#include "nsMimeTypes.h"
#include "nsMimeStringResources.h"
#include "mimebuf.h"
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "mimemult.h"
#include "nsIMimeConverter.h" // for MimeConverterOutputCallback
#define MIME_SUPERCLASS mimeContainerClass
MimeDefClass(MimeEncrypted, MimeEncryptedClass, mimeEncryptedClass,
&MIME_SUPERCLASS);
static int MimeEncrypted_initialize (MimeObject *);
static void MimeEncrypted_finalize (MimeObject *);
static int MimeEncrypted_parse_begin (MimeObject *);
static int MimeEncrypted_parse_buffer (const char *, int32_t, MimeObject *);
static int MimeEncrypted_parse_line (const char *, int32_t, MimeObject *);
static int MimeEncrypted_parse_decoded_buffer (const char *, int32_t, MimeObject *);
static int MimeEncrypted_parse_eof (MimeObject *, bool);
static int MimeEncrypted_parse_end (MimeObject *, bool);
static int MimeEncrypted_add_child (MimeObject *, MimeObject *);
static int MimeHandleDecryptedOutput (const char *, int32_t, void *);
static int MimeHandleDecryptedOutputLine (char *, int32_t, MimeObject *);
static int MimeEncrypted_close_headers (MimeObject *);
static int MimeEncrypted_emit_buffered_child(MimeObject *);
static int
MimeEncryptedClassInitialize(MimeEncryptedClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeContainerClass *cclass = (MimeContainerClass *) clazz;
NS_ASSERTION(!oclass->class_initialized, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
oclass->initialize = MimeEncrypted_initialize;
oclass->finalize = MimeEncrypted_finalize;
oclass->parse_begin = MimeEncrypted_parse_begin;
oclass->parse_buffer = MimeEncrypted_parse_buffer;
oclass->parse_line = MimeEncrypted_parse_line;
oclass->parse_eof = MimeEncrypted_parse_eof;
oclass->parse_end = MimeEncrypted_parse_end;
cclass->add_child = MimeEncrypted_add_child;
clazz->parse_decoded_buffer = MimeEncrypted_parse_decoded_buffer;
return 0;
}
static int
MimeEncrypted_initialize (MimeObject *obj)
{
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(obj);
}
static int
MimeEncrypted_parse_begin (MimeObject *obj)
{
MimeEncrypted *enc = (MimeEncrypted *) obj;
MimeDecoderData *(*fn) (MimeConverterOutputCallback, void*) = 0;
if (enc->crypto_closure)
return -1;
enc->crypto_closure = (((MimeEncryptedClass *) obj->clazz)->crypto_init) (obj, MimeHandleDecryptedOutput, obj);
if (!enc->crypto_closure)
return -1;
/* (Mostly duplicated from MimeLeaf, see comments in mimecryp.h.)
Initialize a decoder if necessary.
*/
if (!obj->encoding)
;
else if (!PL_strcasecmp(obj->encoding, ENCODING_BASE64))
fn = &MimeB64DecoderInit;
else if (!PL_strcasecmp(obj->encoding, ENCODING_QUOTED_PRINTABLE))
{
enc->decoder_data =
MimeQPDecoderInit (/* The (MimeConverterOutputCallback) cast is to turn the
`void' argument into `MimeObject'. */
((MimeConverterOutputCallback)
((MimeEncryptedClass *)obj->clazz)->parse_decoded_buffer),
obj);
if (!enc->decoder_data)
return MIME_OUT_OF_MEMORY;
}
else if (!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE) ||
!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE2) ||
!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE3) ||
!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE4))
fn = &MimeUUDecoderInit;
else if (!PL_strcasecmp(obj->encoding, ENCODING_YENCODE))
fn = &MimeYDecoderInit;
if (fn)
{
enc->decoder_data =
fn (/* The (MimeConverterOutputCallback) cast is to turn the `void'
argument into `MimeObject'. */
((MimeConverterOutputCallback)
((MimeEncryptedClass *)obj->clazz)->parse_decoded_buffer),
obj);
if (!enc->decoder_data)
return MIME_OUT_OF_MEMORY;
}
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
}
static int
MimeEncrypted_parse_buffer (const char *buffer, int32_t size, MimeObject *obj)
{
/* (Duplicated from MimeLeaf, see comments in mimecryp.h.)
*/
MimeEncrypted *enc = (MimeEncrypted *) obj;
if (obj->closed_p) return -1;
/* Don't consult output_p here, since at this point we're behaving as a
simple container object -- the output_p decision should be made by
the child of this object. */
if (enc->decoder_data)
return MimeDecoderWrite (enc->decoder_data, buffer, size, nullptr);
else
return ((MimeEncryptedClass *)obj->clazz)->parse_decoded_buffer (buffer,
size,
obj);
}
static int
MimeEncrypted_parse_line (const char *line, int32_t length, MimeObject *obj)
{
NS_ERROR("This method shouldn't ever be called.");
return -1;
}
static int
MimeEncrypted_parse_decoded_buffer (const char *buffer, int32_t size, MimeObject *obj)
{
MimeEncrypted *enc = (MimeEncrypted *) obj;
return
((MimeEncryptedClass *) obj->clazz)->crypto_write (buffer, size,
enc->crypto_closure);
}
static int
MimeEncrypted_parse_eof (MimeObject *obj, bool abort_p)
{
int status = 0;
MimeEncrypted *enc = (MimeEncrypted *) obj;
if (obj->closed_p) return 0;
NS_ASSERTION(!obj->parsed_p, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
/* (Duplicated from MimeLeaf, see comments in mimecryp.h.)
Close off the decoder, to cause it to give up any buffered data that
it is still holding.
*/
if (enc->decoder_data)
{
int status = MimeDecoderDestroy(enc->decoder_data, false);
enc->decoder_data = 0;
if (status < 0) return status;
}
/* If there is still data in the ibuffer, that means that the last
*decrypted* line of this part didn't end in a newline; so push it out
anyway (this means that the parse_line method will be called with a
string with no trailing newline, which isn't the usual case.) */
if (!abort_p &&
obj->ibuffer_fp > 0)
{
int status = MimeHandleDecryptedOutputLine (obj->ibuffer,
obj->ibuffer_fp, obj);
obj->ibuffer_fp = 0;
if (status < 0)
{
obj->closed_p = true;
return status;
}
}
/* Now run the superclass's parse_eof, which (because we've already taken
care of ibuffer in a way appropriate for this class, immediately above)
will ony set closed_p to true.
*/
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof (obj, abort_p);
if (status < 0) return status;
/* Now close off the underlying crypto module. At this point, the crypto
module has all of the input. (DecoderDestroy called parse_decoded_buffer
which called crypto_write, with the last of the data.)
*/
if (enc->crypto_closure)
{
status =
((MimeEncryptedClass *) obj->clazz)->crypto_eof (enc->crypto_closure,
abort_p);
if (status < 0 && !abort_p)
return status;
}
/* Now we have the entire child part in the part buffer.
We are now able to verify its signature, emit a blurb, and then
emit the part.
*/
if (abort_p)
return 0;
else
return MimeEncrypted_emit_buffered_child (obj);
}
static int
MimeEncrypted_parse_end (MimeObject *obj, bool abort_p)
{
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_end (obj, abort_p);
}
static void
MimeEncrypted_cleanup (MimeObject *obj, bool finalizing_p)
{
MimeEncrypted *enc = (MimeEncrypted *) obj;
if (enc->part_buffer)
{
MimePartBufferDestroy(enc->part_buffer);
enc->part_buffer = 0;
}
if (finalizing_p && enc->crypto_closure)
{
/* Don't free these until this object is really going away -- keep them
around for the lifetime of the MIME object, so that we can get at the
security info of sub-parts of the currently-displayed message. */
((MimeEncryptedClass *) obj->clazz)->crypto_free (enc->crypto_closure);
enc->crypto_closure = 0;
}
/* (Duplicated from MimeLeaf, see comments in mimecryp.h.)
Free the decoder data, if it's still around. */
if (enc->decoder_data)
{
MimeDecoderDestroy(enc->decoder_data, true);
enc->decoder_data = 0;
}
if (enc->hdrs)
{
MimeHeaders_free(enc->hdrs);
enc->hdrs = 0;
}
}
static void
MimeEncrypted_finalize (MimeObject *obj)
{
MimeEncrypted_cleanup (obj, true);
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize (obj);
}
static int
MimeHandleDecryptedOutput (const char *buf, int32_t buf_size,
void *output_closure)
{
/* This method is invoked by the underlying decryption module.
The module is assumed to return a MIME object, and its associated
headers. For example, if a text/plain document was encrypted,
the encryption module would return the following data:
Content-Type: text/plain
Decrypted text goes here.
This function will then extract a header block (up to the first
blank line, as usual) and will then handle the included data as
appropriate.
*/
MimeObject *obj = (MimeObject *) output_closure;
/* Is it truly safe to use ibuffer here? I think so... */
return mime_LineBuffer (buf, buf_size,
&obj->ibuffer, &obj->ibuffer_size, &obj->ibuffer_fp,
true,
((int (*) (char *, int32_t, void *))
/* This cast is to turn void into MimeObject */
MimeHandleDecryptedOutputLine),
obj);
}
static int
MimeHandleDecryptedOutputLine (char *line, int32_t length, MimeObject *obj)
{
/* Largely the same as MimeMessage_parse_line (the other MIME container
type which contains exactly one child.)
*/
MimeEncrypted *enc = (MimeEncrypted *) obj;
int status = 0;
if (!line || !*line) return -1;
/* If we're supposed to write this object, but aren't supposed to convert
it to HTML, simply pass it through unaltered. */
if (obj->output_p &&
obj->options &&
!obj->options->write_html_p &&
obj->options->output_fn)
return MimeObject_write(obj, line, length, true);
/* If we already have a child object in the buffer, then we're done parsing
headers, and all subsequent lines get passed to the inferior object
without further processing by us. (Our parent will stop feeding us
lines when this MimeMessage part is out of data.)
*/
if (enc->part_buffer)
return MimePartBufferWrite (enc->part_buffer, line, length);
/* Otherwise we don't yet have a child object in the buffer, which means
we're not done parsing our headers yet.
*/
if (!enc->hdrs)
{
enc->hdrs = MimeHeaders_new();
if (!enc->hdrs) return MIME_OUT_OF_MEMORY;
}
status = MimeHeaders_parse_line(line, length, enc->hdrs);
if (status < 0) return status;
/* If this line is blank, we're now done parsing headers, and should
examine our content-type to create our "body" part.
*/
if (*line == '\r' || *line == '\n')
{
status = MimeEncrypted_close_headers(obj);
if (status < 0) return status;
}
return 0;
}
static int
MimeEncrypted_close_headers (MimeObject *obj)
{
MimeEncrypted *enc = (MimeEncrypted *) obj;
// Notify the JS Mime Emitter that this was an encrypted part that it should
// hopefully not analyze for indexing...
if (obj->options->notify_nested_bodies)
mimeEmitterAddHeaderField(obj->options, "x-jsemitter-encrypted", "1");
if (enc->part_buffer) return -1;
enc->part_buffer = MimePartBufferCreate();
if (!enc->part_buffer)
return MIME_OUT_OF_MEMORY;
return 0;
}
static int
MimeEncrypted_add_child (MimeObject *parent, MimeObject *child)
{
MimeContainer *cont = (MimeContainer *) parent;
if (!parent || !child) return -1;
/* Encryption containers can only have one child. */
if (cont->nchildren != 0) return -1;
return ((MimeContainerClass*)&MIME_SUPERCLASS)->add_child (parent, child);
}
static int
MimeEncrypted_emit_buffered_child(MimeObject *obj)
{
MimeEncrypted *enc = (MimeEncrypted *) obj;
int status = 0;
char *ct = 0;
MimeObject *body;
NS_ASSERTION(enc->crypto_closure, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
/* Emit some HTML saying whether the signature was cool.
But don't emit anything if in FO_QUOTE_MESSAGE mode.
Also, don't emit anything if the enclosed object is itself a signed
object -- in the case of an encrypted object which contains a signed
object, we only emit the HTML once (since the normal way of encrypting
and signing is to nest the signature inside the crypto envelope.)
*/
if (enc->crypto_closure &&
obj->options &&
obj->options->headers != MimeHeadersCitation &&
obj->options->write_html_p &&
obj->options->output_fn)
// && !mime_crypto_object_p(enc->hdrs, true, obj->options)) // XXX fix later XXX //
{
char *html;
#if 0 // XXX Fix this later XXX //
char *html = (((MimeEncryptedClass *) obj->clazz)->crypto_generate_html
(enc->crypto_closure));
if (!html) return -1; /* MK_OUT_OF_MEMORY? */
status = MimeObject_write(obj, html, strlen(html), false);
PR_FREEIF(html);
if (status < 0) return status;
#endif
/* Now that we have written out the crypto stamp, the outermost header
block is well and truly closed. If this is in fact the outermost
message, then run the post_header_html_fn now.
*/
if (obj->options &&
obj->options->state &&
obj->options->generate_post_header_html_fn &&
!obj->options->state->post_header_html_run_p)
{
MimeHeaders *outer_headers = nullptr;
MimeObject *p;
for (p = obj; p->parent; p = p->parent)
outer_headers = p->headers;
NS_ASSERTION(obj->options->state->first_data_written_p, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
html = obj->options->generate_post_header_html_fn(NULL,
obj->options->html_closure,
outer_headers);
obj->options->state->post_header_html_run_p = true;
if (html)
{
status = MimeObject_write(obj, html, strlen(html), false);
PR_FREEIF(html);
if (status < 0) return status;
}
}
}
else if (enc->crypto_closure &&
obj->options &&
obj->options->decrypt_p)
{
/* Do this just to cause `mime_set_crypto_stamp' to be called, and to
cause the various `decode_error' and `verify_error' slots to be set:
we don't actually use the returned HTML, because we're not emitting
HTML. It's maybe not such a good thing that the determination of
whether it was encrypted or not is tied up with generating HTML,
but oh well. */
char *html = (((MimeEncryptedClass *) obj->clazz)->crypto_generate_html
(enc->crypto_closure));
PR_FREEIF(html);
}
if (enc->hdrs)
ct = MimeHeaders_get (enc->hdrs, HEADER_CONTENT_TYPE, true, false);
body = mime_create((ct ? ct : TEXT_PLAIN), enc->hdrs, obj->options);
#ifdef MIME_DRAFTS
if (obj->options->decompose_file_p) {
if (mime_typep (body, (MimeObjectClass*) &mimeMultipartClass) )
obj->options->is_multipart_msg = true;
else if (obj->options->decompose_file_init_fn)
obj->options->decompose_file_init_fn(obj->options->stream_closure,
enc->hdrs);
}
#endif /* MIME_DRAFTS */
PR_FREEIF(ct);
if (!body) return MIME_OUT_OF_MEMORY;
status = ((MimeContainerClass *) obj->clazz)->add_child (obj, body);
if (status < 0)
{
mime_free(body);
return status;
}
/* Now that we've added this new object to our list of children,
start its parser going. */
status = body->clazz->parse_begin(body);
if (status < 0) return status;
/* If this object (or the parent) is being output, then by definition
the child is as well. (This is only necessary because this is such
a funny sort of container...)
*/
if (!body->output_p &&
(obj->output_p ||
(obj->parent && obj->parent->output_p)))
body->output_p = true;
/* If the body is being written raw (not as HTML) then make sure to
write its headers as well. */
if (body->output_p && obj->output_p && !obj->options->write_html_p)
{
status = MimeObject_write(body, "", 0, false); /* initialize */
if (status < 0) return status;
status = MimeHeaders_write_raw_headers(body->headers, obj->options,
false);
if (status < 0) return status;
}
if (enc->part_buffer) /* part_buffer is 0 for 0-length encrypted data. */
{
#ifdef MIME_DRAFTS
if (obj->options->decompose_file_p && !obj->options->is_multipart_msg)
{
status = MimePartBufferRead(enc->part_buffer,
/* The (MimeConverterOutputCallback) cast is to turn the `void'
argument into `MimeObject'. */
((MimeConverterOutputCallback)
obj->options->decompose_file_output_fn),
obj->options->stream_closure);
}
else
{
#endif /* MIME_DRAFTS */
status = MimePartBufferRead(enc->part_buffer,
/* The (MimeConverterOutputCallback) cast is to turn the `void'
argument into `MimeObject'. */
((MimeConverterOutputCallback) body->clazz->parse_buffer),
body);
#ifdef MIME_DRAFTS
}
#endif /* MIME_DRAFTS */
}
if (status < 0) return status;
/* The child has been fully processed. Close it off.
*/
status = body->clazz->parse_eof(body, false);
if (status < 0) return status;
status = body->clazz->parse_end(body, false);
if (status < 0) return status;
#ifdef MIME_DRAFTS
if (obj->options->decompose_file_p && !obj->options->is_multipart_msg)
obj->options->decompose_file_close_fn(obj->options->stream_closure);
#endif /* MIME_DRAFTS */
/* Put out a separator after every encrypted object. */
status = MimeObject_write_separator(obj);
if (status < 0) return status;
MimeEncrypted_cleanup (obj, false);
return 0;
}

View file

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef _MIMECRYP_H_
#define _MIMECRYP_H_
#include "mimecont.h"
// #include "mimeenc.h"
#include "modmimee.h"
#include "mimepbuf.h"
/* The MimeEncrypted class implements a type of MIME object where the object
is passed to some other routine, which then returns a new MIME object.
This is the basis of a decryption module.
Oddly, this class behaves both as a container and as a leaf: it acts as a
container in that it parses out data in order to eventually present a
contained object; however, it acts as a leaf in that this container may
itself have a Content-Transfer-Encoding applied to its body. This violates
the cardinal rule of MIME containers, which is that encodings don't nest,
and therefore containers can't have encodings. But, the fact that the
S/MIME spec doesn't follow the groundwork laid down by previous MIME specs
isn't something we can do anything about at this point...
Therefore, this class duplicates some of the work done by the MimeLeaf
class, to meet its dual goals of container-hood and leaf-hood. (We could
alternately have made this class be a subclass of leaf, and had it duplicate
the effort of MimeContainer, but that seemed like the harder approach.)
The MimeEncrypted class provides the following methods:
void *crypto_init(MimeObject *obj,
int (*output_fn) (const char *data, int32 data_size,
void *output_closure),
void *output_closure)
This is called with the MimeObject representing the encrypted data.
The obj->headers should be used to initialize the decryption engine.
NULL indicates failure; otherwise, an opaque closure object should
be returned.
output_fn is what the decryption module should use to write a new MIME
object (the decrypted data.) output_closure should be passed along to
every call to the output routine.
The data sent to output_fn should begin with valid MIME headers indicating
the type of the data. For example, if decryption resulted in a text
document, the data fed through to the output_fn might minimally look like
Content-Type: text/plain
This is the decrypted data.
It is only two lines long.
Of course, the data may be of any MIME type, including multipart/mixed.
Any returned MIME object will be recursively processed and presented
appropriately. (This also imples that encrypted objects may nest, and
thus that the underlying decryption module must be reentrant.)
int crypto_write (const char *data, int32 data_size, void *crypto_closure)
This is called with the raw encrypted data. This data might not come
in line-based chunks: if there was a Content-Transfer-Encoding applied
to the data (base64 or quoted-printable) then it will have been decoded
first (handing binary data to the filter_fn.) `crypto_closure' is the
object that `crypto_init' returned. This may return negative on error.
int crypto_eof (void *crypto_closure, bool abort_p)
This is called when no more data remains. It may call `output_fn' again
to flush out any buffered data. If `abort_p' is true, then it may choose
to discard any data rather than processing it, as we're terminating
abnormally.
char * crypto_generate_html (void *crypto_closure)
This is called after `crypto_eof' but before `crypto_free'. The crypto
module should return a newly-allocated string of HTML code which
explains the status of the decryption to the user (whether the signature
checked out, etc.)
void crypto_free (void *crypto_closure)
This will be called when we're all done, after `crypto_eof' and
`crypto_emit_html'. It is intended to free any data represented
by the crypto_closure. output_fn may not be called.
int (*parse_decoded_buffer) (const char *buf, int32 size, MimeObject *obj)
This method, of the same name as one in MimeLeaf, is a part of the
afforementioned leaf/container hybridization. This method is invoked
with the content-transfer-decoded body of this part (without line
buffering.) The default behavior of this method is to simply invoke
`crypto_write' on the data with which it is called. It's unlikely that
a subclass will need to specialize this.
*/
typedef struct MimeEncryptedClass MimeEncryptedClass;
typedef struct MimeEncrypted MimeEncrypted;
struct MimeEncryptedClass {
MimeContainerClass container;
/* Duplicated from MimeLeaf, see comments above.
This is the callback that is handed to the decoder. */
int (*parse_decoded_buffer) (const char *buf, int32_t size, MimeObject *obj);
/* Callbacks used by decryption module. */
void * (*crypto_init) (MimeObject *obj,
int (*output_fn) (const char *data, int32_t data_size,
void *output_closure),
void *output_closure);
int (*crypto_write) (const char *data, int32_t data_size,
void *crypto_closure);
int (*crypto_eof) (void *crypto_closure, bool abort_p);
char * (*crypto_generate_html) (void *crypto_closure);
void (*crypto_free) (void *crypto_closure);
};
extern MimeEncryptedClass mimeEncryptedClass;
struct MimeEncrypted {
MimeContainer container; /* superclass variables */
void *crypto_closure; /* Opaque data used by decryption module. */
MimeDecoderData *decoder_data; /* Opaque data for the Transfer-Encoding
decoder. */
MimeHeaders *hdrs; /* Headers of the enclosed object (including
the type of the *decrypted* data.) */
MimePartBufferData *part_buffer; /* The data of the decrypted enclosed
object (see mimepbuf.h) */
};
#define MimeEncryptedClassInitializer(ITYPE,CSUPER) \
{ MimeContainerClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMECRYP_H_ */

View file

@ -0,0 +1,51 @@
/* -*- 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 "mimecth.h"
/*
* These calls are necessary to expose the object class hierarchy
* to externally developed content type handlers.
*/
MimeInlineTextClass *
MIME_GetmimeInlineTextClass(void)
{
return &mimeInlineTextClass;
}
MimeLeafClass *
MIME_GetmimeLeafClass(void)
{
return &mimeLeafClass;
}
MimeObjectClass *
MIME_GetmimeObjectClass(void)
{
return &mimeObjectClass;
}
MimeContainerClass *
MIME_GetmimeContainerClass(void)
{
return &mimeContainerClass;
}
MimeMultipartClass *
MIME_GetmimeMultipartClass(void)
{
return &mimeMultipartClass;
}
MimeMultipartSignedClass *
MIME_GetmimeMultipartSignedClass(void)
{
return &mimeMultipartSignedClass;
}
MimeEncryptedClass *
MIME_GetmimeEncryptedClass(void)
{
return &mimeEncryptedClass;
}

135
mailnews/mime/src/mimecth.h Normal file
View file

@ -0,0 +1,135 @@
/* -*- 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/. */
/*
* This is the definitions for the Content Type Handler plugins for
* libmime. This will allow developers the dynamically add the ability
* for libmime to render new content types in the MHTML rendering of
* HTML messages.
*/
#ifndef _MIMECTH_H_
#define _MIMECTH_H_
#include "mimei.h"
#include "mimeobj.h" /* MimeObject (abstract) */
#include "mimecont.h" /* |--- MimeContainer (abstract) */
#include "mimemult.h" /* | |--- MimeMultipart (abstract) */
#include "mimemsig.h" /* | | |--- MimeMultipartSigned (abstract)*/
#include "mimetext.h" /* | |--- MimeInlineText (abstract) */
#include "mimecryp.h"
/*
This header exposes functions that are necessary to access the
object hierarchy for the mime chart. The class hierarchy is:
MimeObject (abstract)
|
|--- MimeContainer (abstract)
| |
| |--- MimeMultipart (abstract)
| | |
| | |--- MimeMultipartMixed
| | |
| | |--- MimeMultipartDigest
| | |
| | |--- MimeMultipartParallel
| | |
| | |--- MimeMultipartAlternative
| | |
| | |--- MimeMultipartRelated
| | |
| | |--- MimeMultipartAppleDouble
| | |
| | |--- MimeSunAttachment
| | |
| | |--- MimeMultipartSigned (abstract)
| | |
| | |--- MimeMultipartSigned
| |
| |--- MimeXlateed (abstract)
| | |
| | |--- MimeXlateed
| |
| |--- MimeMessage
| |
| |--- MimeUntypedText
|
|--- MimeLeaf (abstract)
| |
| |--- MimeInlineText (abstract)
| | |
| | |--- MimeInlineTextPlain
| | |
| | |--- MimeInlineTextHTML
| | |
| | |--- MimeInlineTextRichtext
| | | |
| | | |--- MimeInlineTextEnriched
| | |
| | |--- MimeInlineTextVCard
| |
| |--- MimeInlineImage
| |
| |--- MimeExternalObject
|
|--- MimeExternalBody
*/
#include "nsIMimeContentTypeHandler.h"
/*
* These functions are exposed by libmime to be used by content type
* handler plugins for processing stream data.
*/
/*
* This is the write call for outputting processed stream data.
*/
extern int MIME_MimeObject_write(MimeObject *,
const char *data,
int32_t length,
bool user_visible_p);
/*
* The following group of calls expose the pointers for the object
* system within libmime.
*/
extern MimeInlineTextClass *MIME_GetmimeInlineTextClass(void);
extern MimeLeafClass *MIME_GetmimeLeafClass(void);
extern MimeObjectClass *MIME_GetmimeObjectClass(void);
extern MimeContainerClass *MIME_GetmimeContainerClass(void);
extern MimeMultipartClass *MIME_GetmimeMultipartClass(void);
extern MimeMultipartSignedClass *MIME_GetmimeMultipartSignedClass(void);
extern MimeEncryptedClass *MIME_GetmimeEncryptedClass(void);
/*
* These are the functions that need to be implemented by the
* content type handler plugin. They will be called by by libmime
* when the module is loaded at runtime.
*/
/*
* MIME_GetContentType() is called by libmime to identify the content
* type handled by this plugin.
*/
extern "C"
char *MIME_GetContentType(void);
/*
* This will create the MimeObjectClass object to be used by the libmime
* object system.
*/
extern "C"
MimeObjectClass *MIME_CreateContentTypeHandlerClass(const char *content_type,
contentTypeHandlerInitStruct *initStruct);
/*
* Typedefs for libmime to use when locating and calling the above
* defined functions.
*/
typedef char * (*mime_get_ct_fn_type)(void);
typedef MimeObjectClass * (*mime_create_class_fn_type)
(const char *, contentTypeHandlerInitStruct *);
#endif /* _MIMECTH_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,509 @@
/* -*- 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 "nsCOMPtr.h"
#include "nsIURL.h"
#include "mimeebod.h"
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "prio.h"
#include "msgCore.h"
#include "nsMimeStringResources.h"
#include "mimemoz2.h"
#include "nsComponentManagerUtils.h"
#include "nsMsgUtils.h"
#include "nsINetUtil.h"
#include <ctype.h>
#define MIME_SUPERCLASS mimeObjectClass
MimeDefClass(MimeExternalBody, MimeExternalBodyClass,
mimeExternalBodyClass, &MIME_SUPERCLASS);
#ifdef XP_MACOSX
extern MimeObjectClass mimeMultipartAppleDoubleClass;
#endif
static int MimeExternalBody_initialize (MimeObject *);
static void MimeExternalBody_finalize (MimeObject *);
static int MimeExternalBody_parse_line (const char *, int32_t, MimeObject *);
static int MimeExternalBody_parse_eof (MimeObject *, bool);
static bool MimeExternalBody_displayable_inline_p (MimeObjectClass *clazz,
MimeHeaders *hdrs);
#if 0
#if defined(DEBUG) && defined(XP_UNIX)
static int MimeExternalBody_debug_print (MimeObject *, PRFileDesc *, int32_t);
#endif
#endif /* 0 */
static int
MimeExternalBodyClassInitialize(MimeExternalBodyClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
NS_ASSERTION(!oclass->class_initialized, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
oclass->initialize = MimeExternalBody_initialize;
oclass->finalize = MimeExternalBody_finalize;
oclass->parse_line = MimeExternalBody_parse_line;
oclass->parse_eof = MimeExternalBody_parse_eof;
oclass->displayable_inline_p = MimeExternalBody_displayable_inline_p;
#if 0
#if defined(DEBUG) && defined(XP_UNIX)
oclass->debug_print = MimeExternalBody_debug_print;
#endif
#endif /* 0 */
return 0;
}
static int
MimeExternalBody_initialize (MimeObject *object)
{
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
}
static void
MimeExternalBody_finalize (MimeObject *object)
{
MimeExternalBody *bod = (MimeExternalBody *) object;
if (bod->hdrs)
{
MimeHeaders_free(bod->hdrs);
bod->hdrs = 0;
}
PR_FREEIF(bod->body);
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
}
static int
MimeExternalBody_parse_line (const char *line, int32_t length, MimeObject *obj)
{
MimeExternalBody *bod = (MimeExternalBody *) obj;
int status = 0;
NS_ASSERTION(line && *line, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (!line || !*line) return -1;
if (!obj->output_p) return 0;
/* If we're supposed to write this object, but aren't supposed to convert
it to HTML, simply pass it through unaltered. */
if (obj->options &&
!obj->options->write_html_p &&
obj->options->output_fn)
return MimeObject_write(obj, line, length, true);
/* If we already have a `body' then we're done parsing headers, and all
subsequent lines get tacked onto the body. */
if (bod->body)
{
int L = strlen(bod->body);
char *new_str = (char *)PR_Realloc(bod->body, L + length + 1);
if (!new_str) return MIME_OUT_OF_MEMORY;
bod->body = new_str;
memcpy(bod->body + L, line, length);
bod->body[L + length] = 0;
return 0;
}
/* Otherwise we don't yet have a body, which means we're not done parsing
our headers.
*/
if (!bod->hdrs)
{
bod->hdrs = MimeHeaders_new();
if (!bod->hdrs) return MIME_OUT_OF_MEMORY;
}
status = MimeHeaders_parse_line(line, length, bod->hdrs);
if (status < 0) return status;
/* If this line is blank, we're now done parsing headers, and should
create a dummy body to show that. Gag.
*/
if (*line == '\r' || *line == '\n')
{
bod->body = strdup("");
if (!bod->body) return MIME_OUT_OF_MEMORY;
}
return 0;
}
char *
MimeExternalBody_make_url(const char *ct,
const char *at, const char *lexp, const char *size,
const char *perm, const char *dir, const char *mode,
const char *name, const char *url, const char *site,
const char *svr, const char *subj, const char *body)
{
char *s;
uint32_t slen;
if (!at)
{
return 0;
}
else if (!PL_strcasecmp(at, "ftp") || !PL_strcasecmp(at, "anon-ftp"))
{
if (!site || !name)
return 0;
slen = strlen(name) + strlen(site) + (dir ? strlen(dir) : 0) + 20;
s = (char *) PR_MALLOC(slen);
if (!s) return 0;
PL_strncpyz(s, "ftp://", slen);
PL_strcatn(s, slen, site);
PL_strcatn(s, slen, "/");
if (dir) PL_strcatn(s, slen, (dir[0] == '/' ? dir+1 : dir));
if (s[strlen(s)-1] != '/')
PL_strcatn(s, slen, "/");
PL_strcatn(s, slen, name);
return s;
}
else if (!PL_strcasecmp(at, "local-file") || !PL_strcasecmp(at, "afs"))
{
if (!name)
return 0;
#ifdef XP_UNIX
if (!PL_strcasecmp(at, "afs")) /* only if there is a /afs/ directory */
{
nsCOMPtr <nsIFile> fs = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
bool exists = false;
if (fs)
{
fs->InitWithNativePath(NS_LITERAL_CSTRING("/afs/."));
fs->Exists(&exists);
}
if (!exists)
return 0;
}
#else /* !XP_UNIX */
return 0; /* never, if not Unix. */
#endif /* !XP_UNIX */
slen = (strlen(name) * 3 + 20);
s = (char *) PR_MALLOC(slen);
if (!s) return 0;
PL_strncpyz(s, "file:", slen);
nsCString s2;
MsgEscapeString(nsDependentCString(name), nsINetUtil::ESCAPE_URL_PATH, s2);
PL_strcatn(s, slen, s2.get());
return s;
}
else if (!PL_strcasecmp(at, "mail-server"))
{
if (!svr)
return 0;
slen = (strlen(svr)*4 + (subj ? strlen(subj)*4 : 0) +
(body ? strlen(body)*4 : 0) + 25); // dpv xxx: why 4x? %xx escaping should be 3x
s = (char *) PR_MALLOC(slen);
if (!s) return 0;
PL_strncpyz(s, "mailto:", slen);
nsCString s2;
MsgEscapeString(nsDependentCString(svr), nsINetUtil::ESCAPE_XALPHAS, s2);
PL_strcatn(s, slen, s2.get());
if (subj)
{
MsgEscapeString(nsDependentCString(subj), nsINetUtil::ESCAPE_XALPHAS, s2);
PL_strcatn(s, slen, "?subject=");
PL_strcatn(s, slen, s2.get());
}
if (body)
{
MsgEscapeString(nsDependentCString(body), nsINetUtil::ESCAPE_XALPHAS, s2);
PL_strcatn(s, slen, (subj ? "&body=" : "?body="));
PL_strcatn(s, slen, s2.get());
}
return s;
}
else if (!PL_strcasecmp(at, "url")) /* RFC 2017 */
{
if (url)
return strdup(url); /* it's already quoted and everything */
else
return 0;
}
else
return 0;
}
static int
MimeExternalBody_parse_eof (MimeObject *obj, bool abort_p)
{
int status = 0;
MimeExternalBody *bod = (MimeExternalBody *) obj;
if (obj->closed_p) return 0;
/* Run parent method first, to flush out any buffered data. */
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0) return status;
#ifdef XP_MACOSX
if (obj->parent && mime_typep(obj->parent,
(MimeObjectClass*) &mimeMultipartAppleDoubleClass))
goto done;
#endif /* XP_MACOSX */
if (!abort_p &&
obj->output_p &&
obj->options &&
obj->options->write_html_p)
{
bool all_headers_p = obj->options->headers == MimeHeadersAll;
MimeDisplayOptions *newopt = obj->options; /* copy it */
char *ct = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE,
false, false);
char *at, *lexp, *size, *perm;
char *url, *dir, *mode, *name, *site, *svr, *subj;
char *h = 0, *lname = 0, *lurl = 0, *body = 0;
MimeHeaders *hdrs = 0;
if (!ct) return MIME_OUT_OF_MEMORY;
at = MimeHeaders_get_parameter(ct, "access-type", NULL, NULL);
lexp = MimeHeaders_get_parameter(ct, "expiration", NULL, NULL);
size = MimeHeaders_get_parameter(ct, "size", NULL, NULL);
perm = MimeHeaders_get_parameter(ct, "permission", NULL, NULL);
dir = MimeHeaders_get_parameter(ct, "directory", NULL, NULL);
mode = MimeHeaders_get_parameter(ct, "mode", NULL, NULL);
name = MimeHeaders_get_parameter(ct, "name", NULL, NULL);
site = MimeHeaders_get_parameter(ct, "site", NULL, NULL);
svr = MimeHeaders_get_parameter(ct, "server", NULL, NULL);
subj = MimeHeaders_get_parameter(ct, "subject", NULL, NULL);
url = MimeHeaders_get_parameter(ct, "url", NULL, NULL);
PR_FREEIF(ct);
/* the *internal* content-type */
ct = MimeHeaders_get(bod->hdrs, HEADER_CONTENT_TYPE,
true, false);
uint32_t hlen = ((at ? strlen(at) : 0) +
(lexp ? strlen(lexp) : 0) +
(size ? strlen(size) : 0) +
(perm ? strlen(perm) : 0) +
(dir ? strlen(dir) : 0) +
(mode ? strlen(mode) : 0) +
(name ? strlen(name) : 0) +
(site ? strlen(site) : 0) +
(svr ? strlen(svr) : 0) +
(subj ? strlen(subj) : 0) +
(ct ? strlen(ct) : 0) +
(url ? strlen(url) : 0) + 100);
h = (char *) PR_MALLOC(hlen);
if (!h)
{
status = MIME_OUT_OF_MEMORY;
goto FAIL;
}
/* If there's a URL parameter, remove all whitespace from it.
(The URL parameter to one of these headers is stored with
lines broken every 40 characters or less; it's assumed that
all significant whitespace was URL-hex-encoded, and all the
rest of it was inserted just to keep the lines short.)
*/
if (url)
{
char *in, *out;
for (in = url, out = url; *in; in++)
if (!IS_SPACE(*in))
*out++ = *in;
*out = 0;
}
hdrs = MimeHeaders_new();
if (!hdrs)
{
status = MIME_OUT_OF_MEMORY;
goto FAIL;
}
# define FROB(STR,VAR) \
if (VAR) \
{ \
PL_strncpyz(h, STR ": ", hlen); \
PL_strcatn(h, hlen, VAR); \
PL_strcatn(h, hlen, MSG_LINEBREAK); \
status = MimeHeaders_parse_line(h, strlen(h), hdrs); \
if (status < 0) goto FAIL; \
}
FROB("Access-Type", at);
FROB("URL", url);
FROB("Site", site);
FROB("Server", svr);
FROB("Directory", dir);
FROB("Name", name);
FROB("Type", ct);
FROB("Size", size);
FROB("Mode", mode);
FROB("Permission", perm);
FROB("Expiration", lexp);
FROB("Subject", subj);
# undef FROB
PL_strncpyz(h, MSG_LINEBREAK, hlen);
status = MimeHeaders_parse_line(h, strlen(h), hdrs);
if (status < 0) goto FAIL;
lurl = MimeExternalBody_make_url(ct, at, lexp, size, perm, dir, mode,
name, url, site, svr, subj, bod->body);
if (lurl)
{
lname = MimeGetStringByID(MIME_MSG_LINK_TO_DOCUMENT);
}
else
{
lname = MimeGetStringByID(MIME_MSG_DOCUMENT_INFO);
all_headers_p = true;
}
all_headers_p = true; /* #### just do this all the time? */
if (bod->body && all_headers_p)
{
char *s = bod->body;
while (IS_SPACE(*s)) s++;
if (*s)
{
char *s2;
const char *pre = "<P><PRE>";
const char *suf = "</PRE>";
int32_t i;
for(i = strlen(s)-1; i >= 0 && IS_SPACE(s[i]); i--)
s[i] = 0;
s2 = MsgEscapeHTML(s);
if (!s2) goto FAIL;
body = (char *) PR_MALLOC(strlen(pre) + strlen(s2) +
strlen(suf) + 1);
if (!body)
{
NS_Free(s2);
goto FAIL;
}
PL_strcpy(body, pre);
PL_strcat(body, s2);
PL_strcat(body, suf);
}
}
newopt->fancy_headers_p = true;
newopt->headers = (all_headers_p ? MimeHeadersAll : MimeHeadersSome);
FAIL:
if (hdrs)
MimeHeaders_free(hdrs);
PR_FREEIF(h);
PR_FREEIF(lname);
PR_FREEIF(lurl);
PR_FREEIF(body);
PR_FREEIF(ct);
PR_FREEIF(at);
PR_FREEIF(lexp);
PR_FREEIF(size);
PR_FREEIF(perm);
PR_FREEIF(dir);
PR_FREEIF(mode);
PR_FREEIF(name);
PR_FREEIF(url);
PR_FREEIF(site);
PR_FREEIF(svr);
PR_FREEIF(subj);
}
#ifdef XP_MACOSX
done:
#endif
return status;
}
#if 0
#if defined(DEBUG) && defined(XP_UNIX)
static int
MimeExternalBody_debug_print (MimeObject *obj, PRFileDesc *stream, int32_t depth)
{
MimeExternalBody *bod = (MimeExternalBody *) obj;
int i;
char *ct, *ct2;
char *addr = mime_part_address(obj);
if (obj->headers)
ct = MimeHeaders_get (obj->headers, HEADER_CONTENT_TYPE, false, false);
if (bod->hdrs)
ct2 = MimeHeaders_get (bod->hdrs, HEADER_CONTENT_TYPE, false, false);
for (i=0; i < depth; i++)
PR_Write(stream, " ", 2);
/***
fprintf(stream,
"<%s %s\n"
"\tcontent-type: %s\n"
"\tcontent-type: %s\n"
"\tBody:%s\n\t0x%08X>\n\n",
obj->clazz->class_name,
addr ? addr : "???",
ct ? ct : "<none>",
ct2 ? ct2 : "<none>",
bod->body ? bod->body : "<none>",
(uint32_t) obj);
***/
PR_FREEIF(addr);
PR_FREEIF(ct);
PR_FREEIF(ct2);
return 0;
}
#endif
#endif /* 0 */
static bool
MimeExternalBody_displayable_inline_p (MimeObjectClass *clazz,
MimeHeaders *hdrs)
{
char *ct = MimeHeaders_get (hdrs, HEADER_CONTENT_TYPE, false, false);
char *at = MimeHeaders_get_parameter(ct, "access-type", NULL, NULL);
bool inline_p = false;
if (!at)
;
else if (!PL_strcasecmp(at, "ftp") ||
!PL_strcasecmp(at, "anon-ftp") ||
!PL_strcasecmp(at, "local-file") ||
!PL_strcasecmp(at, "mail-server") ||
!PL_strcasecmp(at, "url"))
inline_p = true;
#ifdef XP_UNIX
else if (!PL_strcasecmp(at, "afs")) /* only if there is a /afs/ directory */
{
nsCOMPtr <nsIFile> fs = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
bool exists = false;
if (fs)
{
fs->InitWithNativePath(NS_LITERAL_CSTRING("/afs/."));
fs->Exists(&exists);
}
if (!exists)
return 0;
inline_p = true;
}
#endif /* XP_UNIX */
PR_FREEIF(ct);
PR_FREEIF(at);
return inline_p;
}

View file

@ -0,0 +1,37 @@
/* -*- 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/. */
#ifndef _MIMEEBOD_H_
#define _MIMEEBOD_H_
#include "mimeobj.h"
/* The MimeExternalBody class implements the message/external-body MIME type.
(This is not to be confused with MimeExternalObject, which implements the
handler for application/octet-stream and other types with no more specific
handlers.)
*/
typedef struct MimeExternalBodyClass MimeExternalBodyClass;
typedef struct MimeExternalBody MimeExternalBody;
struct MimeExternalBodyClass {
MimeObjectClass object;
};
extern MimeExternalBodyClass mimeExternalBodyClass;
struct MimeExternalBody {
MimeObject object; /* superclass variables */
MimeHeaders *hdrs; /* headers within this external-body, which
describe the network data which this body
is a pointer to. */
char *body; /* The "phantom body" of this link. */
};
#define MimeExternalBodyClassInitializer(ITYPE,CSUPER) \
{ MimeObjectClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEEBOD_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,236 @@
/* -*- 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 "nsCOMPtr.h"
#include "mimeeobj.h"
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "nsMimeStringResources.h"
#include "mimemoz2.h"
#include "mimemapl.h"
#include "nsMimeTypes.h"
#define MIME_SUPERCLASS mimeLeafClass
MimeDefClass(MimeExternalObject, MimeExternalObjectClass,
mimeExternalObjectClass, &MIME_SUPERCLASS);
static int MimeExternalObject_initialize (MimeObject *);
static void MimeExternalObject_finalize (MimeObject *);
static int MimeExternalObject_parse_begin (MimeObject *);
static int MimeExternalObject_parse_buffer (const char *, int32_t, MimeObject *);
static int MimeExternalObject_parse_line (const char *, int32_t, MimeObject *);
static int MimeExternalObject_parse_decoded_buffer (const char*, int32_t, MimeObject*);
static bool MimeExternalObject_displayable_inline_p (MimeObjectClass *clazz,
MimeHeaders *hdrs);
static int
MimeExternalObjectClassInitialize(MimeExternalObjectClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeLeafClass *lclass = (MimeLeafClass *) clazz;
NS_ASSERTION(!oclass->class_initialized, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
oclass->initialize = MimeExternalObject_initialize;
oclass->finalize = MimeExternalObject_finalize;
oclass->parse_begin = MimeExternalObject_parse_begin;
oclass->parse_buffer = MimeExternalObject_parse_buffer;
oclass->parse_line = MimeExternalObject_parse_line;
oclass->displayable_inline_p = MimeExternalObject_displayable_inline_p;
lclass->parse_decoded_buffer = MimeExternalObject_parse_decoded_buffer;
return 0;
}
static int
MimeExternalObject_initialize (MimeObject *object)
{
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
}
static void
MimeExternalObject_finalize (MimeObject *object)
{
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
}
static int
MimeExternalObject_parse_begin (MimeObject *obj)
{
int status;
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
if (status < 0) return status;
// If we're writing this object, and we're doing it in raw form, then
// now is the time to inform the backend what the type of this data is.
//
if (obj->output_p &&
obj->options &&
!obj->options->write_html_p &&
!obj->options->state->first_data_written_p)
{
status = MimeObject_output_init(obj, 0);
if (status < 0) return status;
NS_ASSERTION(obj->options->state->first_data_written_p, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
}
//
// If we're writing this object as HTML, do all the work now -- just write
// out a table with a link in it. (Later calls to the `parse_buffer' method
// will simply discard the data of the object itself.)
//
if (obj->options &&
obj->output_p &&
obj->options->write_html_p &&
obj->options->output_fn)
{
MimeDisplayOptions newopt = *obj->options; // copy it
char *id = 0;
char *id_url = 0;
char *id_name = 0;
nsCString id_imap;
bool all_headers_p = obj->options->headers == MimeHeadersAll;
id = mime_part_address (obj);
if (obj->options->missing_parts)
id_imap.Adopt(mime_imap_part_address (obj));
if (! id) return MIME_OUT_OF_MEMORY;
if (obj->options && obj->options->url)
{
const char *url = obj->options->url;
if (!id_imap.IsEmpty() && id)
{
// if this is an IMAP part.
id_url = mime_set_url_imap_part(url, id_imap.get(), id);
}
else
{
// This is just a normal MIME part as usual.
id_url = mime_set_url_part(url, id, true);
}
if (!id_url)
{
PR_Free(id);
return MIME_OUT_OF_MEMORY;
}
}
if (!strcmp (id, "0"))
{
PR_Free(id);
id = MimeGetStringByID(MIME_MSG_ATTACHMENT);
}
else
{
const char *p = "Part ";
uint32_t slen = strlen(p) + strlen(id) + 1;
char *s = (char *)PR_MALLOC(slen);
if (!s)
{
PR_Free(id);
PR_Free(id_url);
return MIME_OUT_OF_MEMORY;
}
// we have a valid id
if (id)
id_name = mime_find_suggested_name_of_part(id, obj);
PL_strncpyz(s, p, slen);
PL_strcatn(s, slen, id);
PR_Free(id);
id = s;
}
if (all_headers_p &&
// Don't bother showing all headers on this part if it's the only
// part in the message: in that case, we've already shown these
// headers.
obj->options->state &&
obj->options->state->root == obj->parent)
all_headers_p = false;
newopt.fancy_headers_p = true;
newopt.headers = (all_headers_p ? MimeHeadersAll : MimeHeadersSome);
/******
RICHIE SHERRY
GOTTA STILL DO THIS FOR QUOTING!
status = MimeHeaders_write_attachment_box (obj->headers, &newopt,
obj->content_type,
obj->encoding,
id_name? id_name : id, id_url, 0)
*****/
// obj->options really owns the storage for this.
newopt.part_to_load = nullptr;
newopt.default_charset = nullptr;
PR_FREEIF(id);
PR_FREEIF(id_url);
PR_FREEIF(id_name);
if (status < 0) return status;
}
return 0;
}
static int
MimeExternalObject_parse_buffer (const char *buffer, int32_t size, MimeObject *obj)
{
NS_ASSERTION(!obj->closed_p, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (obj->closed_p) return -1;
// Currently, we always want to stream, in order to determine the size of the
// MIME object.
/* The data will be base64-decoded and passed to
MimeExternalObject_parse_decoded_buffer. */
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_buffer(buffer, size, obj);
}
static int
MimeExternalObject_parse_decoded_buffer (const char *buf, int32_t size,
MimeObject *obj)
{
/* This is called (by MimeLeafClass->parse_buffer) with blocks of data
that have already been base64-decoded. This will only be called in
the case where we're not emitting HTML, and want access to the raw
data itself.
We override the `parse_decoded_buffer' method provided by MimeLeaf
because, unlike most children of MimeLeaf, we do not want to line-
buffer the decoded data -- we want to simply pass it along to the
backend, without going through our `parse_line' method.
*/
/* Don't do a roundtrip through XPConnect when we're only interested in
* metadata and size. This includes when we are writing HTML (otherwise, the
* contents of binary attachments will just get dumped into messages when
* reading them) and the JS emitter (which doesn't care about attachment data
* at all). 0 means ok, the caller just checks for negative return value.
*/
if (obj->options && (obj->options->metadata_only ||
obj->options->write_html_p))
return 0;
else
return MimeObject_write(obj, buf, size, true);
}
static int
MimeExternalObject_parse_line (const char *line, int32_t length, MimeObject *obj)
{
NS_ERROR("This method should never be called (externals do no line buffering).");
return -1;
}
static bool
MimeExternalObject_displayable_inline_p (MimeObjectClass *clazz,
MimeHeaders *hdrs)
{
return false;
}

View file

@ -0,0 +1,34 @@
/* -*- 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/. */
#ifndef _MIMEEOBJ_H_
#define _MIMEEOBJ_H_
#include "mimeleaf.h"
/* The MimeExternalObject class represents MIME parts which contain data
which cannot be displayed inline -- application/octet-stream and any
other type that is not otherwise specially handled. (This is not to
be confused with MimeExternalBody, which is the handler for the
message/external-object MIME type only.)
*/
typedef struct MimeExternalObjectClass MimeExternalObjectClass;
typedef struct MimeExternalObject MimeExternalObject;
struct MimeExternalObjectClass {
MimeLeafClass leaf;
};
extern "C" MimeExternalObjectClass mimeExternalObjectClass;
struct MimeExternalObject {
MimeLeaf leaf;
};
#define MimeExternalObjectClassInitializer(ITYPE,CSUPER) \
{ MimeLeafClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEEOBJ_H_ */

View file

@ -0,0 +1,399 @@
/* -*- 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/. */
/* mimefilt.c --- test harness for libmime.a
This program reads a message from stdin and writes the output of the MIME
parser on stdout.
Parameters can be passed to the parser through the usual URL mechanism:
mimefilt BASE-URL?headers=all&rot13 < in > out
Some parameters can't be affected that way, so some additional switches
may be passed on the command line after the URL:
-fancy whether fancy headers should be generated (default)
-no-fancy opposite; this uses the headers used in the cases of
FO_SAVE_AS_TEXT or FO_QUOTE_MESSAGE
-html whether we should convert to HTML (like FO_PRESENT);
this is the default if no ?part= is specified.
-raw don't convert to HTML (FO_SAVE_AS);
this is the default if a ?part= is specified.
-outline at the end, print a debugging overview of the MIME structure
Before any output comes a blurb listing the content-type, charset, and
various other info that would have been put in the generated URL struct.
It's printed to the beginning of the output because otherwise this out-
of-band data would have been lost. (So the output of this program is,
in fact, a raw HTTP response.)
*/
#include "mimemsg.h"
#include "prglobal.h"
#include "key.h"
#include "cert.h"
#include "secrng.h"
#include "secmod.h"
#include "pk11func.h"
#include "nsMimeStringResources.h"
#ifndef XP_UNIX
ERROR! This is a unix-only file for the "mimefilt" standalone program.
This does not go into libmime.a.
#endif
static char *
test_file_type (const char *filename, void *stream_closure)
{
const char *suf = PL_strrchr(filename, '.');
if (!suf)
return 0;
suf++;
if (!PL_strcasecmp(suf, "txt") ||
!PL_strcasecmp(suf, "text"))
return strdup("text/plain");
else if (!PL_strcasecmp(suf, "htm") ||
!PL_strcasecmp(suf, "html"))
return strdup("text/html");
else if (!PL_strcasecmp(suf, "gif"))
return strdup("image/gif");
else if (!PL_strcasecmp(suf, "svg"))
return strdup("image/svg+xml");
else if (!PL_strcasecmp(suf, "jpg") ||
!PL_strcasecmp(suf, "jpeg"))
return strdup("image/jpeg");
else if (!PL_strcasecmp(suf, "pjpg") ||
!PL_strcasecmp(suf, "pjpeg"))
return strdup("image/pjpeg");
else if (!PL_strcasecmp(suf, "xbm"))
return strdup("image/x-xbitmap");
else if (!PL_strcasecmp(suf, "xpm"))
return strdup("image/x-xpixmap");
else if (!PL_strcasecmp(suf, "xwd"))
return strdup("image/x-xwindowdump");
else if (!PL_strcasecmp(suf, "bmp"))
return strdup("image/x-MS-bmp");
else if (!PL_strcasecmp(suf, "au"))
return strdup("audio/basic");
else if (!PL_strcasecmp(suf, "aif") ||
!PL_strcasecmp(suf, "aiff") ||
!PL_strcasecmp(suf, "aifc"))
return strdup("audio/x-aiff");
else if (!PL_strcasecmp(suf, "ps"))
return strdup("application/postscript");
else
return 0;
}
static int
test_output_fn(char *buf, int32_t size, void *closure)
{
FILE *out = (FILE *) closure;
if (out)
return fwrite(buf, sizeof(*buf), size, out);
else
return 0;
}
static int
test_output_init_fn (const char *type,
const char *charset,
const char *name,
const char *x_mac_type,
const char *x_mac_creator,
void *stream_closure)
{
FILE *out = (FILE *) stream_closure;
fprintf(out, "CONTENT-TYPE: %s", type);
if (charset)
fprintf(out, "; charset=\"%s\"", charset);
if (name)
fprintf(out, "; name=\"%s\"", name);
if (x_mac_type || x_mac_creator)
fprintf(out, "; x-mac-type=\"%s\"; x-mac-creator=\"%s\"",
x_mac_type ? x_mac_type : "",
x_mac_creator ? x_mac_type : "");
fprintf(out, CRLF CRLF);
return 0;
}
static void *
test_image_begin(const char *image_url, const char *content_type,
void *stream_closure)
{
return ((void *) strdup(image_url));
}
static void
test_image_end(void *image_closure, int status)
{
char *url = (char *) image_closure;
if (url) PR_Free(url);
}
static char *
test_image_make_image_html(void *image_data)
{
char *url = (char *) image_data;
#if 0
const char *prefix = "<P><CENTER><IMG SRC=\"";
const char *suffix = "\"></CENTER><P>";
#else
const char *prefix = ("<P><CENTER><TABLE BORDER=2 CELLPADDING=20"
" BGCOLOR=WHITE>"
"<TR><TD ALIGN=CENTER>"
"an inlined image would have gone here for<BR>");
const char *suffix = "</TD></TR></TABLE></CENTER><P>";
#endif
uint32_t buflen = strlen (prefix) + strlen (suffix) + strlen (url) + 20;
char *buf = (char *) PR_MALLOC (buflen);
if (!buf) return 0;
*buf = 0;
PL_strcatn (buf, buflen, prefix);
PL_strcatn (buf, buflen, url);
PL_strcatn (buf, buflen, suffix);
return buf;
}
static int test_image_write_buffer(const char *buf, int32_t size, void *image_closure)
{
return 0;
}
static char *
test_passwd_prompt (PK11SlotInfo *slot, void *wincx)
{
char buf[2048], *s;
fprintf(stdout, "#### Password required: ");
s = fgets(buf, sizeof(buf)-1, stdin);
if (!s) return s;
if (s[strlen(s)-1] == '\r' ||
s[strlen(s)-1] == '\n')
s[strlen(s)-1] = '\0';
return s;
}
int
test(FILE *in, FILE *out,
const char *url,
bool fancy_headers_p,
bool html_p,
bool outline_p,
bool dexlate_p,
bool variable_width_plaintext_p)
{
int status = 0;
MimeObject *obj = 0;
MimeDisplayOptions *opt = new MimeDisplayOptions;
// memset(opt, 0, sizeof(*opt));
if (dexlate_p) html_p = false;
opt->fancy_headers_p = fancy_headers_p;
opt->headers = MimeHeadersSome;
opt->rot13_p = false;
status = mime_parse_url_options(url, opt);
if (status < 0)
{
PR_Free(opt);
return MIME_OUT_OF_MEMORY;
}
opt->url = url;
opt->write_html_p = html_p;
opt->dexlate_p = dexlate_p;
opt->output_init_fn = test_output_init_fn;
opt->output_fn = test_output_fn;
opt->charset_conversion_fn= 0;
opt->rfc1522_conversion_p = false;
opt->file_type_fn = test_file_type;
opt->stream_closure = out;
opt->image_begin = test_image_begin;
opt->image_end = test_image_end;
opt->make_image_html = test_image_make_image_html;
opt->image_write_buffer = test_image_write_buffer;
opt->variable_width_plaintext_p = variable_width_plaintext_p;
obj = mime_new ((MimeObjectClass *)&mimeMessageClass,
(MimeHeaders *) NULL,
MESSAGE_RFC822);
if (!obj)
{
PR_Free(opt);
return MIME_OUT_OF_MEMORY;
}
obj->options = opt;
status = obj->class->initialize(obj);
if (status >= 0)
status = obj->class->parse_begin(obj);
if (status < 0)
{
PR_Free(opt);
PR_Free(obj);
return MIME_OUT_OF_MEMORY;
}
while (1)
{
char buf[255];
int size = fread(buf, sizeof(*buf), sizeof(buf), stdin);
if (size <= 0) break;
status = obj->class->parse_buffer(buf, size, obj);
if (status < 0)
{
mime_free(obj);
PR_Free(opt);
return status;
}
}
status = obj->class->parse_eof(obj, false);
if (status >= 0)
status = obj->class->parse_end(obj, false);
if (status < 0)
{
mime_free(obj);
PR_Free(opt);
return status;
}
if (outline_p)
{
fprintf(out, "\n\n"
"###############################################################\n");
obj->class->debug_print(obj, stderr, 0);
fprintf(out,
"###############################################################\n");
}
mime_free (obj);
PR_Free(opt);
return 0;
}
static char *
test_cdb_name_cb (void *arg, int vers)
{
static char f[1024];
if (vers <= 4)
sprintf(f, "%s/.netscape/cert.db", getenv("HOME"));
else
sprintf(f, "%s/.netscape/cert%d.db", getenv("HOME"), vers);
return f;
}
static char *
test_kdb_name_cb (void *arg, int vers)
{
static char f[1024];
if (vers <= 2)
sprintf(f, "%s/.netscape/key.db", getenv("HOME"));
else
sprintf(f, "%s/.netscape/key%d.db", getenv("HOME"), vers);
return f;
}
extern void SEC_Init(void);
int
main (int argc, char **argv)
{
int32_t i = 1;
char *url = "";
bool fancy_p = true;
bool html_p = true;
bool outline_p = false;
bool dexlate_p = false;
char filename[1000];
CERTCertDBHandle *cdb_handle;
SECKEYKeyDBHandle *kdb_handle;
PR_Init("mimefilt", 24, 1, 0);
cdb_handle = (CERTCertDBHandle *) calloc(1, sizeof(*cdb_handle));
if (SECSuccess != CERT_OpenCertDB(cdb_handle, false, test_cdb_name_cb, NULL))
CERT_OpenVolatileCertDB(cdb_handle);
CERT_SetDefaultCertDB(cdb_handle);
RNG_RNGInit();
kdb_handle = SECKEY_OpenKeyDB(false, test_kdb_name_cb, NULL);
SECKEY_SetDefaultKeyDB(kdb_handle);
PK11_SetPasswordFunc(test_passwd_prompt);
sprintf(filename, "%s/.netscape/secmodule.db", getenv("HOME"));
SECMOD_init(filename);
SEC_Init();
if (i < argc)
{
if (argv[i][0] == '-')
url = strdup("");
else
url = argv[i++];
}
if (url &&
(PL_strstr(url, "?part=") ||
PL_strstr(url, "&part=")))
html_p = false;
while (i < argc)
{
if (!strcmp(argv[i], "-fancy"))
fancy_p = true;
else if (!strcmp(argv[i], "-no-fancy"))
fancy_p = false;
else if (!strcmp(argv[i], "-html"))
html_p = true;
else if (!strcmp(argv[i], "-raw"))
html_p = false;
else if (!strcmp(argv[i], "-outline"))
outline_p = true;
else if (!strcmp(argv[i], "-dexlate"))
dexlate_p = true;
else
{
fprintf(stderr,
"usage: %s [ URL [ -fancy | -no-fancy | -html | -raw | -outline | -dexlate ]]\n"
" < message/rfc822 > output\n",
(PL_strrchr(argv[0], '/') ?
PL_strrchr(argv[0], '/') + 1 :
argv[0]));
i = 1;
goto FAIL;
}
i++;
}
i = test(stdin, stdout, url, fancy_p, html_p, outline_p, dexlate_p, true);
fprintf(stdout, "\n");
fflush(stdout);
FAIL:
CERT_ClosePermCertDB(cdb_handle);
SECKEY_CloseKeyDB(kdb_handle);
exit(i);
}

View file

@ -0,0 +1,888 @@
/* -*- 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 "nsCOMPtr.h"
#include "msgCore.h"
#include "mimei.h"
#include "prmem.h"
#include "prlog.h"
#include "plstr.h"
#include "mimebuf.h"
#include "mimemoz2.h"
#include "nsIMimeEmitter.h"
#include "nsMsgMessageFlags.h"
#include "comi18n.h"
#include "nsMailHeaders.h"
#include "msgCore.h"
#include "nsMimeStringResources.h"
#include "mimemoz2.h"
#include "nsMsgI18N.h"
#include "mimehdrs.h"
#include "nsIMIMEHeaderParam.h"
#include "nsNetCID.h"
#include "nsServiceManagerUtils.h"
#include "nsMemory.h"
#include <ctype.h>
#include "nsMsgUtils.h"
// Forward declares...
int32_t MimeHeaders_build_heads_list(MimeHeaders *hdrs);
void
MimeHeaders_convert_header_value(MimeDisplayOptions *opt, nsCString &value,
bool convert_charset_only)
{
if (value.IsEmpty())
return;
if (convert_charset_only)
{
nsAutoCString output;
ConvertRawBytesToUTF8(value, opt->default_charset, output);
value.Assign(output);
return;
}
if (opt && opt->rfc1522_conversion_p)
{
nsAutoCString temporary;
MIME_DecodeMimeHeader(value.get(), opt->default_charset,
opt->override_charset, true, temporary);
if (!temporary.IsEmpty())
{
value = temporary;
}
}
else
{
// This behavior, though highly unusual, was carefully preserved
// from the previous implementation. It may be that this is dead
// code, in which case opt->rfc1522_conversion_p is no longer
// needed.
value.Truncate();
}
}
MimeHeaders *
MimeHeaders_new (void)
{
MimeHeaders *hdrs = (MimeHeaders *) PR_MALLOC(sizeof(MimeHeaders));
if (!hdrs) return 0;
memset(hdrs, 0, sizeof(*hdrs));
hdrs->done_p = false;
return hdrs;
}
void
MimeHeaders_free (MimeHeaders *hdrs)
{
if (!hdrs) return;
PR_FREEIF(hdrs->all_headers);
PR_FREEIF(hdrs->heads);
PR_FREEIF(hdrs->obuffer);
PR_FREEIF(hdrs->munged_subject);
hdrs->obuffer_fp = 0;
hdrs->obuffer_size = 0;
# ifdef DEBUG__
{
int i, size = sizeof(*hdrs);
uint32_t *array = (uint32_t*) hdrs;
for (i = 0; i < (size / sizeof(*array)); i++)
array[i] = (uint32_t) 0xDEADBEEF;
}
# endif /* DEBUG */
PR_Free(hdrs);
}
int
MimeHeaders_parse_line (const char *buffer, int32_t size, MimeHeaders *hdrs)
{
int status = 0;
int desired_size;
NS_ASSERTION(hdrs, "1.22 <rhp@netscape.com> 22 Aug 1999 08:48");
if (!hdrs) return -1;
/* Don't try and feed me more data after having fed me a blank line... */
NS_ASSERTION(!hdrs->done_p, "1.22 <rhp@netscape.com> 22 Aug 1999 08:48");
if (hdrs->done_p) return -1;
if (!buffer || size == 0 || *buffer == '\r' || *buffer == '\n')
{
/* If this is a blank line, we're done.
*/
hdrs->done_p = true;
return MimeHeaders_build_heads_list(hdrs);
}
/* Tack this data on to the end of our copy.
*/
desired_size = hdrs->all_headers_fp + size + 1;
if (desired_size >= hdrs->all_headers_size)
{
status = mime_GrowBuffer (desired_size, sizeof(char), 255,
&hdrs->all_headers, &hdrs->all_headers_size);
if (status < 0) return status;
}
memcpy(hdrs->all_headers+hdrs->all_headers_fp, buffer, size);
hdrs->all_headers_fp += size;
return 0;
}
MimeHeaders *
MimeHeaders_copy (MimeHeaders *hdrs)
{
MimeHeaders *hdrs2;
if (!hdrs) return 0;
hdrs2 = (MimeHeaders *) PR_MALLOC(sizeof(*hdrs));
if (!hdrs2) return 0;
memset(hdrs2, 0, sizeof(*hdrs2));
if (hdrs->all_headers)
{
hdrs2->all_headers = (char *) PR_MALLOC(hdrs->all_headers_fp);
if (!hdrs2->all_headers)
{
PR_Free(hdrs2);
return 0;
}
memcpy(hdrs2->all_headers, hdrs->all_headers, hdrs->all_headers_fp);
hdrs2->all_headers_fp = hdrs->all_headers_fp;
hdrs2->all_headers_size = hdrs->all_headers_fp;
}
hdrs2->done_p = hdrs->done_p;
if (hdrs->heads)
{
int i;
hdrs2->heads = (char **) PR_MALLOC(hdrs->heads_size
* sizeof(*hdrs->heads));
if (!hdrs2->heads)
{
PR_FREEIF(hdrs2->all_headers);
PR_Free(hdrs2);
return 0;
}
hdrs2->heads_size = hdrs->heads_size;
for (i = 0; i < hdrs->heads_size; i++)
{
hdrs2->heads[i] = (hdrs2->all_headers +
(hdrs->heads[i] - hdrs->all_headers));
}
}
return hdrs2;
}
int
MimeHeaders_build_heads_list(MimeHeaders *hdrs)
{
char *s;
char *end;
int i;
NS_ASSERTION(hdrs, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (!hdrs) return -1;
NS_ASSERTION(hdrs->done_p && !hdrs->heads, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (!hdrs->done_p || hdrs->heads)
return -1;
if (hdrs->all_headers_fp == 0)
{
/* Must not have been any headers (we got the blank line right away.) */
PR_FREEIF (hdrs->all_headers);
hdrs->all_headers_size = 0;
return 0;
}
/* At this point, we might as well realloc all_headers back down to the
minimum size it must be (it could be up to 1k bigger.) But don't
bother if we're only off by a tiny bit. */
NS_ASSERTION(hdrs->all_headers_fp <= hdrs->all_headers_size, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (hdrs->all_headers_fp + 60 <= hdrs->all_headers_size)
{
char *ls = (char *)PR_Realloc(hdrs->all_headers, hdrs->all_headers_fp);
if (ls) /* can this ever fail? we're making it smaller... */
{
hdrs->all_headers = ls; /* in case it got relocated */
hdrs->all_headers_size = hdrs->all_headers_fp;
}
}
/* First go through and count up the number of headers in the block.
*/
end = hdrs->all_headers + hdrs->all_headers_fp;
for (s = hdrs->all_headers; s < end; s++)
{
if (s < (end-1) && s[0] == '\r' && s[1] == '\n') /* CRLF -> LF */
s++;
if ((s[0] == '\r' || s[0] == '\n') && /* we're at a newline, and */
(s >= (end-1) || /* we're at EOF, or */
!(s[1] == ' ' || s[1] == '\t'))) /* next char is nonwhite */
hdrs->heads_size++;
}
/* Now allocate storage for the pointers to each of those headers.
*/
hdrs->heads = (char **) PR_MALLOC((hdrs->heads_size + 1) * sizeof(char *));
if (!hdrs->heads)
return MIME_OUT_OF_MEMORY;
memset(hdrs->heads, 0, (hdrs->heads_size + 1) * sizeof(char *));
/* Now make another pass through the headers, and this time, record the
starting position of each header.
*/
i = 0;
hdrs->heads[i++] = hdrs->all_headers;
s = hdrs->all_headers;
while (s < end)
{
SEARCH_NEWLINE:
while (s < end && *s != '\r' && *s != '\n')
s++;
if (s >= end)
break;
/* If "\r\n " or "\r\n\t" is next, that doesn't terminate the header. */
else if (s+2 < end &&
(s[0] == '\r' && s[1] == '\n') &&
(s[2] == ' ' || s[2] == '\t'))
{
s += 3;
goto SEARCH_NEWLINE;
}
/* If "\r " or "\r\t" or "\n " or "\n\t" is next, that doesn't terminate
the header either. */
else if (s+1 < end &&
(s[0] == '\r' || s[0] == '\n') &&
(s[1] == ' ' || s[1] == '\t'))
{
s += 2;
goto SEARCH_NEWLINE;
}
/* At this point, `s' points before a header-terminating newline.
Move past that newline, and store that new position in `heads'.
*/
if (*s == '\r')
s++;
if (s >= end)
break;
if (*s == '\n')
s++;
if (s < end)
{
NS_ASSERTION(! (i > hdrs->heads_size), "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (i > hdrs->heads_size)
return -1;
hdrs->heads[i++] = s;
}
}
return 0;
}
char *
MimeHeaders_get (MimeHeaders *hdrs, const char *header_name,
bool strip_p, bool all_p)
{
int i;
int name_length;
char *result = 0;
if (!hdrs) return 0;
NS_ASSERTION(header_name, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (!header_name) return 0;
/* Specifying strip_p and all_p at the same time doesn't make sense... */
NS_ASSERTION(!(strip_p && all_p), "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
/* One shouldn't be trying to read headers when one hasn't finished
parsing them yet... but this can happen if the message ended
prematurely, and has no body at all (as opposed to a null body,
which is more normal.) So, if we try to read from the headers,
let's assume that the headers are now finished. If they aren't
in fact finished, then a later attempt to write to them will assert.
*/
if (!hdrs->done_p)
{
int status;
hdrs->done_p = true;
status = MimeHeaders_build_heads_list(hdrs);
if (status < 0) return 0;
}
if (!hdrs->heads) /* Must not have been any headers. */
{
NS_ASSERTION(hdrs->all_headers_fp == 0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
return 0;
}
name_length = strlen(header_name);
for (i = 0; i < hdrs->heads_size; i++)
{
char *head = hdrs->heads[i];
char *end = (i == hdrs->heads_size-1
? hdrs->all_headers + hdrs->all_headers_fp
: hdrs->heads[i+1]);
char *colon, *ocolon;
NS_ASSERTION(head, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (!head) continue;
/* Quick hack to skip over BSD Mailbox delimiter. */
if (i == 0 && head[0] == 'F' && !strncmp(head, "From ", 5))
continue;
/* Find the colon. */
for (colon = head; colon < end; colon++)
if (*colon == ':') break;
if (colon >= end) continue;
/* Back up over whitespace before the colon. */
ocolon = colon;
for (; colon > head && IS_SPACE(colon[-1]); colon--)
;
/* If the strings aren't the same length, it doesn't match. */
if (name_length != colon - head )
continue;
/* If the strings differ, it doesn't match. */
if (PL_strncasecmp(header_name, head, name_length))
continue;
/* Otherwise, we've got a match. */
{
char *contents = ocolon + 1;
char *s;
/* Skip over whitespace after colon. */
while (contents < end && IS_SPACE(contents[0])) {
/* Mac or Unix style line break, followed by space or tab. */
if (contents < (end - 1) &&
(contents[0] == '\r' || contents[0] == '\n') &&
(contents[1] == ' ' || contents[1] == '\t'))
contents += 2;
/* Windows style line break, followed by space or tab. */
else if (contents < (end - 2) &&
contents[0] == '\r' && contents[1] == '\n' &&
(contents[2] == ' ' || contents[2] == '\t'))
contents += 3;
/* Any space or tab. */
else if (contents[0] == ' ' || contents[0] == '\t')
contents++;
/* If we get here, it's because this character is a line break
followed by non-whitespace, or a line break followed by
another line break
*/
else {
end = contents;
break;
}
}
/* If we're supposed to strip at the first token, pull `end' back to
the first whitespace or ';' after the first token.
*/
if (strip_p)
{
for (s = contents;
s < end && *s != ';' && *s != ',' && !IS_SPACE(*s);
s++)
;
end = s;
}
/* Now allocate some storage.
If `result' already has a value, enlarge it.
Otherwise, just allocate a block.
`s' gets set to the place where the new data goes.
*/
if (!result)
{
result = (char *) PR_MALLOC(end - contents + 1);
if (!result)
return 0;
s = result;
}
else
{
int32_t L = strlen(result);
s = (char *) PR_Realloc(result, (L + (end - contents + 10)));
if (!s)
{
PR_Free(result);
return 0;
}
result = s;
s = result + L;
/* Since we are tacking more data onto the end of the header
field, we must make it be a well-formed continuation line,
by separating the old and new data with CR-LF-TAB.
*/
*s++ = ','; /* #### only do this for addr headers? */
*s++ = MSG_LINEBREAK[0];
# if (MSG_LINEBREAK_LEN == 2)
*s++ = MSG_LINEBREAK[1];
# endif
*s++ = '\t';
}
/* Take off trailing whitespace... */
while (end > contents && IS_SPACE(end[-1]))
end--;
if (end > contents)
{
/* Now copy the header's contents in...
*/
memcpy(s, contents, end - contents);
s[end - contents] = 0;
}
else
{
s[0] = 0;
}
/* If we only wanted the first occurence of this header, we're done. */
if (!all_p) break;
}
}
if (result && !*result) /* empty string */
{
PR_Free(result);
return 0;
}
return result;
}
char *
MimeHeaders_get_parameter (const char *header_value, const char *parm_name,
char **charset, char **language)
{
if (!header_value || !parm_name || !*header_value || !*parm_name)
return nullptr;
nsresult rv;
nsCOMPtr <nsIMIMEHeaderParam> mimehdrpar =
do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
if (NS_FAILED(rv))
return nullptr;
nsCString result;
rv = mimehdrpar->GetParameterInternal(header_value, parm_name, charset,
language, getter_Copies(result));
return NS_SUCCEEDED(rv) ? PL_strdup(result.get()) : nullptr;
}
#define MimeHeaders_write(HDRS,OPT,DATA,LENGTH) \
MimeOptions_write((HDRS), (OPT), (DATA), (LENGTH), true);
#define MimeHeaders_grow_obuffer(hdrs, desired_size) \
((((long) (desired_size)) >= ((long) (hdrs)->obuffer_size)) ? \
mime_GrowBuffer ((desired_size), sizeof(char), 255, \
&(hdrs)->obuffer, &(hdrs)->obuffer_size) \
: 0)
int
MimeHeaders_write_all_headers (MimeHeaders *hdrs, MimeDisplayOptions *opt, bool attachment)
{
int status = 0;
int i;
bool wrote_any_p = false;
NS_ASSERTION(hdrs, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (!hdrs)
return -1;
/* One shouldn't be trying to read headers when one hasn't finished
parsing them yet... but this can happen if the message ended
prematurely, and has no body at all (as opposed to a null body,
which is more normal.) So, if we try to read from the headers,
let's assume that the headers are now finished. If they aren't
in fact finished, then a later attempt to write to them will assert.
*/
if (!hdrs->done_p)
{
hdrs->done_p = true;
status = MimeHeaders_build_heads_list(hdrs);
if (status < 0) return 0;
}
char *charset = nullptr;
if (opt->format_out == nsMimeOutput::nsMimeMessageSaveAs)
{
if (opt->override_charset)
charset = PL_strdup(opt->default_charset);
else
{
char *contentType = MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, false, false);
if (contentType)
charset = MimeHeaders_get_parameter(contentType, HEADER_PARM_CHARSET, nullptr, nullptr);
PR_FREEIF(contentType);
}
}
for (i = 0; i < hdrs->heads_size; i++)
{
char *head = hdrs->heads[i];
char *end = (i == hdrs->heads_size-1
? hdrs->all_headers + hdrs->all_headers_fp
: hdrs->heads[i+1]);
char *colon, *ocolon;
char *contents = end;
/* Hack for BSD Mailbox delimiter. */
if (i == 0 && head[0] == 'F' && !strncmp(head, "From ", 5))
{
/* For now, we don't really want this header to be output so
we are going to just continue */
continue;
/* colon = head + 4; contents = colon + 1; */
}
else
{
/* Find the colon. */
for (colon = head; colon < end && *colon != ':'; colon++)
;
/* Back up over whitespace before the colon. */
ocolon = colon;
for (; colon > head && IS_SPACE(colon[-1]); colon--)
;
contents = ocolon + 1;
}
/* Skip over whitespace after colon. */
while (contents < end && IS_SPACE(*contents))
contents++;
/* Take off trailing whitespace... */
while (end > contents && IS_SPACE(end[-1]))
end--;
nsAutoCString name(Substring(head, colon));
nsAutoCString hdr_value;
if ( (end - contents) > 0 )
{
hdr_value = Substring(contents, end);
}
// MW Fixme: more?
bool convert_charset_only =
MsgLowerCaseEqualsLiteral(name, "to") || MsgLowerCaseEqualsLiteral(name, "from") ||
MsgLowerCaseEqualsLiteral(name, "cc") || MsgLowerCaseEqualsLiteral(name, "bcc") ||
MsgLowerCaseEqualsLiteral(name, "reply-to") || MsgLowerCaseEqualsLiteral(name, "sender");
MimeHeaders_convert_header_value(opt, hdr_value, convert_charset_only);
// if we're saving as html, we need to convert headers from utf8 to message charset, if any
if (opt->format_out == nsMimeOutput::nsMimeMessageSaveAs && charset)
{
nsAutoCString convertedStr;
if (NS_SUCCEEDED(ConvertFromUnicode(charset, NS_ConvertUTF8toUTF16(hdr_value),
convertedStr)))
{
hdr_value = convertedStr;
}
}
if (attachment) {
if (NS_FAILED(mimeEmitterAddAttachmentField(opt, name.get(), hdr_value.get())))
status = -1;
}
else {
if (NS_FAILED(mimeEmitterAddHeaderField(opt, name.get(), hdr_value.get())))
status = -1;
}
if (status < 0) return status;
if (!wrote_any_p)
wrote_any_p = (status > 0);
}
mimeEmitterAddAllHeaders(opt, hdrs->all_headers, hdrs->all_headers_fp);
PR_FREEIF(charset);
return 1;
}
/* Strip CR+LF runs within (original).
Since the string at (original) can only shrink,
this conversion is done in place. (original)
is returned. */
extern "C" char *
MIME_StripContinuations(char *original)
{
char *p1, *p2;
/* If we were given a null string, return it as is */
if (!original) return NULL;
/* Start source and dest pointers at the beginning */
p1 = p2 = original;
while (*p2) {
/* p2 runs ahead at (CR and/or LF) */
if ((p2[0] == '\r') || (p2[0] == '\n'))
p2++;
else if (p2 > p1)
*p1++ = *p2++;
else {
p1++;
p2++;
}
}
*p1 = '\0';
return original;
}
extern int16_t INTL_DefaultMailToWinCharSetID(int16_t csid);
/* Given text purporting to be a qtext header value, strip backslashes that
may be escaping other chars in the string. */
char *
mime_decode_filename(const char *name, const char *charset,
MimeDisplayOptions *opt)
{
nsresult rv;
nsCOMPtr <nsIMIMEHeaderParam> mimehdrpar =
do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
if (NS_FAILED(rv))
return nullptr;
nsAutoCString result;
rv = mimehdrpar->DecodeParameter(nsDependentCString(name), charset,
opt ? opt->default_charset : nullptr,
opt ? opt->override_charset : false,
result);
return NS_SUCCEEDED(rv) ? PL_strdup(result.get()) : nullptr;
}
/* Pull the name out of some header or another. Order is:
Content-Disposition: XXX; filename=NAME (RFC 1521/1806)
Content-Type: XXX; name=NAME (RFC 1341)
Content-Name: NAME (no RFC, but seen to occur)
X-Sun-Data-Name: NAME (no RFC, but used by MailTool)
*/
char *
MimeHeaders_get_name(MimeHeaders *hdrs, MimeDisplayOptions *opt)
{
char *s = 0, *name = 0, *cvt = 0;
char *charset = nullptr; // for RFC2231 support
s = MimeHeaders_get(hdrs, HEADER_CONTENT_DISPOSITION, false, false);
if (s)
{
name = MimeHeaders_get_parameter(s, HEADER_PARM_FILENAME, &charset, NULL);
PR_Free(s);
}
if (! name)
{
s = MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, false, false);
if (s)
{
free(charset);
name = MimeHeaders_get_parameter(s, HEADER_PARM_NAME, &charset, NULL);
PR_Free(s);
}
}
if (! name)
name = MimeHeaders_get (hdrs, HEADER_CONTENT_NAME, false, false);
if (! name)
name = MimeHeaders_get (hdrs, HEADER_X_SUN_DATA_NAME, false, false);
if (name)
{
/* First remove continuation delimiters (CR+LF+space), then
remove escape ('\\') characters, then attempt to decode
mime-2 encoded-words. The latter two are done in
mime_decode_filename.
*/
MIME_StripContinuations(name);
/* Argh. What we should do if we want to be robust is to decode qtext
in all appropriate headers. Unfortunately, that would be too scary
at this juncture. So just decode qtext/mime2 here. */
cvt = mime_decode_filename(name, charset, opt);
free(charset);
if (cvt && cvt != name)
{
PR_Free(name);
name = cvt;
}
}
return name;
}
#ifdef XP_UNIX
/* This piece of junk is so that I can use BBDB with Mozilla.
= Put bbdb-srv.perl on your path.
= Put bbdb-srv.el on your lisp path.
= Make sure gnudoit (comes with xemacs) is on your path.
= Put (gnuserv-start) in ~/.emacs
= setenv NS_MSG_DISPLAY_HOOK bbdb-srv.perl
*/
void
MimeHeaders_do_unix_display_hook_hack(MimeHeaders *hdrs)
{
static const char *cmd = 0;
if (!cmd)
{
/* The first time we're invoked, look up the command in the
environment. Use "" as the `no command' tag. */
cmd = getenv("NS_MSG_DISPLAY_HOOK");
if (!cmd)
cmd = "";
}
/* Invoke "cmd" at the end of a pipe, and give it the headers on stdin.
The command is expected to be safe from hostile input!!
*/
if (cmd && *cmd)
{
FILE *fp = popen(cmd, "w");
if (fp)
{
fwrite(hdrs->all_headers, 1, hdrs->all_headers_fp, fp);
pclose(fp);
}
}
}
#endif /* XP_UNIX */
static void
MimeHeaders_compact (MimeHeaders *hdrs)
{
NS_ASSERTION(hdrs, "1.22 <rhp@netscape.com> 22 Aug 1999 08:48");
if (!hdrs) return;
PR_FREEIF(hdrs->obuffer);
hdrs->obuffer_fp = 0;
hdrs->obuffer_size = 0;
/* These really shouldn't have gotten out of whack again. */
NS_ASSERTION(hdrs->all_headers_fp <= hdrs->all_headers_size &&
hdrs->all_headers_fp + 100 > hdrs->all_headers_size, "1.22 <rhp@netscape.com> 22 Aug 1999 08:48");
}
/* Writes the headers as text/plain.
This writes out a blank line after the headers, unless
dont_write_content_type is true, in which case the header-block
is not closed off, and none of the Content- headers are written.
*/
int
MimeHeaders_write_raw_headers (MimeHeaders *hdrs, MimeDisplayOptions *opt,
bool dont_write_content_type)
{
int status;
if (hdrs && !hdrs->done_p)
{
hdrs->done_p = true;
status = MimeHeaders_build_heads_list(hdrs);
if (status < 0) return 0;
}
if (!dont_write_content_type)
{
char nl[] = MSG_LINEBREAK;
if (hdrs)
{
status = MimeHeaders_write(hdrs, opt, hdrs->all_headers,
hdrs->all_headers_fp);
if (status < 0) return status;
}
status = MimeHeaders_write(hdrs, opt, nl, strlen(nl));
if (status < 0) return status;
}
else if (hdrs)
{
int32_t i;
for (i = 0; i < hdrs->heads_size; i++)
{
char *head = hdrs->heads[i];
char *end = (i == hdrs->heads_size-1
? hdrs->all_headers + hdrs->all_headers_fp
: hdrs->heads[i+1]);
NS_ASSERTION(head, "1.22 <rhp@netscape.com> 22 Aug 1999 08:48");
if (!head) continue;
/* Don't write out any Content- header. */
if (!PL_strncasecmp(head, "Content-", 8))
continue;
/* Write out this (possibly multi-line) header. */
status = MimeHeaders_write(hdrs, opt, head, end - head);
if (status < 0) return status;
}
}
if (hdrs)
MimeHeaders_compact(hdrs);
return 0;
}
// XXX Fix this XXX //
char *
MimeHeaders_open_crypto_stamp(void)
{
return nullptr;
}
char *
MimeHeaders_finish_open_crypto_stamp(void)
{
return nullptr;
}
char *
MimeHeaders_close_crypto_stamp(void)
{
return nullptr;
}
char *
MimeHeaders_make_crypto_stamp(bool encrypted_p,
bool signed_p,
bool good_p,
bool unverified_p,
bool close_parent_stamp_p,
const char *stamp_url)
{
return nullptr;
}

View file

@ -0,0 +1,88 @@
/* -*- 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/. */
#ifndef _MIMEHDRS_H_
#define _MIMEHDRS_H_
#include "modlmime.h"
/* This file defines the interface to message-header parsing and formatting
code, including conversion to HTML. */
/* Other structs defined later in this file.
*/
/* Creation and destruction.
*/
extern MimeHeaders *MimeHeaders_new (void);
//extern void MimeHeaders_free (MimeHeaders *);
//extern MimeHeaders *MimeHeaders_copy (MimeHeaders *);
/* Feed this method the raw data from which you would like a header
block to be parsed, one line at a time. Feed it a blank line when
you're done. Returns negative on allocation-related failure.
*/
extern int MimeHeaders_parse_line (const char *buffer, int32_t size,
MimeHeaders *hdrs);
/* Converts a MimeHeaders object into HTML, by writing to the provided
output function.
*/
extern int MimeHeaders_write_headers_html (MimeHeaders *hdrs,
MimeDisplayOptions *opt,
bool attachment);
/*
* Writes all headers to the mime emitter.
*/
extern int
MimeHeaders_write_all_headers (MimeHeaders *, MimeDisplayOptions *, bool);
/* Writes the headers as text/plain.
This writes out a blank line after the headers, unless
dont_write_content_type is true, in which case the header-block
is not closed off, and none of the Content- headers are written.
*/
extern int MimeHeaders_write_raw_headers (MimeHeaders *hdrs,
MimeDisplayOptions *opt,
bool dont_write_content_type);
/* Some crypto-related HTML-generated utility routines.
* XXX This may not be needed. XXX
*/
extern char *MimeHeaders_open_crypto_stamp(void);
extern char *MimeHeaders_finish_open_crypto_stamp(void);
extern char *MimeHeaders_close_crypto_stamp(void);
extern char *MimeHeaders_make_crypto_stamp(bool encrypted_p,
bool signed_p,
bool good_p,
bool unverified_p,
bool close_parent_stamp_p,
const char *stamp_url);
/* Does all the heuristic silliness to find the filename in the given headers.
*/
extern char *MimeHeaders_get_name(MimeHeaders *hdrs, MimeDisplayOptions *opt);
extern char *mime_decode_filename(const char *name, const char* charset,
MimeDisplayOptions *opt);
extern "C" char * MIME_StripContinuations(char *original);
/**
* Convert this value to a unicode string, based on the charset.
*/
extern void MimeHeaders_convert_header_value(MimeDisplayOptions *opt,
nsCString &value,
bool convert_charset_only);
#endif /* _MIMEHDRS_H_ */

1920
mailnews/mime/src/mimei.cpp Normal file

File diff suppressed because it is too large Load diff

422
mailnews/mime/src/mimei.h Normal file
View file

@ -0,0 +1,422 @@
/* -*- 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/. */
#ifndef _MIMEI_H_
#define _MIMEI_H_
/*
This module, libmime, implements a general-purpose MIME parser.
One of the methods provided by this parser is the ability to emit
an HTML representation of it.
All Mozilla-specific code is (and should remain) isolated in the
file mimemoz.c. Generally, if the code involves images, netlib
streams it should be in mimemoz.c instead of in the main body of
the MIME parser.
The parser is object-oriented and fully buzzword-compliant.
There is a class for each MIME type, and each class is responsible
for parsing itself, and/or handing the input data off to one of its
child objects.
The class hierarchy is:
MimeObject (abstract)
|
+--- MimeContainer (abstract)
| |
| +--- MimeMultipart (abstract)
| | |
| | +--- MimeMultipartMixed
| | |
| | +--- MimeMultipartDigest
| | |
| | +--- MimeMultipartParallel
| | |
| | +--- MimeMultipartAlternative
| | |
| | +--- MimeMultipartRelated
| | |
| | +--- MimeMultipartAppleDouble
| | |
| | +--- MimeSunAttachment
| | |
| | \--- MimeMultipartSigned (abstract)
| | |
| | \--- MimeMultipartSignedCMS
| |
| +--- MimeEncrypted (abstract)
| | |
| | \--- MimeEncryptedPKCS7
| |
| +--- MimeXlateed (abstract)
| | |
| | \--- MimeXlateed
| |
| +--- MimeMessage
| |
| \--- MimeUntypedText
|
+--- MimeLeaf (abstract)
| |
| +--- MimeInlineText (abstract)
| | |
| | +--- MimeInlineTextPlain
| | | |
| | | \--- MimeInlineTextHTMLAsPlaintext
| | |
| | +--- MimeInlineTextPlainFlowed
| | |
| | +--- MimeInlineTextHTML
| | | |
| | | +--- MimeInlineTextHTMLParsed
| | | |
| | | \--- MimeInlineTextHTMLSanitized
| | |
| | +--- MimeInlineTextRichtext
| | | |
| | | \--- MimeInlineTextEnriched
| | |
| | +--- MimeInlineTextVCard
| |
| +--- MimeInlineImage
| |
| \--- MimeExternalObject
|
\--- MimeExternalBody
=========================================================================
The definition of these classes is somewhat idiosyncratic, since I defined
my own small object system, instead of giving the C++ virus another foothold.
(I would have liked to have written this in Java, but our runtime isn't
quite ready for prime time yet.)
There is one header file and one source file for each class (for example,
the MimeInlineText class is defined in "mimetext.h" and "mimetext.c".)
Each header file follows the following boiler-plate form:
TYPEDEFS: these come first to avoid circular dependencies.
typedef struct FoobarClass FoobarClass;
typedef struct Foobar Foobar;
CLASS DECLARATION:
Theis structure defines the callback routines and other per-class data
of the class defined in this module.
struct FoobarClass {
ParentClass superclass;
...any callbacks or class-variables...
};
CLASS DEFINITION:
This variable holds an instance of the one-and-only class record; the
various instances of this class point to this object. (One interrogates
the type of an instance by comparing the value of its class pointer with
the address of this variable.)
extern FoobarClass foobarClass;
INSTANCE DECLARATION:
Theis structure defines the per-instance data of an object, and a pointer
to the corresponding class record.
struct Foobar {
Parent parent;
...any instance variables...
};
Then, in the corresponding .c file, the following structure is used:
CLASS DEFINITION:
First we pull in the appropriate include file (which includes all necessary
include files for the parent classes) and then we define the class object
using the MimeDefClass macro:
#include "foobar.h"
#define MIME_SUPERCLASS parentlClass
MimeDefClass(Foobar, FoobarClass, foobarClass, &MIME_SUPERCLASS);
The definition of MIME_SUPERCLASS is just to move most of the knowlege of the
exact class hierarchy up to the file's header, instead of it being scattered
through the various methods; see below.
METHOD DECLARATIONS:
We will be putting function pointers into the class object, so we declare
them here. They can generally all be static, since nobody outside of this
file needs to reference them by name; all references to these routines should
be through the class object.
extern int FoobarMethod(Foobar *);
...etc...
CLASS INITIALIZATION FUNCTION:
The MimeDefClass macro expects us to define a function which will finish up
any initialization of the class object that needs to happen before the first
time it is instantiated. Its name must be of the form "<class>Initialize",
and it should initialize the various method slots in the class as
appropriate. Any methods or class variables which this class does not wish
to override will be automatically inherited from the parent class (by virtue
of its class-initialization function having been run first.) Each class
object will only be initialized once.
static int
FoobarClassInitialize(FoobarClass *class)
{
clazz->method = FoobarMethod.
...etc...
}
METHOD DEFINITIONS:
Next come the definitions of the methods we referred to in the class-init
function. The way to access earlier methods (methods defined on the
superclass) is to simply extract them from the superclass's object.
But note that you CANNOT get at methods by indirecting through
object->clazz->superclass: that will only work to one level, and will
go into a loop if some subclass tries to continue on this method.
The easiest way to do this is to make use of the MIME_SUPERCLASS macro that
was defined at the top of the file, as shown below. The alternative to that
involves typing the literal name of the direct superclass of the class
defined in this file, which will be a maintenance headache if the class
hierarchy changes. If you use the MIME_SUPERCLASS idiom, then a textual
change is required in only one place if this class's superclass changes.
static void
Foobar_finalize (MimeObject *object)
{
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object); // RIGHT
parentClass.whatnot.object.finalize(object); // (works...)
object->clazz->superclass->finalize(object); // WRONG!!
}
If you write a libmime content type handler, libmime might create several
instances of your class at once and call e.g. the same finalize code for
3 different objects in a row.
*/
#include "mimehdrs.h"
#include "nsTArray.h"
typedef struct MimeObject MimeObject;
typedef struct MimeObjectClass MimeObjectClass;
#ifdef ENABLE_SMIME
class nsICMSMessage;
#endif // ENABLE_SMIME
/* (I don't pretend to understand this.) */
#define cpp_stringify_noop_helper(x)#x
#define cpp_stringify(x) cpp_stringify_noop_helper(x)
#define MimeObjectClassInitializer(ITYPE,CSUPER) \
cpp_stringify(ITYPE), \
sizeof(ITYPE), \
(MimeObjectClass *) CSUPER, \
(int (*) (MimeObjectClass *)) ITYPE##ClassInitialize, \
0
/* Macro used for setting up class definitions.
*/
#define MimeDefClass(ITYPE,CTYPE,CVAR,CSUPER) \
static int ITYPE##ClassInitialize(ITYPE##Class *); \
ITYPE##Class CVAR = { ITYPE##ClassInitializer(ITYPE,CSUPER) }
/* Creates a new (subclass of) MimeObject of the given class, with the
given headers (which are copied.)
*/
extern MimeObject *mime_new (MimeObjectClass *clazz, MimeHeaders *hdrs,
const char *override_content_type);
/* Destroys a MimeObject (or subclass) and all data associated with it.
*/
extern "C" void mime_free (MimeObject *object);
/* Given a content-type string, finds and returns an appropriate subclass
of MimeObject. A class object is returned. If `exact_match_p' is true,
then only fully-known types will be returned; that is, if it is true,
then "text/x-unknown" will return MimeInlineTextPlainType, but if it is
false, it will return NULL.
*/
extern MimeObjectClass *mime_find_class (const char *content_type,
MimeHeaders *hdrs,
MimeDisplayOptions *opts,
bool exact_match_p);
/** Given a content-type string, creates and returns an appropriate subclass
* of MimeObject. The headers (from which the content-type was presumably
* extracted) are copied. forceInline is set to true when the caller wants
* the function to ignore opts->show_attachment_inline_p and force inline
* display, e.g., mimemalt wants the body part to be shown inline.
*/
extern MimeObject *mime_create (const char *content_type, MimeHeaders *hdrs,
MimeDisplayOptions *opts, bool forceInline = false);
/* Querying the type hierarchy */
extern bool mime_subclass_p(MimeObjectClass *child,
MimeObjectClass *parent);
extern bool mime_typep(MimeObject *obj, MimeObjectClass *clazz);
/* Returns a string describing the location of the part (like "2.5.3").
This is not a full URL, just a part-number.
*/
extern char *mime_part_address(MimeObject *obj);
/* Returns a string describing the location of the *IMAP* part (like "2.5.3").
This is not a full URL, just a part-number.
This part is explicitly passed in the X-Mozilla-IMAP-Part header.
Return value must be freed by the caller.
*/
extern char *mime_imap_part_address(MimeObject *obj);
extern char *mime_external_attachment_url(MimeObject *obj);
/* Puts a part-number into a URL. If append_p is true, then the part number
is appended to any existing part-number already in that URL; otherwise,
it replaces it.
*/
extern char *mime_set_url_part(const char *url, const char *part, bool append_p);
/*
cut the part of url for display a attachment as a email.
*/
extern char *mime_get_base_url(const char *url);
/* Puts an *IMAP* part-number into a URL.
*/
extern char *mime_set_url_imap_part(const char *url, const char *part, const char *libmimepart);
/* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
number matches, and returns the MimeObject (else NULL.)
(part is not a URL -- it's of the form "1.3.5".)
*/
extern MimeObject *mime_address_to_part(const char *part, MimeObject *obj);
/* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
number matches; if one is found, returns the Content-Name of that part.
Else returns NULL. (part is not a URL -- it's of the form "1.3.5".)
*/
extern char *mime_find_suggested_name_of_part(const char *part,
MimeObject *obj);
/* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
number matches; if one is found, returns the Content-Name of that part.
Else returns NULL. (part is not a URL -- it's of the form "1.3.5".)
*/
extern char *mime_find_content_type_of_part(const char *part, MimeObject *obj);
/* Parse the various "?" options off the URL and into the options struct.
*/
extern int mime_parse_url_options(const char *url, MimeDisplayOptions *);
#ifdef ENABLE_SMIME
/* Asks whether the given object is one of the cryptographically signed
or encrypted objects that we know about. (MimeMessageClass uses this
to decide if the headers need to be presented differently.)
*/
extern bool mime_crypto_object_p(MimeHeaders *, bool clearsigned_counts, MimeDisplayOptions *);
/* Tells whether the given MimeObject is a message which has been encrypted
or signed. (Helper for MIME_GetMessageCryptoState()).
*/
extern void mime_get_crypto_state (MimeObject *obj,
bool *signed_p, bool *encrypted_p,
bool *signed_ok, bool *encrypted_ok);
/* Whether the given object has written out the HTML version of its headers
in such a way that it will have a "crypto stamp" next to the headers. If
this is true, then the child must write out its HTML slightly differently
to take this into account...
*/
extern bool mime_crypto_stamped_p(MimeObject *obj);
/* How the crypto code tells the MimeMessage object what the crypto stamp
on it says. */
extern void mime_set_crypto_stamp(MimeObject *obj,
bool signed_p, bool encrypted_p);
#endif // ENABLE_SMIME
class MimeParseStateObject {
public:
MimeParseStateObject()
{root = 0; separator_queued_p = false; separator_suppressed_p = false;
first_part_written_p = false; post_header_html_run_p = false; first_data_written_p = false;
decrypted_p = false; strippingPart = false;
}
MimeObject *root; /* The outermost parser object. */
bool separator_queued_p; /* Whether a separator should be written out
before the next text is written (this lets
us write separators lazily, so that one
doesn't appear at the end, and so that more
than one don't appear in a row.) */
bool separator_suppressed_p; /* Whether the currently-queued separator
should not be printed; this is a kludge to
prevent seps from being printed just after
a header block... */
bool first_part_written_p; /* State used for the `Show Attachments As
Links' kludge. */
bool post_header_html_run_p; /* Whether we've run the
options->generate_post_header_html_fn */
bool first_data_written_p; /* State used for Mozilla lazy-stream-
creation evilness. */
bool decrypted_p; /* If options->dexlate_p is true, then this
will be set to indicate whether any
dexlateion did in fact occur.
*/
nsTArray<nsCString> partsToStrip; /* if we're stripping parts, what parts to strip */
nsTArray<nsCString> detachToFiles; /* if we're detaching parts, where each part was detached to */
bool strippingPart;
nsCString detachedFilePath; /* if we've detached this part, filepath of detached part */
};
/* Some output-generation utility functions...
*/
extern int MimeObject_output_init(MimeObject *obj, const char *content_type);
/* The `user_visible_p' argument says whether the output that has just been
written will cause characters or images to show up on the screen, that
is, it should be false if the stuff being written is merely structural
HTML or whitespace ("<P>", "</TABLE>", etc.) This information is used
when making the decision of whether a separating <HR> is needed.
*/
extern int MimeObject_write(MimeObject *, const char *data, int32_t length,
bool user_visible_p);
extern int MimeOptions_write(MimeHeaders *,
MimeDisplayOptions *,
const char *data, int32_t length,
bool user_visible_p);
/* Writes out the right kind of HR (or rather, queues it for writing.) */
extern int MimeObject_write_separator(MimeObject *);
extern bool MimeObjectIsMessageBody(MimeObject *obj);
struct MimeDisplayData { /* This struct is what we hang off of
(context)->mime_data, to remember info
about the last MIME object we've
parsed and displayed. See
MimeGuessURLContentName() below.
*/
MimeObject *last_parsed_object;
char *last_parsed_url;
};
#endif /* _MIMEI_H_ */

View file

@ -0,0 +1,249 @@
/* -*- 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 "nsCOMPtr.h"
#include "mimeiimg.h"
#include "mimemoz2.h"
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "nsMimeTypes.h"
#include "nsMimeStringResources.h"
#include "nsINetUtil.h"
#include "nsMsgUtils.h"
#define MIME_SUPERCLASS mimeLeafClass
MimeDefClass(MimeInlineImage, MimeInlineImageClass,
mimeInlineImageClass, &MIME_SUPERCLASS);
static int MimeInlineImage_initialize (MimeObject *);
static void MimeInlineImage_finalize (MimeObject *);
static int MimeInlineImage_parse_begin (MimeObject *);
static int MimeInlineImage_parse_line (const char *, int32_t, MimeObject *);
static int MimeInlineImage_parse_eof (MimeObject *, bool);
static int MimeInlineImage_parse_decoded_buffer (const char *, int32_t, MimeObject *);
static int
MimeInlineImageClassInitialize(MimeInlineImageClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeLeafClass *lclass = (MimeLeafClass *) clazz;
NS_ASSERTION(!oclass->class_initialized, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
oclass->initialize = MimeInlineImage_initialize;
oclass->finalize = MimeInlineImage_finalize;
oclass->parse_begin = MimeInlineImage_parse_begin;
oclass->parse_line = MimeInlineImage_parse_line;
oclass->parse_eof = MimeInlineImage_parse_eof;
lclass->parse_decoded_buffer = MimeInlineImage_parse_decoded_buffer;
return 0;
}
static int
MimeInlineImage_initialize (MimeObject *object)
{
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
}
static void
MimeInlineImage_finalize (MimeObject *object)
{
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
}
static int
MimeInlineImage_parse_begin (MimeObject *obj)
{
MimeInlineImage *img = (MimeInlineImage *) obj;
int status;
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
if (status < 0) return status;
if (!obj->output_p) return 0;
if (!obj->options || !obj->options->output_fn ||
// don't bother processing if the consumer doesn't want us
// gunking the body up.
obj->options->write_pure_bodies)
return 0;
if (obj->options &&
obj->options->image_begin &&
obj->options->write_html_p &&
obj->options->image_write_buffer)
{
char *html, *part, *image_url;
const char *ct;
part = mime_part_address(obj);
if (!part) return MIME_OUT_OF_MEMORY;
char *no_part_url = nullptr;
if (obj->options->part_to_load && obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
no_part_url = mime_get_base_url(obj->options->url);
if (no_part_url)
{
image_url = mime_set_url_part(no_part_url, part, true);
PR_Free(no_part_url);
}
else
image_url = mime_set_url_part(obj->options->url, part, true);
if (!image_url)
{
PR_Free(part);
return MIME_OUT_OF_MEMORY;
}
PR_Free(part);
ct = obj->content_type;
if (!ct) ct = IMAGE_GIF; /* Can't happen? Close enough. */
// Fill in content type and attachment name here.
nsAutoCString url_with_filename(image_url);
url_with_filename += "&type=";
url_with_filename += ct;
char * filename = MimeHeaders_get_name ( obj->headers, obj->options );
if (filename)
{
nsCString escapedName;
MsgEscapeString(nsDependentCString(filename), nsINetUtil::ESCAPE_URL_PATH,
escapedName);
url_with_filename += "&filename=";
url_with_filename += escapedName;
PR_Free(filename);
}
// We need to separate images with HR's...
MimeObject_write_separator(obj);
img->image_data =
obj->options->image_begin(url_with_filename.get(), ct, obj->options->stream_closure);
PR_Free(image_url);
if (!img->image_data) return MIME_OUT_OF_MEMORY;
html = obj->options->make_image_html(img->image_data);
if (!html) return MIME_OUT_OF_MEMORY;
status = MimeObject_write(obj, html, strlen(html), true);
PR_Free(html);
if (status < 0) return status;
}
//
// Now we are going to see if we should set the content type in the
// URI for the url being run...
//
if (obj->options && obj->options->stream_closure && obj->content_type)
{
mime_stream_data *msd = (mime_stream_data *) (obj->options->stream_closure);
if ( (msd) && (msd->channel) )
{
msd->channel->SetContentType(nsDependentCString(obj->content_type));
}
}
return 0;
}
static int
MimeInlineImage_parse_eof (MimeObject *obj, bool abort_p)
{
MimeInlineImage *img = (MimeInlineImage *) obj;
int status;
if (obj->closed_p) return 0;
/* Force out any buffered data from the superclass (the base64 decoder.) */
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0) abort_p = true;
if (img->image_data)
{
obj->options->image_end(img->image_data,
(status < 0 ? status : (abort_p ? -1 : 0)));
img->image_data = 0;
}
return status;
}
static int
MimeInlineImage_parse_decoded_buffer (const char *buf, int32_t size, MimeObject *obj)
{
/* This is called (by MimeLeafClass->parse_buffer) with blocks of data
that have already been base64-decoded. Pass this raw image data
along to the backend-specific image display code.
*/
MimeInlineImage *img = (MimeInlineImage *) obj;
int status;
/* Don't do a roundtrip through XPConnect when we're only interested in
* metadata and size. 0 means ok, the caller just checks for negative return
* value
*/
if (obj->options && obj->options->metadata_only)
return 0;
if (obj->output_p &&
obj->options &&
!obj->options->write_html_p)
{
/* in this case, we just want the raw data...
Make the stream, if it's not made, and dump the data out.
*/
if (!obj->options->state->first_data_written_p)
{
status = MimeObject_output_init(obj, 0);
if (status < 0) return status;
NS_ASSERTION(obj->options->state->first_data_written_p, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
}
return MimeObject_write(obj, buf, size, true);
}
if (!obj->options ||
!obj->options->image_write_buffer)
return 0;
/* If we don't have any image data, the image_end method must have already
been called, so don't call image_write_buffer again. */
if (!img->image_data) return 0;
/* Hand this data off to the backend-specific image display stream.
*/
status = obj->options->image_write_buffer (buf, size, img->image_data);
/* If the image display stream fails, then close the stream - but do not
return the failure status, and do not give up on parsing this object.
Just because the image data was corrupt doesn't mean we need to give up
on the whole document; we can continue by just skipping over the rest of
this part, and letting our parent continue.
*/
if (status < 0)
{
obj->options->image_end (img->image_data, status);
img->image_data = 0;
status = 0;
}
return status;
}
static int
MimeInlineImage_parse_line (const char *line, int32_t length, MimeObject *obj)
{
NS_ERROR("This method should never be called (inline images do no line buffering).");
return -1;
}

View file

@ -0,0 +1,35 @@
/* -*- 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/. */
#ifndef _MIMEIIMG_H_
#define _MIMEIIMG_H_
#include "mimeleaf.h"
/* The MimeInlineImage class implements those MIME image types which can be
displayed inline.
*/
typedef struct MimeInlineImageClass MimeInlineImageClass;
typedef struct MimeInlineImage MimeInlineImage;
struct MimeInlineImageClass {
MimeLeafClass leaf;
};
extern MimeInlineImageClass mimeInlineImageClass;
struct MimeInlineImage {
MimeLeaf leaf;
/* Opaque data object for the backend-specific inline-image-display code
(internal-external-reconnect nastiness.) */
void *image_data;
};
#define MimeInlineImageClassInitializer(ITYPE,CSUPER) \
{ MimeLeafClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEIIMG_H_ */

View file

@ -0,0 +1,221 @@
/* -*- 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 "modmimee.h"
#include "mimeleaf.h"
#include "nsMimeTypes.h"
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "nsMimeStringResources.h"
#define MIME_SUPERCLASS mimeObjectClass
MimeDefClass(MimeLeaf, MimeLeafClass, mimeLeafClass, &MIME_SUPERCLASS);
static int MimeLeaf_initialize (MimeObject *);
static void MimeLeaf_finalize (MimeObject *);
static int MimeLeaf_parse_begin (MimeObject *);
static int MimeLeaf_parse_buffer (const char *, int32_t, MimeObject *);
static int MimeLeaf_parse_line (const char *, int32_t, MimeObject *);
static int MimeLeaf_close_decoder (MimeObject *);
static int MimeLeaf_parse_eof (MimeObject *, bool);
static bool MimeLeaf_displayable_inline_p (MimeObjectClass *clazz,
MimeHeaders *hdrs);
static int
MimeLeafClassInitialize(MimeLeafClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
NS_ASSERTION(!oclass->class_initialized, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
oclass->initialize = MimeLeaf_initialize;
oclass->finalize = MimeLeaf_finalize;
oclass->parse_begin = MimeLeaf_parse_begin;
oclass->parse_buffer = MimeLeaf_parse_buffer;
oclass->parse_line = MimeLeaf_parse_line;
oclass->parse_eof = MimeLeaf_parse_eof;
oclass->displayable_inline_p = MimeLeaf_displayable_inline_p;
clazz->close_decoder = MimeLeaf_close_decoder;
/* Default `parse_buffer' method is one which line-buffers the now-decoded
data and passes it on to `parse_line'. (We snarf the implementation of
this method from our superclass's implementation of `parse_buffer', which
inherited it from MimeObject.)
*/
clazz->parse_decoded_buffer =
((MimeObjectClass*)&MIME_SUPERCLASS)->parse_buffer;
return 0;
}
static int
MimeLeaf_initialize (MimeObject *obj)
{
/* This is an abstract class; it shouldn't be directly instantiated. */
NS_ASSERTION(obj->clazz != (MimeObjectClass *) &mimeLeafClass, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
// Initial size is -1 (meaning "unknown size") - we'll correct it in
// parse_buffer.
MimeLeaf *leaf = (MimeLeaf *) obj;
leaf->sizeSoFar = -1;
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(obj);
}
static void
MimeLeaf_finalize (MimeObject *object)
{
MimeLeaf *leaf = (MimeLeaf *)object;
object->clazz->parse_eof (object, false);
/* Free the decoder data, if it's still around. It was probably freed
in MimeLeaf_parse_eof(), but just in case... */
if (leaf->decoder_data)
{
MimeDecoderDestroy(leaf->decoder_data, true);
leaf->decoder_data = 0;
}
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize (object);
}
static int
MimeLeaf_parse_begin (MimeObject *obj)
{
MimeLeaf *leaf = (MimeLeaf *) obj;
MimeDecoderData *(*fn) (MimeConverterOutputCallback, void*) = 0;
/* Initialize a decoder if necessary.
*/
if (!obj->encoding ||
// If we need the object as "raw" for saving or forwarding,
// don't decode text parts of message types. Other output formats,
// like "display" (nsMimeMessageBodyDisplay), need decoding.
(obj->options->format_out == nsMimeOutput::nsMimeMessageRaw &&
obj->parent &&
(!PL_strcasecmp(obj->parent->content_type, MESSAGE_NEWS) ||
!PL_strcasecmp(obj->parent->content_type, MESSAGE_RFC822)) &&
!PL_strncasecmp(obj->content_type, "text/", 5)))
/* no-op */ ;
else if (!PL_strcasecmp(obj->encoding, ENCODING_BASE64))
fn = &MimeB64DecoderInit;
else if (!PL_strcasecmp(obj->encoding, ENCODING_QUOTED_PRINTABLE))
leaf->decoder_data =
MimeQPDecoderInit(((MimeConverterOutputCallback)
((MimeLeafClass *)obj->clazz)->parse_decoded_buffer),
obj, obj);
else if (!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE) ||
!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE2) ||
!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE3) ||
!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE4))
fn = &MimeUUDecoderInit;
else if (!PL_strcasecmp(obj->encoding, ENCODING_YENCODE))
fn = &MimeYDecoderInit;
if (fn)
{
leaf->decoder_data =
fn (/* The MimeConverterOutputCallback cast is to turn the `void' argument
into `MimeObject'. */
((MimeConverterOutputCallback)
((MimeLeafClass *)obj->clazz)->parse_decoded_buffer),
obj);
if (!leaf->decoder_data)
return MIME_OUT_OF_MEMORY;
}
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
}
static int
MimeLeaf_parse_buffer (const char *buffer, int32_t size, MimeObject *obj)
{
MimeLeaf *leaf = (MimeLeaf *) obj;
NS_ASSERTION(!obj->closed_p, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
if (obj->closed_p) return -1;
/* If we're not supposed to write this object, bug out now.
*/
if (!obj->output_p ||
!obj->options ||
!obj->options->output_fn)
return 0;
int rv;
if (leaf->sizeSoFar == -1)
leaf->sizeSoFar = 0;
if (leaf->decoder_data &&
obj->options &&
obj->options->format_out != nsMimeOutput::nsMimeMessageDecrypt
&& obj->options->format_out != nsMimeOutput::nsMimeMessageAttach) {
int outSize = 0;
rv = MimeDecoderWrite (leaf->decoder_data, buffer, size, &outSize);
leaf->sizeSoFar += outSize;
}
else {
rv = ((MimeLeafClass *)obj->clazz)->parse_decoded_buffer (buffer, size,
obj);
leaf->sizeSoFar += size;
}
return rv;
}
static int
MimeLeaf_parse_line (const char *line, int32_t length, MimeObject *obj)
{
NS_ERROR("MimeLeaf_parse_line shouldn't ever be called.");
return -1;
}
static int
MimeLeaf_close_decoder (MimeObject *obj)
{
MimeLeaf *leaf = (MimeLeaf *) obj;
if (leaf->decoder_data)
{
int status = MimeDecoderDestroy(leaf->decoder_data, false);
leaf->decoder_data = 0;
return status;
}
return 0;
}
static int
MimeLeaf_parse_eof (MimeObject *obj, bool abort_p)
{
MimeLeaf *leaf = (MimeLeaf *) obj;
if (obj->closed_p) return 0;
/* Close off the decoder, to cause it to give up any buffered data that
it is still holding.
*/
if (leaf->decoder_data)
{
int status = MimeLeaf_close_decoder(obj);
if (status < 0) return status;
}
/* Now run the superclass's parse_eof, which will force out the line
buffer (which we may have just repopulated, above.)
*/
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof (obj, abort_p);
}
static bool
MimeLeaf_displayable_inline_p (MimeObjectClass *clazz, MimeHeaders *hdrs)
{
return true;
}

View file

@ -0,0 +1,59 @@
/* -*- 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/. */
#ifndef _MIMELEAF_H_
#define _MIMELEAF_H_
#include "mimeobj.h"
#include "modmimee.h"
/* MimeLeaf is the class for the objects representing all MIME types which
are not containers for other MIME objects. The implication of this is
that they are MIME types which can have Content-Transfer-Encodings
applied to their data. This class provides that service in its
parse_buffer() method:
int (*parse_decoded_buffer) (const char *buf, int32_t size, MimeObject *obj)
The `parse_buffer' method of MimeLeaf passes each block of data through
the appropriate decoder (if any) and then calls `parse_decoded_buffer'
on each block (not line) of output.
The default `parse_decoded_buffer' method of MimeLeaf line-buffers the
now-decoded data, handing each line to the `parse_line' method in turn.
If different behavior is desired (for example, if a class wants access
to the decoded data before it is line-buffered) the `parse_decoded_buffer'
method should be overridden. (MimeExternalObject does this.)
*/
typedef struct MimeLeafClass MimeLeafClass;
typedef struct MimeLeaf MimeLeaf;
struct MimeLeafClass {
MimeObjectClass object;
/* This is the callback that is handed to the decoder. */
int (*parse_decoded_buffer) (const char *buf, int32_t size, MimeObject *obj);
int (*close_decoder) (MimeObject *obj);
};
extern MimeLeafClass mimeLeafClass;
struct MimeLeaf {
MimeObject object; /* superclass variables */
/* If we're doing Base64, Quoted-Printable, or UU decoding, this is the
state object for the decoder. */
MimeDecoderData *decoder_data;
/* We want to count the size of the MimeObject to offer consumers the
* opportunity to display the sizes of attachments.
*/
int sizeSoFar;
};
#define MimeLeafClassInitializer(ITYPE,CSUPER) \
{ MimeObjectClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMELEAF_H_ */

View file

@ -0,0 +1,580 @@
/* -*- 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/. */
/*
BACKGROUND
----------
At the simplest level, multipart/alternative means "pick one of these and
display it." However, it's actually a lot more complicated than that.
The alternatives are in preference order, and counterintuitively, they go
from *least* to *most* preferred rather than the reverse. Therefore, when
we're parsing, we can't just take the first one we like and throw the rest
away -- we have to parse through the whole thing, discarding the n'th part if
we are capable of displaying the n+1'th.
Adding a wrinkle to that is the fact that we give the user the option of
demanding the plain-text alternative even though we are perfectly capable of
displaying the HTML, and it is almost always the preferred format, i.e., it
almost always comes after the plain-text alternative.
Speaking of which, you can't assume that each of the alternatives is just a
basic text/[whatever]. There may be, for example, a text/plain followed by a
multipart/related which contains text/html and associated embedded
images. Yikes!
You also can't assume that there will be just two parts. There can be an
arbitrary number, and the ones we are capable of displaying and the ones we
aren't could be interspersed in any order by the producer of the MIME.
We can't just throw away the parts we're not displaying when we're processing
the MIME for display. If we were to do that, then the MIME parts that
remained wouldn't get numbered properly, and that would mean, for example,
that deleting attachments wouldn't work in some messages. Indeed, that very
problem is what prompted a rewrite of this file into its current
architecture.
ARCHITECTURE
------------
Parts are read and queued until we know whether we're going to display
them. If the first pending part is one we don't know how to display, then we
can add it to the MIME structure immediatelly, with output_p disabled. If the
first pending part is one we know how to display, then we can't add it to the
in-memory MIME structure until either (a) we encounter a later, more
preferred part we know how to display, or (b) we reach the end of the
parts. A display-capable part of the queue may be followed by one or more
display-incapable parts. We can't add them to the in-memory structure until
we figure out what to do with the first, display-capable pending part,
because otherwise the order and numbering will be wrong. All of the logic in
this paragraph is implemented in the flush_children function.
The display_cached_part function is what actually adds a MIME part to the
in-memory MIME structure. There is one complication there which forces us to
violate abstrations... Even if we set output_p on a child before adding it to
the parent, the parse_begin function resets it. The kluge I came up with to
prevent that was to give the child a separate options object and set
output_fn to nullptr in it, because that causes parse_begin to set output_p to
false. This seemed like the least onerous way to accomplish this, although I
can't say it's a solution I'm particularly fond of.
Another complication in display_cached_part is that if we were just a normal
multipart type, we could rely on MimeMultipart_parse_line to notify emitters
about content types, character sets, part numbers, etc. as our new children
get created. However, since we defer creation of some children, the
notification doesn't happen there, so we have to handle it
ourselves. Unfortunately, this requires a small abstraction violation in
MimeMultipart_parse_line -- we have to check there if the entity is
multipart/alternative and if so not notify emitters there because
MimeMultipartAlternative_create_child handles it.
- Jonathan Kamens, 2010-07-23
When the option prefer_plaintext is on, the last text/plain part
should be preferred over any other part that can be displayed. But
if no text/plain part is found, then the algorithm should go as
normal and convert any html part found to text. To achive this I
found that the simplest way was to change the function display_part_p
into returning priority as an integer instead of boolean can/can't
display. Then I also changed the function flush_children so it selects
the last part with the highest priority. (Priority 0 means it cannot
be displayed and the part is never choosen.)
- Terje Bråten, 2013-02-16
*/
#include "mimemalt.h"
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "nsMimeTypes.h"
#include "nsMimeStringResources.h"
#include "nsIPrefBranch.h"
#include "mimemoz2.h" // for prefs
extern "C" MimeObjectClass mimeMultipartRelatedClass;
#define MIME_SUPERCLASS mimeMultipartClass
MimeDefClass(MimeMultipartAlternative, MimeMultipartAlternativeClass,
mimeMultipartAlternativeClass, &MIME_SUPERCLASS);
static int MimeMultipartAlternative_initialize (MimeObject *);
static void MimeMultipartAlternative_finalize (MimeObject *);
static int MimeMultipartAlternative_parse_eof (MimeObject *, bool);
static int MimeMultipartAlternative_create_child(MimeObject *);
static int MimeMultipartAlternative_parse_child_line (MimeObject *, const char *,
int32_t, bool);
static int MimeMultipartAlternative_close_child(MimeObject *);
static int MimeMultipartAlternative_flush_children(MimeObject *, bool, priority_t);
static priority_t MimeMultipartAlternative_display_part_p(MimeObject *self,
MimeHeaders *sub_hdrs);
static priority_t MimeMultipartAlternative_prioritize_part(char *content_type,
bool prefer_plaintext);
static int MimeMultipartAlternative_display_cached_part(MimeObject *,
MimeHeaders *,
MimePartBufferData *,
bool);
static int
MimeMultipartAlternativeClassInitialize(MimeMultipartAlternativeClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeMultipartClass *mclass = (MimeMultipartClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
oclass->initialize = MimeMultipartAlternative_initialize;
oclass->finalize = MimeMultipartAlternative_finalize;
oclass->parse_eof = MimeMultipartAlternative_parse_eof;
mclass->create_child = MimeMultipartAlternative_create_child;
mclass->parse_child_line = MimeMultipartAlternative_parse_child_line;
mclass->close_child = MimeMultipartAlternative_close_child;
return 0;
}
static int
MimeMultipartAlternative_initialize (MimeObject *obj)
{
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
NS_ASSERTION(!malt->part_buffers, "object initialized multiple times");
NS_ASSERTION(!malt->buffered_hdrs, "object initialized multiple times");
malt->pending_parts = 0;
malt->max_parts = 0;
malt->buffered_priority = PRIORITY_UNDISPLAYABLE;
malt->buffered_hdrs = nullptr;
malt->part_buffers = nullptr;
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(obj);
}
static void
MimeMultipartAlternative_cleanup(MimeObject *obj)
{
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
int32_t i;
for (i = 0; i < malt->pending_parts; i++) {
MimeHeaders_free(malt->buffered_hdrs[i]);
MimePartBufferDestroy(malt->part_buffers[i]);
}
PR_FREEIF(malt->buffered_hdrs);
PR_FREEIF(malt->part_buffers);
malt->pending_parts = 0;
malt->max_parts = 0;
}
static void
MimeMultipartAlternative_finalize (MimeObject *obj)
{
MimeMultipartAlternative_cleanup(obj);
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj);
}
static int
MimeMultipartAlternative_flush_children(MimeObject *obj,
bool finished,
priority_t next_priority)
{
/*
The cache should always have at the head the part with highest priority.
Possible states:
1. Cache contains nothing: do nothing.
2. Finished, and the cache contains one displayable body followed
by zero or more bodies with lower priority:
3. Finished, and the cache contains one non-displayable body:
create it with output off.
4. Not finished, and the cache contains one displayable body
followed by zero or more bodies with lower priority, and the new
body we're about to create is higher or equal priority:
create all cached bodies with output off.
5. Not finished, and the cache contains one displayable body
followed by zero or more bodies with lower priority, and the new
body we're about to create has lower priority: do nothing.
6. Not finished, and the cache contains one non-displayable body:
create it with output off.
*/
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
bool have_displayable, do_flush, do_display;
/* Case 1 */
if (! malt->pending_parts)
return 0;
have_displayable = (malt->buffered_priority > next_priority);
if (finished && have_displayable) {
/* Case 2 */
do_flush = true;
do_display = true;
}
else if (finished && ! have_displayable) {
/* Case 3 */
do_flush = true;
do_display = false;
}
else if (! finished && have_displayable) {
/* Case 5 */
do_flush = false;
do_display = false;
}
else if (! finished && ! have_displayable) {
/* Case 4 */
/* Case 6 */
do_flush = true;
do_display = false;
}
else {
NS_ERROR("mimemalt.cpp: logic error in flush_children");
return -1;
}
if (do_flush) {
int32_t i;
for (i = 0; i < malt->pending_parts; i++) {
MimeMultipartAlternative_display_cached_part(obj,
malt->buffered_hdrs[i],
malt->part_buffers[i],
do_display && (i == 0));
MimeHeaders_free(malt->buffered_hdrs[i]);
MimePartBufferDestroy(malt->part_buffers[i]);
}
malt->pending_parts = 0;
}
return 0;
}
static int
MimeMultipartAlternative_parse_eof (MimeObject *obj, bool abort_p)
{
int status = 0;
if (obj->closed_p) return 0;
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0) return status;
status = MimeMultipartAlternative_flush_children(obj, true,
PRIORITY_UNDISPLAYABLE);
if (status < 0)
return status;
MimeMultipartAlternative_cleanup(obj);
return status;
}
static int
MimeMultipartAlternative_create_child(MimeObject *obj)
{
MimeMultipart *mult = (MimeMultipart *) obj;
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
priority_t priority =
MimeMultipartAlternative_display_part_p (obj, mult->hdrs);
MimeMultipartAlternative_flush_children(obj, false, priority);
mult->state = MimeMultipartPartFirstLine;
int32_t i = malt->pending_parts++;
if (i==0) {
malt->buffered_priority = priority;
}
if (malt->pending_parts > malt->max_parts) {
malt->max_parts = malt->pending_parts;
MimeHeaders **newBuf = (MimeHeaders **)
PR_REALLOC(malt->buffered_hdrs,
malt->max_parts * sizeof(*malt->buffered_hdrs));
NS_ENSURE_TRUE(newBuf, MIME_OUT_OF_MEMORY);
malt->buffered_hdrs = newBuf;
MimePartBufferData **newBuf2 = (MimePartBufferData **)
PR_REALLOC(malt->part_buffers,
malt->max_parts * sizeof(*malt->part_buffers));
NS_ENSURE_TRUE(newBuf2, MIME_OUT_OF_MEMORY);
malt->part_buffers = newBuf2;
}
malt->buffered_hdrs[i] = MimeHeaders_copy(mult->hdrs);
NS_ENSURE_TRUE(malt->buffered_hdrs[i], MIME_OUT_OF_MEMORY);
malt->part_buffers[i] = MimePartBufferCreate();
NS_ENSURE_TRUE(malt->part_buffers[i], MIME_OUT_OF_MEMORY);
return 0;
}
static int
MimeMultipartAlternative_parse_child_line (MimeObject *obj,
const char *line, int32_t length,
bool first_line_p)
{
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
NS_ASSERTION(malt->pending_parts, "should be pending parts, but there aren't");
if (!malt->pending_parts)
return -1;
int32_t i = malt->pending_parts - 1;
/* Push this line into the buffer for later retrieval. */
return MimePartBufferWrite (malt->part_buffers[i], line, length);
}
static int
MimeMultipartAlternative_close_child(MimeObject *obj)
{
MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
MimeMultipart *mult = (MimeMultipart *) obj;
/* PR_ASSERT(malt->part_buffer); Some Mac brokenness trips this...
if (!malt->part_buffer) return -1; */
if (malt->pending_parts)
MimePartBufferClose(malt->part_buffers[malt->pending_parts-1]);
/* PR_ASSERT(mult->hdrs); I expect the Mac trips this too */
if (mult->hdrs) {
MimeHeaders_free(mult->hdrs);
mult->hdrs = 0;
}
return 0;
}
static priority_t
MimeMultipartAlternative_display_part_p(MimeObject *self,
MimeHeaders *sub_hdrs)
{
priority_t priority = PRIORITY_UNDISPLAYABLE;
char *ct = MimeHeaders_get (sub_hdrs, HEADER_CONTENT_TYPE, true, false);
if (!ct)
return priority;
/* RFC 1521 says:
Receiving user agents should pick and display the last format
they are capable of displaying. In the case where one of the
alternatives is itself of type "multipart" and contains unrecognized
sub-parts, the user agent may choose either to show that alternative,
an earlier alternative, or both.
*/
// We must pass 'true' as last parameter so that text/calendar is
// only displayable when Lightning is installed.
MimeObjectClass *clazz = mime_find_class(ct, sub_hdrs, self->options, true);
if (clazz && clazz->displayable_inline_p(clazz, sub_hdrs)) {
// prefer_plaintext pref
bool prefer_plaintext = false;
nsIPrefBranch *prefBranch = GetPrefBranch(self->options);
if (prefBranch) {
prefBranch->GetBoolPref("mailnews.display.prefer_plaintext",
&prefer_plaintext);
}
prefer_plaintext = prefer_plaintext &&
(self->options->format_out != nsMimeOutput::nsMimeMessageSaveAs) &&
(self->options->format_out != nsMimeOutput::nsMimeMessageRaw);
priority = MimeMultipartAlternative_prioritize_part(ct, prefer_plaintext);
}
PR_FREEIF(ct);
return priority;
}
/**
* RFC 1521 says we should display the last format we are capable of displaying.
* But for various reasons (mainly to improve the user experience) we choose
* to ignore that in some cases, and rather pick one that we prioritize.
*/
static priority_t
MimeMultipartAlternative_prioritize_part(char *content_type,
bool prefer_plaintext)
{
/*
* PRIORITY_NORMAL is the priority of text/html, multipart/..., etc. that
* we normally display. We should try to have as few exceptions from
* PRIORITY_NORMAL as possible
*/
/* (with no / in the type) */
if (!PL_strcasecmp(content_type, "text")) {
if (prefer_plaintext) {
/* When in plain text view, a plain text part is what we want. */
return PRIORITY_HIGH;
}
/* We normally prefer other parts over the unspecified text type. */
return PRIORITY_TEXT_UNKNOWN;
}
if (!PL_strncasecmp(content_type, "text/", 5)) {
char *text_type = content_type + 5;
if (!PL_strncasecmp(text_type, "plain", 5)) {
if (prefer_plaintext) {
/* When in plain text view,
the text/plain part is exactly what we want */
return PRIORITY_HIGHEST;
}
/*
* Because the html and the text part may be switched,
* or we have an extra text/plain added by f.ex. a buggy virus checker,
* we prioritize text/plain lower than normal.
*/
return PRIORITY_TEXT_PLAIN;
}
if (!PL_strncasecmp(text_type, "calendar", 8) && prefer_plaintext) {
/*
* text/calendar receives an equally high priority so an invitation
* shows even in plaintext mode.
*/
return PRIORITY_HIGHEST;
}
/* Need to white-list all text/... types that are or could be implemented. */
if (!PL_strncasecmp(text_type, "html", 4) ||
!PL_strncasecmp(text_type, "enriched", 8) ||
!PL_strncasecmp(text_type, "richtext", 8) ||
!PL_strncasecmp(text_type, "calendar", 8) ||
!PL_strncasecmp(text_type, "rtf", 3)) {
return PRIORITY_NORMAL;
}
/* We prefer other parts over unknown text types. */
return PRIORITY_TEXT_UNKNOWN;
}
// Guard against rogue messages with incorrect MIME structure and
// don't show images when plain text is requested.
if (!PL_strncasecmp(content_type, "image", 5)) {
if (prefer_plaintext)
return PRIORITY_UNDISPLAYABLE;
else
return PRIORITY_LOW;
}
return PRIORITY_NORMAL;
}
static int
MimeMultipartAlternative_display_cached_part(MimeObject *obj,
MimeHeaders *hdrs,
MimePartBufferData *buffer,
bool do_display)
{
int status;
bool old_options_no_output_p;
char *ct = (hdrs
? MimeHeaders_get (hdrs, HEADER_CONTENT_TYPE, true, false)
: 0);
const char *dct = (((MimeMultipartClass *) obj->clazz)->default_part_type);
MimeObject *body;
/** Don't pass in NULL as the content-type (this means that the
* auto-uudecode-hack won't ever be done for subparts of a
* multipart, but only for untyped children of message/rfc822.
*/
const char *uct = (ct && *ct) ? ct : (dct ? dct: TEXT_PLAIN);
// We always want to display the cached part inline.
body = mime_create(uct, hdrs, obj->options, true);
PR_FREEIF(ct);
if (!body) return MIME_OUT_OF_MEMORY;
body->output_p = do_display;
status = ((MimeContainerClass *) obj->clazz)->add_child(obj, body);
if (status < 0)
{
mime_free(body);
return status;
}
/* add_child assigns body->options from obj->options, but that's
just a pointer so if we muck with it in the child it'll modify
the parent as well, which we definitely don't want. Therefore we
need to make a copy of the old value and restore it later. */
old_options_no_output_p = obj->options->no_output_p;
if (! do_display)
body->options->no_output_p = true;
#ifdef MIME_DRAFTS
/* if this object is a child of a multipart/related object, the parent is
taking care of decomposing the whole part, don't need to do it at this level.
However, we still have to call decompose_file_init_fn and decompose_file_close_fn
in order to set the correct content-type. But don't call MimePartBufferRead
*/
bool multipartRelatedChild = mime_typep(obj->parent,(MimeObjectClass*)&mimeMultipartRelatedClass);
bool decomposeFile = do_display && obj->options &&
obj->options->decompose_file_p &&
obj->options->decompose_file_init_fn &&
!mime_typep(body, (MimeObjectClass *) &mimeMultipartClass);
if (decomposeFile)
{
status = obj->options->decompose_file_init_fn (
obj->options->stream_closure, hdrs);
if (status < 0) return status;
}
#endif /* MIME_DRAFTS */
/* Now that we've added this new object to our list of children,
notify emitters and start its parser going. */
MimeMultipart_notify_emitter(body);
status = body->clazz->parse_begin(body);
if (status < 0) return status;
#ifdef MIME_DRAFTS
if (decomposeFile && !multipartRelatedChild)
status = MimePartBufferRead (buffer,
obj->options->decompose_file_output_fn,
obj->options->stream_closure);
else
#endif /* MIME_DRAFTS */
status = MimePartBufferRead (buffer,
/* The MimeConverterOutputCallback cast is to turn the
`void' argument into `MimeObject'. */
((MimeConverterOutputCallback) body->clazz->parse_buffer),
body);
if (status < 0) return status;
/* Done parsing. */
status = body->clazz->parse_eof(body, false);
if (status < 0) return status;
status = body->clazz->parse_end(body, false);
if (status < 0) return status;
#ifdef MIME_DRAFTS
if (decomposeFile)
{
status = obj->options->decompose_file_close_fn ( obj->options->stream_closure );
if (status < 0) return status;
}
#endif /* MIME_DRAFTS */
/* Restore options to what parent classes expects. */
obj->options->no_output_p = old_options_no_output_p;
return 0;
}

View file

@ -0,0 +1,48 @@
/* -*- 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/. */
#ifndef _MIMEMALT_H_
#define _MIMEMALT_H_
#include "mimemult.h"
#include "mimepbuf.h"
/* The MimeMultipartAlternative class implements the multipart/alternative
MIME container, which displays only one (the `best') of a set of enclosed
documents.
*/
typedef struct MimeMultipartAlternativeClass MimeMultipartAlternativeClass;
typedef struct MimeMultipartAlternative MimeMultipartAlternative;
struct MimeMultipartAlternativeClass {
MimeMultipartClass multipart;
};
extern "C" MimeMultipartAlternativeClass mimeMultipartAlternativeClass;
enum priority_t {PRIORITY_UNDISPLAYABLE,
PRIORITY_LOW,
PRIORITY_TEXT_UNKNOWN,
PRIORITY_TEXT_PLAIN,
PRIORITY_NORMAL,
PRIORITY_HIGH,
PRIORITY_HIGHEST};
struct MimeMultipartAlternative {
MimeMultipart multipart; /* superclass variables */
MimeHeaders **buffered_hdrs; /* The headers of pending parts */
MimePartBufferData **part_buffers; /* The data of pending parts
(see mimepbuf.h) */
int32_t pending_parts;
int32_t max_parts;
priority_t buffered_priority; /* Priority of head of pending parts */
};
#define MimeMultipartAlternativeClassInitializer(ITYPE,CSUPER) \
{ MimeMultipartClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEMALT_H_ */

View file

@ -0,0 +1,189 @@
/* -*- 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 "nsCOMPtr.h"
#include "mimemapl.h"
#include "prmem.h"
#include "plstr.h"
#include "nsMimeStringResources.h"
#include "mimemoz2.h"
#include "nsMimeTypes.h"
#define MIME_SUPERCLASS mimeMultipartClass
MimeDefClass(MimeMultipartAppleDouble, MimeMultipartAppleDoubleClass,
mimeMultipartAppleDoubleClass, &MIME_SUPERCLASS);
static int MimeMultipartAppleDouble_parse_begin (MimeObject *);
static bool MimeMultipartAppleDouble_output_child_p(MimeObject *,
MimeObject *);
static int
MimeMultipartAppleDoubleClassInitialize(MimeMultipartAppleDoubleClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeMultipartClass *mclass = (MimeMultipartClass *) clazz;
NS_ASSERTION(!oclass->class_initialized, "mime class not initialized");
oclass->parse_begin = MimeMultipartAppleDouble_parse_begin;
mclass->output_child_p = MimeMultipartAppleDouble_output_child_p;
return 0;
}
static int
MimeMultipartAppleDouble_parse_begin (MimeObject *obj)
{
/* #### This method is identical to MimeExternalObject_parse_begin
which kinda s#$%s...
*/
int status;
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
if (status < 0) return status;
/* If we're writing this object, and we're doing it in raw form, then
now is the time to inform the backend what the type of this data is.
*/
if (obj->output_p &&
obj->options &&
!obj->options->write_html_p &&
!obj->options->state->first_data_written_p)
{
status = MimeObject_output_init(obj, 0);
if (status < 0) return status;
NS_ASSERTION(obj->options->state->first_data_written_p, "first data not written");
}
#ifdef XP_MACOSX
if (obj->options && obj->options->state)
{
// obj->options->state->separator_suppressed_p = true;
goto done;
}
/*
* It would be nice to not showing the resource fork links
* if we are displaying inline. But, there is no way we could
* know ahead of time that we could display the data fork and
* the data fork is always hidden on MAC platform.
*/
#endif
/* If we're writing this object as HTML, then emit a link for the
multipart/appledouble part (both links) that looks just like the
links that MimeExternalObject emits for external leaf parts.
*/
if (obj->options &&
obj->output_p &&
obj->options->write_html_p &&
obj->options->output_fn)
{
char *id = 0;
char *id_url = 0;
char *id_imap = 0;
id = mime_part_address (obj);
if (! id) return MIME_OUT_OF_MEMORY;
if (obj->options->missing_parts)
id_imap = mime_imap_part_address (obj);
if (obj->options && obj->options->url)
{
const char *url = obj->options->url;
if (id_imap && id)
{
/* if this is an IMAP part. */
id_url = mime_set_url_imap_part(url, id_imap, id);
}
else
{
/* This is just a normal MIME part as usual. */
id_url = mime_set_url_part(url, id, true);
}
if (!id_url)
{
PR_Free(id);
return MIME_OUT_OF_MEMORY;
}
}
/**********************
if (!strcmp (id, "0"))
{
PR_Free(id);
id = MimeGetStringByID(MIME_MSG_ATTACHMENT);
}
else
{
const char *p = "Part ";
char *s = (char *)PR_MALLOC(strlen(p) + strlen(id) + 1);
if (!s)
{
PR_Free(id);
PR_Free(id_url);
return MIME_OUT_OF_MEMORY;
}
PL_strcpy(s, p);
PL_strcat(s, id);
PR_Free(id);
id = s;
}
if (all_headers_p &&
// Don't bother showing all headers on this part if it's the only
// part in the message: in that case, we've already shown these
// headers.
obj->options->state &&
obj->options->state->root == obj->parent)
all_headers_p = false;
newopt.fancy_headers_p = true;
newopt.headers = (all_headers_p ? MimeHeadersAll : MimeHeadersSome);
//
RICHIE SHERRY
GOTTA STILL DO THIS FOR QUOTING!
status = MimeHeaders_write_attachment_box (obj->headers, &newopt,
obj->content_type,
obj->encoding,
id_name? id_name : id, id_url, 0
//
*********************************************************************************/
// FAIL:
PR_FREEIF(id);
PR_FREEIF(id_url);
PR_FREEIF(id_imap);
if (status < 0) return status;
}
#ifdef XP_MACOSX
done:
#endif
return 0;
}
static bool
MimeMultipartAppleDouble_output_child_p(MimeObject *obj, MimeObject *child)
{
MimeContainer *cont = (MimeContainer *) obj;
/* If this is the first child, and it's an application/applefile, then
don't emit a link for it. (There *should* be only two children, and
the first one should always be an application/applefile.)
*/
if (cont->nchildren >= 1 && cont->children[0] == child && child->content_type &&
!PL_strcasecmp(child->content_type, APPLICATION_APPLEFILE))
{
#ifdef XP_MACOSX
if (obj->output_p && obj->options && obj->options->write_html_p) //output HTML
return false;
#else
/* if we are not on a Macintosh, don't emitte the resources fork at all. */
return false;
#endif
}
return true;
}

View file

@ -0,0 +1,32 @@
/* -*- 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/. */
#ifndef _MIMEMAPL_H_
#define _MIMEMAPL_H_
#include "mimemult.h"
/* The MimeMultipartAppleDouble class implements the multipart/appledouble
MIME container, which provides a method of encapsulating and reconstructing
a two-forked Macintosh file.
*/
typedef struct MimeMultipartAppleDoubleClass MimeMultipartAppleDoubleClass;
typedef struct MimeMultipartAppleDouble MimeMultipartAppleDouble;
struct MimeMultipartAppleDoubleClass {
MimeMultipartClass multipart;
};
extern MimeMultipartAppleDoubleClass mimeMultipartAppleDoubleClass;
struct MimeMultipartAppleDouble {
MimeMultipart multipart;
};
#define MimeMultipartAppleDoubleClassInitializer(ITYPE,CSUPER) \
{ MimeMultipartClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEMAPL_H_ */

View file

@ -0,0 +1,470 @@
/* -*- Mode: C++; tab-width: 2; 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 "nsICMSMessage.h"
#include "nsICMSMessageErrors.h"
#include "nsICMSDecoder.h"
#include "nsICryptoHash.h"
#include "mimemcms.h"
#include "mimecryp.h"
#include "nsMimeTypes.h"
#include "nspr.h"
#include "nsMimeStringResources.h"
#include "mimemsg.h"
#include "mimemoz2.h"
#include "nsIURI.h"
#include "nsIMsgWindow.h"
#include "nsIMsgMailNewsUrl.h"
#include "nsIMimeMiscStatus.h"
#include "nsIMsgSMIMEHeaderSink.h"
#include "nsCOMPtr.h"
#include "nsIX509Cert.h"
#include "plstr.h"
#include "nsComponentManagerUtils.h"
#define MIME_SUPERCLASS mimeMultipartSignedClass
MimeDefClass(MimeMultipartSignedCMS, MimeMultipartSignedCMSClass,
mimeMultipartSignedCMSClass, &MIME_SUPERCLASS);
static int MimeMultipartSignedCMS_initialize (MimeObject *);
static void *MimeMultCMS_init (MimeObject *);
static int MimeMultCMS_data_hash (const char *, int32_t, void *);
static int MimeMultCMS_sig_hash (const char *, int32_t, void *);
static int MimeMultCMS_data_eof (void *, bool);
static int MimeMultCMS_sig_eof (void *, bool);
static int MimeMultCMS_sig_init (void *, MimeObject *, MimeHeaders *);
static char * MimeMultCMS_generate (void *);
static void MimeMultCMS_free (void *);
extern int SEC_ERROR_CERT_ADDR_MISMATCH;
static int
MimeMultipartSignedCMSClassInitialize(MimeMultipartSignedCMSClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeMultipartSignedClass *sclass = (MimeMultipartSignedClass *) clazz;
oclass->initialize = MimeMultipartSignedCMS_initialize;
sclass->crypto_init = MimeMultCMS_init;
sclass->crypto_data_hash = MimeMultCMS_data_hash;
sclass->crypto_data_eof = MimeMultCMS_data_eof;
sclass->crypto_signature_init = MimeMultCMS_sig_init;
sclass->crypto_signature_hash = MimeMultCMS_sig_hash;
sclass->crypto_signature_eof = MimeMultCMS_sig_eof;
sclass->crypto_generate_html = MimeMultCMS_generate;
sclass->crypto_free = MimeMultCMS_free;
PR_ASSERT(!oclass->class_initialized);
return 0;
}
static int
MimeMultipartSignedCMS_initialize (MimeObject *object)
{
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
}
typedef struct MimeMultCMSdata
{
int16_t hash_type;
nsCOMPtr<nsICryptoHash> data_hash_context;
nsCOMPtr<nsICMSDecoder> sig_decoder_context;
nsCOMPtr<nsICMSMessage> content_info;
char *sender_addr;
bool decoding_failed;
unsigned char* item_data;
uint32_t item_len;
MimeObject *self;
bool parent_is_encrypted_p;
bool parent_holds_stamp_p;
nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink;
MimeMultCMSdata()
:hash_type(0),
sender_addr(nullptr),
decoding_failed(false),
item_data(nullptr),
self(nullptr),
parent_is_encrypted_p(false),
parent_holds_stamp_p(false)
{
}
~MimeMultCMSdata()
{
PR_FREEIF(sender_addr);
// Do a graceful shutdown of the nsICMSDecoder and release the nsICMSMessage //
if (sig_decoder_context)
{
nsCOMPtr<nsICMSMessage> cinfo;
sig_decoder_context->Finish(getter_AddRefs(cinfo));
}
delete [] item_data;
}
} MimeMultCMSdata;
/* #### MimeEncryptedCMS and MimeMultipartSignedCMS have a sleazy,
incestuous, dysfunctional relationship. */
extern bool MimeEncryptedCMS_encrypted_p (MimeObject *obj);
extern void MimeCMSGetFromSender(MimeObject *obj,
nsCString &from_addr,
nsCString &from_name,
nsCString &sender_addr,
nsCString &sender_name);
extern bool MimeCMSHeadersAndCertsMatch(MimeObject *obj,
nsICMSMessage *,
bool *signing_cert_without_email_address);
extern void MimeCMSRequestAsyncSignatureVerification(nsICMSMessage *aCMSMsg,
const char *aFromAddr, const char *aFromName,
const char *aSenderAddr, const char *aSenderName,
nsIMsgSMIMEHeaderSink *aHeaderSink, int32_t aMimeNestingLevel,
unsigned char* item_data, uint32_t item_len);
extern char *MimeCMS_MakeSAURL(MimeObject *obj);
extern char *IMAP_CreateReloadAllPartsUrl(const char *url);
extern int MIMEGetRelativeCryptoNestLevel(MimeObject *obj);
static void *
MimeMultCMS_init (MimeObject *obj)
{
MimeHeaders *hdrs = obj->headers;
MimeMultCMSdata *data = 0;
char *ct, *micalg;
int16_t hash_type;
nsresult rv;
ct = MimeHeaders_get (hdrs, HEADER_CONTENT_TYPE, false, false);
if (!ct) return 0; /* #### bogus message? out of memory? */
micalg = MimeHeaders_get_parameter (ct, PARAM_MICALG, NULL, NULL);
PR_Free(ct);
ct = 0;
if (!micalg) return 0; /* #### bogus message? out of memory? */
if (!PL_strcasecmp(micalg, PARAM_MICALG_MD5) ||
!PL_strcasecmp(micalg, PARAM_MICALG_MD5_2))
hash_type = nsICryptoHash::MD5;
else if (!PL_strcasecmp(micalg, PARAM_MICALG_SHA1) ||
!PL_strcasecmp(micalg, PARAM_MICALG_SHA1_2) ||
!PL_strcasecmp(micalg, PARAM_MICALG_SHA1_3) ||
!PL_strcasecmp(micalg, PARAM_MICALG_SHA1_4) ||
!PL_strcasecmp(micalg, PARAM_MICALG_SHA1_5))
hash_type = nsICryptoHash::SHA1;
else if (!PL_strcasecmp(micalg, PARAM_MICALG_SHA256) ||
!PL_strcasecmp(micalg, PARAM_MICALG_SHA256_2) ||
!PL_strcasecmp(micalg, PARAM_MICALG_SHA256_3))
hash_type = nsICryptoHash::SHA256;
else if (!PL_strcasecmp(micalg, PARAM_MICALG_SHA384) ||
!PL_strcasecmp(micalg, PARAM_MICALG_SHA384_2) ||
!PL_strcasecmp(micalg, PARAM_MICALG_SHA384_3))
hash_type = nsICryptoHash::SHA384;
else if (!PL_strcasecmp(micalg, PARAM_MICALG_SHA512) ||
!PL_strcasecmp(micalg, PARAM_MICALG_SHA512_2) ||
!PL_strcasecmp(micalg, PARAM_MICALG_SHA512_3))
hash_type = nsICryptoHash::SHA512;
else if (!PL_strcasecmp(micalg, PARAM_MICALG_MD2))
hash_type = nsICryptoHash::MD2;
else
hash_type = -1;
PR_Free(micalg);
micalg = 0;
if (hash_type == -1) return 0; /* #### bogus message? */
data = new MimeMultCMSdata;
if (!data)
return 0;
data->self = obj;
data->hash_type = hash_type;
data->data_hash_context = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
if (NS_FAILED(rv))
{
delete data;
return 0;
}
rv = data->data_hash_context->Init(data->hash_type);
if (NS_FAILED(rv))
{
delete data;
return 0;
}
PR_SetError(0,0);
data->parent_holds_stamp_p =
(obj->parent && mime_crypto_stamped_p(obj->parent));
data->parent_is_encrypted_p =
(obj->parent && MimeEncryptedCMS_encrypted_p (obj->parent));
/* If the parent of this object is a crypto-blob, then it's the grandparent
who would have written out the headers and prepared for a stamp...
(This s##t s$%#s.)
*/
if (data->parent_is_encrypted_p &&
!data->parent_holds_stamp_p &&
obj->parent && obj->parent->parent)
data->parent_holds_stamp_p =
mime_crypto_stamped_p (obj->parent->parent);
mime_stream_data *msd = (mime_stream_data *) (data->self->options->stream_closure);
if (msd)
{
nsIChannel *channel = msd->channel; // note the lack of ref counting...
if (channel)
{
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIMsgWindow> msgWindow;
nsCOMPtr<nsIMsgHeaderSink> headerSink;
nsCOMPtr<nsIMsgMailNewsUrl> msgurl;
nsCOMPtr<nsISupports> securityInfo;
channel->GetURI(getter_AddRefs(uri));
if (uri)
{
nsAutoCString urlSpec;
rv = uri->GetSpec(urlSpec);
// We only want to update the UI if the current mime transaction
// is intended for display.
// If the current transaction is intended for background processing,
// we can learn that by looking at the additional header=filter
// string contained in the URI.
//
// If we find something, we do not set smimeHeaderSink,
// which will prevent us from giving UI feedback.
//
// If we do not find header=filter, we assume the result of the
// processing will be shown in the UI.
if (!strstr(urlSpec.get(), "?header=filter") &&
!strstr(urlSpec.get(), "&header=filter")&&
!strstr(urlSpec.get(), "?header=attach") &&
!strstr(urlSpec.get(), "&header=attach"))
{
msgurl = do_QueryInterface(uri);
if (msgurl)
msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
if (msgWindow)
msgWindow->GetMsgHeaderSink(getter_AddRefs(headerSink));
if (headerSink)
headerSink->GetSecurityInfo(getter_AddRefs(securityInfo));
if (securityInfo)
data->smimeHeaderSink = do_QueryInterface(securityInfo);
}
}
} // if channel
} // if msd
return data;
}
static int
MimeMultCMS_data_hash (const char *buf, int32_t size, void *crypto_closure)
{
MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
if (!data || !data->data_hash_context) {
return -1;
}
PR_SetError(0, 0);
nsresult rv = data->data_hash_context->Update((unsigned char *) buf, size);
data->decoding_failed = NS_FAILED(rv);
return 0;
}
static int
MimeMultCMS_data_eof (void *crypto_closure, bool abort_p)
{
MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
if (!data || !data->data_hash_context) {
return -1;
}
nsAutoCString hashString;
data->data_hash_context->Finish(false, hashString);
PR_SetError(0, 0);
data->item_len = hashString.Length();
data->item_data = new unsigned char[data->item_len];
if (!data->item_data) return MIME_OUT_OF_MEMORY;
memcpy(data->item_data, hashString.get(), data->item_len);
// Release our reference to nsICryptoHash //
data->data_hash_context = nullptr;
/* At this point, data->item.data contains a digest for the first part.
When we process the signature, the security library will compare this
digest to what's in the signature object. */
return 0;
}
static int
MimeMultCMS_sig_init (void *crypto_closure,
MimeObject *multipart_object,
MimeHeaders *signature_hdrs)
{
MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
char *ct;
int status = 0;
nsresult rv;
if (!signature_hdrs) {
return -1;
}
ct = MimeHeaders_get (signature_hdrs, HEADER_CONTENT_TYPE, true, false);
/* Verify that the signature object is of the right type. */
if (!ct || /* is not a signature type */
(PL_strcasecmp(ct, APPLICATION_XPKCS7_SIGNATURE) != 0
&& PL_strcasecmp(ct, APPLICATION_PKCS7_SIGNATURE) != 0)) {
status = -1; /* #### error msg about bogus message */
}
PR_FREEIF(ct);
if (status < 0) return status;
data->sig_decoder_context = do_CreateInstance(NS_CMSDECODER_CONTRACTID, &rv);
if (NS_FAILED(rv)) return 0;
rv = data->sig_decoder_context->Start(nullptr, nullptr);
if (NS_FAILED(rv)) {
status = PR_GetError();
if (status >= 0) status = -1;
}
return status;
}
static int
MimeMultCMS_sig_hash (const char *buf, int32_t size, void *crypto_closure)
{
MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
nsresult rv;
if (!data || !data->sig_decoder_context) {
return -1;
}
rv = data->sig_decoder_context->Update(buf, size);
data->decoding_failed = NS_FAILED(rv);
return 0;
}
static int
MimeMultCMS_sig_eof (void *crypto_closure, bool abort_p)
{
MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
if (!data) {
return -1;
}
/* Hand an EOF to the crypto library.
We save away the value returned and will use it later to emit a
blurb about whether the signature validation was cool.
*/
if (data->sig_decoder_context) {
data->sig_decoder_context->Finish(getter_AddRefs(data->content_info));
// Release our reference to nsICMSDecoder //
data->sig_decoder_context = nullptr;
}
return 0;
}
static void
MimeMultCMS_free (void *crypto_closure)
{
MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
if (!data) return;
delete data;
}
static char *
MimeMultCMS_generate (void *crypto_closure)
{
MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
if (!data) return 0;
nsCOMPtr<nsIX509Cert> signerCert;
int aRelativeNestLevel = MIMEGetRelativeCryptoNestLevel(data->self);
if (aRelativeNestLevel < 0)
return nullptr;
int32_t maxNestLevel = 0;
if (data->smimeHeaderSink && aRelativeNestLevel >= 0)
{
data->smimeHeaderSink->MaxWantedNesting(&maxNestLevel);
if (aRelativeNestLevel > maxNestLevel)
return nullptr;
}
if (data->self->options->missing_parts)
{
// We were not given all parts of the message.
// We are therefore unable to verify correctness of the signature.
if (data->smimeHeaderSink)
data->smimeHeaderSink->SignedStatus(aRelativeNestLevel,
nsICMSMessageErrors::VERIFY_NOT_YET_ATTEMPTED,
nullptr);
return nullptr;
}
if (!data->content_info)
{
/* No content_info at all -- since we're inside a multipart/signed,
that means that we've either gotten a message that was truncated
before the signature part, or we ran out of memory, or something
awful has happened.
*/
return nullptr;
}
nsCString from_addr;
nsCString from_name;
nsCString sender_addr;
nsCString sender_name;
MimeCMSGetFromSender(data->self,
from_addr, from_name,
sender_addr, sender_name);
MimeCMSRequestAsyncSignatureVerification(data->content_info,
from_addr.get(), from_name.get(),
sender_addr.get(), sender_name.get(),
data->smimeHeaderSink, aRelativeNestLevel,
data->item_data, data->item_len);
if (data->content_info)
{
#if 0 // XXX Fix this. What do we do here? //
if (SEC_CMSContainsCertsOrCrls(data->content_info))
{
/* #### call libsec telling it to import the certs */
}
#endif
}
return nullptr;
}

View file

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef _MIMEMPKC_H_
#define _MIMEMPKC_H_
#include "mimemsig.h"
class nsICMSMessage;
/* The MimeMultipartSignedCMS class implements a multipart/signed MIME
container with protocol=application/x-CMS-signature, which passes the
signed object through CMS code to verify the signature. See mimemsig.h
for details of the general mechanism on which this is built.
*/
typedef struct MimeMultipartSignedCMSClass MimeMultipartSignedCMSClass;
typedef struct MimeMultipartSignedCMS MimeMultipartSignedCMS;
struct MimeMultipartSignedCMSClass {
MimeMultipartSignedClass msigned;
};
extern MimeMultipartSignedCMSClass mimeMultipartSignedCMSClass;
struct MimeMultipartSignedCMS {
MimeMultipartSigned msigned;
};
#define MimeMultipartSignedCMSClassInitializer(ITYPE,CSUPER) \
{ MimeMultipartSignedClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEMPKC_H_ */

View file

@ -0,0 +1,24 @@
/* -*- 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 "mimemdig.h"
#include "prlog.h"
#include "nsMimeTypes.h"
#define MIME_SUPERCLASS mimeMultipartClass
MimeDefClass(MimeMultipartDigest, MimeMultipartDigestClass,
mimeMultipartDigestClass, &MIME_SUPERCLASS);
static int
MimeMultipartDigestClassInitialize(MimeMultipartDigestClass *clazz)
{
#ifdef DEBUG
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
#endif
MimeMultipartClass *mclass = (MimeMultipartClass *) clazz;
mclass->default_part_type = MESSAGE_RFC822;
return 0;
}

View file

@ -0,0 +1,33 @@
/* -*- 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/. */
#ifndef _MIMEMDIG_H_
#define _MIMEMDIG_H_
#include "mimemult.h"
/* The MimeMultipartDigest class implements the multipart/digest MIME
container, which is just like multipart/mixed, except that the default
type (for parts with no type explicitly specified) is message/rfc822
instead of text/plain.
*/
typedef struct MimeMultipartDigestClass MimeMultipartDigestClass;
typedef struct MimeMultipartDigest MimeMultipartDigest;
struct MimeMultipartDigestClass {
MimeMultipartClass multipart;
};
extern MimeMultipartDigestClass mimeMultipartDigestClass;
struct MimeMultipartDigest {
MimeMultipart multipart;
};
#define MimeMultipartDigestClassInitializer(ITYPE,CSUPER) \
{ MimeMultipartClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEMDIG_H_ */

View file

@ -0,0 +1,21 @@
/* -*- 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 "mimemmix.h"
#include "prlog.h"
#define MIME_SUPERCLASS mimeMultipartClass
MimeDefClass(MimeMultipartMixed, MimeMultipartMixedClass,
mimeMultipartMixedClass, &MIME_SUPERCLASS);
static int
MimeMultipartMixedClassInitialize(MimeMultipartMixedClass *clazz)
{
#ifdef DEBUG
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
#endif
return 0;
}

View file

@ -0,0 +1,32 @@
/* -*- 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/. */
#ifndef _MIMEMMIX_H_
#define _MIMEMMIX_H_
#include "mimemult.h"
/* The MimeMultipartMixed class implements the multipart/mixed MIME container,
and is also used for any and all otherwise-unrecognised subparts of
multipart/.
*/
typedef struct MimeMultipartMixedClass MimeMultipartMixedClass;
typedef struct MimeMultipartMixed MimeMultipartMixed;
struct MimeMultipartMixedClass {
MimeMultipartClass multipart;
};
extern MimeMultipartMixedClass mimeMultipartMixedClass;
struct MimeMultipartMixed {
MimeMultipart multipart;
};
#define MimeMultipartMixedClassInitializer(ITYPE,CSUPER) \
{ MimeMultipartClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEMMIX_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,196 @@
/* -*- 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/. */
#ifndef _MIMEMOZ_H_
#define _MIMEMOZ_H_
#include "nsStreamConverter.h"
#include "nsIMimeEmitter.h"
#include "nsIURI.h"
#include "mozITXTToHTMLConv.h"
#include "nsIMsgSend.h"
#include "modmimee.h"
#include "nsMsgAttachmentData.h"
// SHERRY - Need to get these out of here eventually
#ifdef XP_UNIX
#undef Bool
#endif
#include "mimei.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include "nsIPrefBranch.h"
typedef struct _nsMIMESession nsMIMESession;
/* stream functions */
typedef unsigned int
(*MKSessionWriteReadyFunc) (nsMIMESession *stream);
#define MAX_WRITE_READY (((unsigned) (~0) << 1) >> 1) /* must be <= than MAXINT!!!!! */
typedef int
(*MKSessionWriteFunc) (nsMIMESession *stream, const char *str, int32_t len);
typedef void
(*MKSessionCompleteFunc) (nsMIMESession *stream);
typedef void
(*MKSessionAbortFunc) (nsMIMESession *stream, int status);
/* streamclass function */
struct _nsMIMESession {
const char * name; /* Just for diagnostics */
void * window_id; /* used for progress messages, etc. */
void * data_object; /* a pointer to whatever
* structure you wish to have
* passed to the routines below
* during writes, etc...
*
* this data object should hold
* the document, document
* structure or a pointer to the
* document.
*/
MKSessionWriteReadyFunc is_write_ready; /* checks to see if the stream is ready
* for writing. Returns 0 if not ready
* or the number of bytes that it can
* accept for write
*/
MKSessionWriteFunc put_block; /* writes a block of data to the stream */
MKSessionCompleteFunc complete; /* normal end */
MKSessionAbortFunc abort; /* abnormal end */
bool is_multipart; /* is the stream part of a multipart sequence */
};
/*
* This is for the reworked mime parser.
*/
class mime_stream_data { /* This object is the state we pass around
amongst the various stream functions
used by MIME_MessageConverter(). */
public:
mime_stream_data();
char *url_name;
char *orig_url_name; /* original url name */
nsCOMPtr<nsIChannel> channel;
nsMimeOutputType format_out;
void *pluginObj2; /* The new XP-COM stream converter object */
nsMIMESession *istream; /* Holdover - new stream we're writing out image data-if any. */
MimeObject *obj; /* The root parser object */
MimeDisplayOptions *options; /* Data for communicating with libmime.a */
MimeHeaders *headers; /* Copy of outer most mime header */
nsIMimeEmitter *output_emitter; /* Output emitter engine for libmime */
bool firstCheck; /* Is this the first look at the stream data */
};
//
// This object is the state we use for loading drafts and templates...
//
class mime_draft_data
{
public:
mime_draft_data();
char *url_name; // original url name */
nsMimeOutputType format_out; // intended output format; should be FO_OPEN_DRAFT */
nsMIMESession *stream; // not used for now
MimeObject *obj; // The root
MimeDisplayOptions *options; // data for communicating with libmime
MimeHeaders *headers; // Copy of outer most mime header
nsTArray<nsMsgAttachedFile*> attachments;// attachments
nsMsgAttachedFile *messageBody; // message body
nsMsgAttachedFile *curAttachment; // temp
nsCOMPtr <nsIFile> tmpFile;
nsCOMPtr <nsIOutputStream> tmpFileStream; // output file handle
MimeDecoderData *decoder_data;
char *mailcharset; // get it from CHARSET of Content-Type
bool forwardInline;
bool forwardInlineFilter;
bool overrideComposeFormat; // Override compose format (for forward inline).
nsString forwardToAddress;
nsCOMPtr<nsIMsgIdentity> identity;
char *originalMsgURI; // the original URI of the message we are currently processing
nsCOMPtr<nsIMsgDBHdr> origMsgHdr;
};
////////////////////////////////////////////////////////////////
// Bridge routines for legacy mime code
////////////////////////////////////////////////////////////////
// Create bridge stream for libmime
extern "C"
void *mime_bridge_create_display_stream(nsIMimeEmitter *newEmitter,
nsStreamConverter *newPluginObj2,
nsIURI *uri,
nsMimeOutputType format_out,
uint32_t whattodo,
nsIChannel *aChannel);
// To get the mime emitter...
extern "C" nsIMimeEmitter *GetMimeEmitter(MimeDisplayOptions *opt);
// To support 2 types of emitters...we need these routines :-(
extern "C" nsresult mimeSetNewURL(nsMIMESession *stream, char *url);
extern "C" nsresult mimeEmitterAddAttachmentField(MimeDisplayOptions *opt, const char *field, const char *value);
extern "C" nsresult mimeEmitterAddHeaderField(MimeDisplayOptions *opt, const char *field, const char *value);
extern "C" nsresult mimeEmitterAddAllHeaders(MimeDisplayOptions *opt, const char *allheaders, const int32_t allheadersize);
extern "C" nsresult mimeEmitterStartAttachment(MimeDisplayOptions *opt, const char *name, const char *contentType, const char *url,
bool aIsExternalAttachment);
extern "C" nsresult mimeEmitterEndAttachment(MimeDisplayOptions *opt);
extern "C" nsresult mimeEmitterEndAllAttachments(MimeDisplayOptions *opt);
extern "C" nsresult mimeEmitterStartBody(MimeDisplayOptions *opt, bool bodyOnly, const char *msgID, const char *outCharset);
extern "C" nsresult mimeEmitterEndBody(MimeDisplayOptions *opt);
extern "C" nsresult mimeEmitterEndHeader(MimeDisplayOptions *opt, MimeObject *obj);
extern "C" nsresult mimeEmitterStartHeader(MimeDisplayOptions *opt, bool rootMailHeader, bool headerOnly, const char *msgID,
const char *outCharset);
extern "C" nsresult mimeEmitterUpdateCharacterSet(MimeDisplayOptions *opt, const char *aCharset);
extern "C" nsresult MimeGetAttachmentList(MimeObject *tobj, const char *aMessageURL, nsMsgAttachmentData **data);
/* To Get the connnection to prefs service manager */
extern "C" nsIPrefBranch *GetPrefBranch(MimeDisplayOptions *opt);
// Get the text converter...
mozITXTToHTMLConv *GetTextConverter(MimeDisplayOptions *opt);
nsresult
HTML2Plaintext(const nsString& inString, nsString& outString,
uint32_t flags, uint32_t wrapCol);
nsresult
HTMLSanitize(const nsString& inString, nsString& outString);
extern "C" char *MimeGetStringByID(int32_t stringID);
extern "C" char *MimeGetStringByName(const char16_t *stringName);
// Utility to create a nsIURI object...
extern "C" nsresult nsMimeNewURI(nsIURI** aInstancePtrResult, const char *aSpec, nsIURI *aBase);
extern "C" nsresult SetMailCharacterSetToMsgWindow(MimeObject *obj, const char *aCharacterSet);
extern "C" nsresult GetMailNewsFont(MimeObject *obj, bool styleFixed, int32_t *fontPixelSize, int32_t *fontSizePercentage, nsCString& fontLang);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _MIMEMOZ_H_ */

View file

@ -0,0 +1,21 @@
/* -*- 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 "mimempar.h"
#include "prlog.h"
#define MIME_SUPERCLASS mimeMultipartClass
MimeDefClass(MimeMultipartParallel, MimeMultipartParallelClass,
mimeMultipartParallelClass, &MIME_SUPERCLASS);
static int
MimeMultipartParallelClassInitialize(MimeMultipartParallelClass *clazz)
{
#ifdef DEBUG
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
#endif
return 0;
}

View file

@ -0,0 +1,32 @@
/* -*- 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/. */
#ifndef _MIMEMPAR_H_
#define _MIMEMPAR_H_
#include "mimemult.h"
/* The MimeMultipartParallel class implements the multipart/parallel MIME
container, which is currently no different from multipart/mixed, since
it's not clear that there's anything useful it could do differently.
*/
typedef struct MimeMultipartParallelClass MimeMultipartParallelClass;
typedef struct MimeMultipartParallel MimeMultipartParallel;
struct MimeMultipartParallelClass {
MimeMultipartClass multipart;
};
extern MimeMultipartParallelClass mimeMultipartParallelClass;
struct MimeMultipartParallel {
MimeMultipart multipart;
};
#define MimeMultipartParallelClassInitializer(ITYPE,CSUPER) \
{ MimeMultipartClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEMPAR_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,66 @@
/* -*- 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/. */
#ifndef _MIMEMREL_H_
#define _MIMEMREL_H_
#include "mimemult.h"
#include "plhash.h"
#include "prio.h"
#include "nsNetUtil.h"
#include "nsIMimeConverter.h" // for MimeConverterOutputCallback
/* The MimeMultipartRelated class implements the multipart/related MIME
container, which allows `sibling' sub-parts to refer to each other.
*/
typedef struct MimeMultipartRelatedClass MimeMultipartRelatedClass;
typedef struct MimeMultipartRelated MimeMultipartRelated;
struct MimeMultipartRelatedClass {
MimeMultipartClass multipart;
};
extern "C" MimeMultipartRelatedClass mimeMultipartRelatedClass;
struct MimeMultipartRelated {
MimeMultipart multipart; /* superclass variables */
char* base_url; /* Base URL (if any) for the whole
multipart/related. */
char* head_buffer; /* Buffer used to remember the text/html 'head'
part. */
uint32_t head_buffer_fp; /* Active length. */
uint32_t head_buffer_size; /* How big it is. */
nsCOMPtr <nsIFile> file_buffer; /* The nsIFile of a temp file used when we
run out of room in the head_buffer. */
nsCOMPtr <nsIInputStream> input_file_stream; /* A stream to it. */
nsCOMPtr <nsIOutputStream> output_file_stream; /* A stream to it. */
MimeHeaders* buffered_hdrs; /* The headers of the 'head' part. */
bool head_loaded; /* Whether we've already passed the 'head'
part. */
MimeObject* headobj; /* The actual text/html head object. */
PLHashTable *hash;
MimeConverterOutputCallback real_output_fn;
void* real_output_closure;
char* curtag;
int32_t curtag_max;
int32_t curtag_length;
};
#define MimeMultipartRelatedClassInitializer(ITYPE,CSUPER) \
{ MimeMultipartClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEMREL_H_ */

View file

@ -0,0 +1,977 @@
/* -*- 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 "nsCOMPtr.h"
#include "nsIMimeEmitter.h"
#include "mimemsg.h"
#include "mimemoz2.h"
#include "prmem.h"
#include "prio.h"
#include "plstr.h"
#include "msgCore.h"
#include "prlog.h"
#include "prprf.h"
#include "nsMimeStringResources.h"
#include "nsMimeTypes.h"
#include "nsMsgMessageFlags.h"
#include "nsStringGlue.h"
#include "mimetext.h"
#include "mimecryp.h"
#include "mimetpfl.h"
#include "nsINetUtil.h"
#include "nsMsgUtils.h"
#include "nsMsgI18N.h"
#define MIME_SUPERCLASS mimeContainerClass
MimeDefClass(MimeMessage, MimeMessageClass, mimeMessageClass,
&MIME_SUPERCLASS);
static int MimeMessage_initialize (MimeObject *);
static void MimeMessage_finalize (MimeObject *);
static int MimeMessage_add_child (MimeObject *, MimeObject *);
static int MimeMessage_parse_begin (MimeObject *);
static int MimeMessage_parse_line (const char *, int32_t, MimeObject *);
static int MimeMessage_parse_eof (MimeObject *, bool);
static int MimeMessage_close_headers (MimeObject *obj);
static int MimeMessage_write_headers_html (MimeObject *);
static char *MimeMessage_partial_message_html(const char *data,
void *closure,
MimeHeaders *headers);
#ifdef XP_UNIX
extern void MimeHeaders_do_unix_display_hook_hack(MimeHeaders *);
#endif /* XP_UNIX */
#if defined(DEBUG) && defined(XP_UNIX)
static int MimeMessage_debug_print (MimeObject *, PRFileDesc *, int32_t depth);
#endif
extern MimeObjectClass mimeMultipartClass;
static int
MimeMessageClassInitialize(MimeMessageClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeContainerClass *cclass = (MimeContainerClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
oclass->initialize = MimeMessage_initialize;
oclass->finalize = MimeMessage_finalize;
oclass->parse_begin = MimeMessage_parse_begin;
oclass->parse_line = MimeMessage_parse_line;
oclass->parse_eof = MimeMessage_parse_eof;
cclass->add_child = MimeMessage_add_child;
#if defined(DEBUG) && defined(XP_UNIX)
oclass->debug_print = MimeMessage_debug_print;
#endif
return 0;
}
static int
MimeMessage_initialize (MimeObject *object)
{
MimeMessage *msg = (MimeMessage *)object;
msg->grabSubject = false;
msg->bodyLength = 0;
msg->sizeSoFar = 0;
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
}
static void
MimeMessage_finalize (MimeObject *object)
{
MimeMessage *msg = (MimeMessage *)object;
if (msg->hdrs)
MimeHeaders_free(msg->hdrs);
msg->hdrs = 0;
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
}
static int
MimeMessage_parse_begin (MimeObject *obj)
{
MimeMessage *msg = (MimeMessage *)obj;
int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
if (status < 0) return status;
if (obj->parent)
{
msg->grabSubject = true;
}
/* Messages have separators before the headers, except for the outermost
message. */
return MimeObject_write_separator(obj);
}
static int
MimeMessage_parse_line (const char *aLine, int32_t aLength, MimeObject *obj)
{
const char * line = aLine;
int32_t length = aLength;
MimeMessage *msg = (MimeMessage *) obj;
int status = 0;
NS_ASSERTION(line && *line, "empty line in mime msg parse_line");
if (!line || !*line) return -1;
msg->sizeSoFar += length;
if (msg->grabSubject)
{
if ( (!PL_strncasecmp(line, "Subject: ", 9)) && (obj->parent) )
{
if ( (obj->headers) && (!obj->headers->munged_subject) )
{
obj->headers->munged_subject = (char *) PL_strndup(line + 9, length - 9);
char *tPtr = obj->headers->munged_subject;
while (*tPtr)
{
if ( (*tPtr == '\r') || (*tPtr == '\n') )
{
*tPtr = '\0';
break;
}
tPtr++;
}
}
}
}
/* If we already have a child object, then we're done parsing headers,
and all subsequent lines get passed to the inferior object without
further processing by us. (Our parent will stop feeding us lines
when this MimeMessage part is out of data.)
*/
if (msg->container.nchildren)
{
MimeObject *kid = msg->container.children[0];
bool nl;
PR_ASSERT(kid);
if (!kid) return -1;
msg->bodyLength += length;
/* Don't allow MimeMessage objects to not end in a newline, since it
would be inappropriate for any following part to appear on the same
line as the last line of the message.
#### This assumes that the only time the `parse_line' method is
called with a line that doesn't end in a newline is when that line
is the last line.
*/
nl = (length > 0 && (line[length-1] == '\r' || line[length-1] == '\n'));
#ifdef MIME_DRAFTS
if (!mime_typep (kid, (MimeObjectClass*) &mimeMessageClass) &&
obj->options &&
obj->options->decompose_file_p &&
!obj->options->is_multipart_msg &&
obj->options->decompose_file_output_fn &&
!obj->options->decrypt_p)
{
// If we are processing a flowed plain text line, we need to parse the
// line in mimeInlineTextPlainFlowedClass.
if (mime_typep(kid, (MimeObjectClass *)&mimeInlineTextPlainFlowedClass))
{
// Remove any stuffed space.
if (length > 0 && ' ' == *line)
{
line++;
length--;
}
return kid->clazz->parse_line (line, length, kid);
}
else
{
status = obj->options->decompose_file_output_fn (line,
length,
obj->options->stream_closure);
if (status < 0) return status;
if (!nl) {
status = obj->options->decompose_file_output_fn (MSG_LINEBREAK,
MSG_LINEBREAK_LEN,
obj->options->stream_closure);
if (status < 0) return status;
}
return status;
}
}
#endif /* MIME_DRAFTS */
if (nl)
return kid->clazz->parse_buffer (line, length, kid);
else
{
/* Hack a newline onto the end. */
char *s = (char *)PR_MALLOC(length + MSG_LINEBREAK_LEN + 1);
if (!s) return MIME_OUT_OF_MEMORY;
memcpy(s, line, length);
PL_strncpyz(s + length, MSG_LINEBREAK, MSG_LINEBREAK_LEN + 1);
status = kid->clazz->parse_buffer (s, length + MSG_LINEBREAK_LEN, kid);
PR_Free(s);
return status;
}
}
/* Otherwise we don't yet have a child object, which means we're not
done parsing our headers yet.
*/
if (!msg->hdrs)
{
msg->hdrs = MimeHeaders_new();
if (!msg->hdrs) return MIME_OUT_OF_MEMORY;
}
#ifdef MIME_DRAFTS
if ( obj->options &&
obj->options->decompose_file_p &&
! obj->options->is_multipart_msg &&
obj->options->done_parsing_outer_headers &&
obj->options->decompose_file_output_fn )
{
status = obj->options->decompose_file_output_fn( line, length,
obj->options->stream_closure );
if (status < 0)
return status;
}
#endif /* MIME_DRAFTS */
status = MimeHeaders_parse_line(line, length, msg->hdrs);
if (status < 0) return status;
/* If this line is blank, we're now done parsing headers, and should
examine our content-type to create our "body" part.
*/
if (*line == '\r' || *line == '\n')
{
status = MimeMessage_close_headers(obj);
if (status < 0) return status;
}
return 0;
}
static int
MimeMessage_close_headers (MimeObject *obj)
{
MimeMessage *msg = (MimeMessage *) obj;
int status = 0;
char *ct = 0; /* Content-Type header */
MimeObject *body;
// Do a proper decoding of the munged subject.
if (obj->headers && msg->hdrs && msg->grabSubject && obj->headers->munged_subject) {
// nsMsgI18NConvertToUnicode wants nsAStrings...
nsDependentCString orig(obj->headers->munged_subject);
nsAutoString dest;
// First, get the Content-Type, then extract the charset="whatever" part of
// it.
nsCString charset;
nsCString contentType;
contentType.Adopt(MimeHeaders_get(msg->hdrs, HEADER_CONTENT_TYPE, false, false));
if (!contentType.IsEmpty())
charset.Adopt(MimeHeaders_get_parameter(contentType.get(), "charset", nullptr, nullptr));
// If we've got a charset, use nsMsgI18NConvertToUnicode to magically decode
// the munged subject.
if (!charset.IsEmpty()) {
nsresult rv = nsMsgI18NConvertToUnicode(charset.get(), orig, dest);
// If we managed to convert the string, replace munged_subject with the
// UTF8 version of it, otherwise, just forget about it (maybe there was an
// improperly encoded string in there).
PR_Free(obj->headers->munged_subject);
if (NS_SUCCEEDED(rv))
obj->headers->munged_subject = ToNewUTF8String(dest);
else
obj->headers->munged_subject = nullptr;
} else {
PR_Free(obj->headers->munged_subject);
obj->headers->munged_subject = nullptr;
}
}
if (msg->hdrs)
{
bool outer_p = !obj->headers; /* is this the outermost message? */
#ifdef MIME_DRAFTS
if (outer_p &&
obj->options &&
(obj->options->decompose_file_p || obj->options->caller_need_root_headers) &&
obj->options->decompose_headers_info_fn)
{
#ifdef ENABLE_SMIME
if (obj->options->decrypt_p && !mime_crypto_object_p(msg->hdrs, false, obj->options))
obj->options->decrypt_p = false;
#endif /* ENABLE_SMIME */
if (!obj->options->caller_need_root_headers || (obj == obj->options->state->root))
status = obj->options->decompose_headers_info_fn (
obj->options->stream_closure,
msg->hdrs );
}
#endif /* MIME_DRAFTS */
/* If this is the outermost message, we need to run the
`generate_header' callback. This happens here instead of
in `parse_begin', because it's only now that we've parsed
our headers. However, since this is the outermost message,
we have yet to write any HTML, so that's fine.
*/
if (outer_p &&
obj->output_p &&
obj->options &&
obj->options->write_html_p &&
obj->options->generate_header_html_fn)
{
int lstatus = 0;
char *html = 0;
/* The generate_header_html_fn might return HTML, so it's important
that the output stream be set up with the proper type before we
make the MimeObject_write() call below. */
if (!obj->options->state->first_data_written_p)
{
lstatus = MimeObject_output_init (obj, TEXT_HTML);
if (lstatus < 0) return lstatus;
PR_ASSERT(obj->options->state->first_data_written_p);
}
html = obj->options->generate_header_html_fn(NULL,
obj->options->html_closure,
msg->hdrs);
if (html)
{
lstatus = MimeObject_write(obj, html, strlen(html), false);
PR_Free(html);
if (lstatus < 0) return lstatus;
}
}
/* Find the content-type of the body of this message.
*/
{
bool ok = true;
char *mv = MimeHeaders_get (msg->hdrs, HEADER_MIME_VERSION,
true, false);
#ifdef REQUIRE_MIME_VERSION_HEADER
/* If this is the outermost message, it must have a MIME-Version
header with the value 1.0 for us to believe what might be in
the Content-Type header. If the MIME-Version header is not
present, we must treat this message as untyped.
*/
ok = (mv && !strcmp(mv, "1.0"));
#else
/* #### actually, we didn't check this in Mozilla 2.0, and checking
it now could cause some compatibility nonsense, so for now, let's
just believe any Content-Type header we see.
*/
ok = true;
#endif
if (ok)
{
ct = MimeHeaders_get (msg->hdrs, HEADER_CONTENT_TYPE, true, false);
/* If there is no Content-Type header, but there is a MIME-Version
header, then assume that this *is* in fact a MIME message.
(I've seen messages with
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
and no Content-Type, and we should treat those as being of type
MimeInlineTextPlain rather than MimeUntypedText.)
*/
if (mv && !ct)
ct = strdup(TEXT_PLAIN);
}
PR_FREEIF(mv); /* done with this now. */
}
/* If this message has a body which is encrypted and we're going to
decrypt it (whithout converting it to HTML, since decrypt_p and
write_html_p are never true at the same time)
*/
if (obj->output_p &&
obj->options &&
obj->options->decrypt_p
#ifdef ENABLE_SMIME
&& !mime_crypto_object_p(msg->hdrs, false, obj->options)
#endif /* ENABLE_SMIME */
)
{
/* The body of this message is not an encrypted object, so we need
to turn off the decrypt_p flag (to prevent us from s#$%ing the
body of the internal object up into one.) In this case,
our output will end up being identical to our input.
*/
obj->options->decrypt_p = false;
}
/* Emit the HTML for this message's headers. Do this before
creating the object representing the body.
*/
if (obj->output_p &&
obj->options &&
obj->options->write_html_p)
{
/* If citation headers are on, and this is not the outermost message,
turn them off. */
if (obj->options->headers == MimeHeadersCitation && !outer_p)
obj->options->headers = MimeHeadersSome;
/* Emit a normal header block. */
status = MimeMessage_write_headers_html(obj);
if (status < 0)
{
PR_FREEIF(ct);
return status;
}
}
else if (obj->output_p)
{
/* Dump the headers, raw. */
status = MimeObject_write(obj, "", 0, false); /* initialize */
if (status < 0)
{
PR_FREEIF(ct);
return status;
}
status = MimeHeaders_write_raw_headers(msg->hdrs, obj->options,
obj->options->decrypt_p);
if (status < 0)
{
PR_FREEIF(ct);
return status;
}
}
#ifdef XP_UNIX
if (outer_p && obj->output_p)
/* Kludge from mimehdrs.c */
MimeHeaders_do_unix_display_hook_hack(msg->hdrs);
#endif /* XP_UNIX */
}
/* Never put out a separator after a message header block. */
if (obj->options && obj->options->state)
obj->options->state->separator_suppressed_p = true;
#ifdef MIME_DRAFTS
if ( !obj->headers && /* outer most message header */
obj->options &&
obj->options->decompose_file_p &&
ct )
obj->options->is_multipart_msg = PL_strcasestr(ct, "multipart/") != NULL;
#endif /* MIME_DRAFTS */
body = mime_create(ct, msg->hdrs, obj->options);
PR_FREEIF(ct);
if (!body) return MIME_OUT_OF_MEMORY;
status = ((MimeContainerClass *) obj->clazz)->add_child (obj, body);
if (status < 0)
{
mime_free(body);
return status;
}
// Only do this if this is a Text Object!
if ( mime_typep(body, (MimeObjectClass *) &mimeInlineTextClass) )
{
((MimeInlineText *) body)->needUpdateMsgWinCharset = true;
}
/* Now that we've added this new object to our list of children,
start its parser going. */
status = body->clazz->parse_begin(body);
if (status < 0) return status;
// Now notify the emitter if this is the outer most message, unless
// it is a part that is not the head of the message. If it's a part,
// we need to figure out the content type/charset of the part
//
bool outer_p = !obj->headers; /* is this the outermost message? */
if ( (outer_p || obj->options->notify_nested_bodies) &&
(!obj->options->part_to_load || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay))
{
// call SetMailCharacterSetToMsgWindow() to set a menu charset
if (mime_typep(body, (MimeObjectClass *) &mimeInlineTextClass))
{
MimeInlineText *text = (MimeInlineText *) body;
if (text && text->charset && *text->charset)
SetMailCharacterSetToMsgWindow(body, text->charset);
}
char *msgID = MimeHeaders_get (msg->hdrs, HEADER_MESSAGE_ID,
false, false);
const char *outCharset = NULL;
if (!obj->options->force_user_charset) /* Only convert if the user prefs is false */
outCharset = "UTF-8";
mimeEmitterStartBody(obj->options, (obj->options->headers == MimeHeadersNone), msgID, outCharset);
PR_FREEIF(msgID);
// setting up truncated message html fotter function
char *xmoz = MimeHeaders_get(msg->hdrs, HEADER_X_MOZILLA_STATUS, false,
false);
if (xmoz)
{
uint32_t flags = 0;
char dummy = 0;
if (sscanf(xmoz, " %x %c", &flags, &dummy) == 1 &&
flags & nsMsgMessageFlags::Partial)
{
obj->options->html_closure = obj;
obj->options->generate_footer_html_fn =
MimeMessage_partial_message_html;
}
PR_FREEIF(xmoz);
}
}
return 0;
}
static int
MimeMessage_parse_eof (MimeObject *obj, bool abort_p)
{
int status;
bool outer_p;
MimeMessage *msg = (MimeMessage *)obj;
if (obj->closed_p) return 0;
/* Run parent method first, to flush out any buffered data. */
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0) return status;
outer_p = !obj->headers; /* is this the outermost message? */
// Hack for messages with truncated headers (bug 244722)
// If there is no empty line in a message, the parser can't figure out where
// the headers end, causing parsing to hang. So we insert an extra newline
// to keep it happy. This is OK, since a message without any empty lines is
// broken anyway...
if(outer_p && msg->hdrs && ! msg->hdrs->done_p) {
MimeMessage_parse_line("\n", 1, obj);
}
// Once we get to the end of parsing the message, we will notify
// the emitter that we are done the the body.
// Mark the end of the mail body if we are actually emitting the
// body of the message (i.e. not Header ONLY)
if ((outer_p || obj->options->notify_nested_bodies) && obj->options &&
obj->options->write_html_p)
{
if (obj->options->generate_footer_html_fn)
{
mime_stream_data *msd =
(mime_stream_data *) obj->options->stream_closure;
if (msd)
{
char *html = obj->options->generate_footer_html_fn
(msd->orig_url_name, obj->options->html_closure, msg->hdrs);
if (html)
{
int lstatus = MimeObject_write(obj, html,
strlen(html),
false);
PR_Free(html);
if (lstatus < 0) return lstatus;
}
}
}
if ((!obj->options->part_to_load || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay) &&
obj->options->headers != MimeHeadersOnly)
mimeEmitterEndBody(obj->options);
}
#ifdef MIME_DRAFTS
if ( obj->options &&
obj->options->decompose_file_p &&
obj->options->done_parsing_outer_headers &&
! obj->options->is_multipart_msg &&
! mime_typep(obj, (MimeObjectClass*) &mimeEncryptedClass) &&
obj->options->decompose_file_close_fn ) {
status = obj->options->decompose_file_close_fn (
obj->options->stream_closure );
if ( status < 0 ) return status;
}
#endif /* MIME_DRAFTS */
/* Put out a separator after every message/rfc822 object. */
if (!abort_p && !outer_p)
{
status = MimeObject_write_separator(obj);
if (status < 0) return status;
}
return 0;
}
static int
MimeMessage_add_child (MimeObject *parent, MimeObject *child)
{
MimeContainer *cont = (MimeContainer *) parent;
PR_ASSERT(parent && child);
if (!parent || !child) return -1;
/* message/rfc822 containers can only have one child. */
PR_ASSERT(cont->nchildren == 0);
if (cont->nchildren != 0) return -1;
#ifdef MIME_DRAFTS
if ( parent->options &&
parent->options->decompose_file_p &&
! parent->options->is_multipart_msg &&
! mime_typep(child, (MimeObjectClass*) &mimeEncryptedClass) &&
parent->options->decompose_file_init_fn ) {
int status = 0;
status = parent->options->decompose_file_init_fn (
parent->options->stream_closure,
((MimeMessage*)parent)->hdrs );
if ( status < 0 ) return status;
}
#endif /* MIME_DRAFTS */
return ((MimeContainerClass*)&MIME_SUPERCLASS)->add_child (parent, child);
}
// This is necessary to determine which charset to use for a reply/forward
char *
DetermineMailCharset(MimeMessage *msg)
{
char *retCharset = nullptr;
if ( (msg) && (msg->hdrs) )
{
char *ct = MimeHeaders_get (msg->hdrs, HEADER_CONTENT_TYPE,
false, false);
if (ct)
{
retCharset = MimeHeaders_get_parameter (ct, "charset", NULL, NULL);
PR_Free(ct);
}
if (!retCharset)
{
// If we didn't find "Content-Type: ...; charset=XX" then look
// for "X-Sun-Charset: XX" instead. (Maybe this should be done
// in MimeSunAttachmentClass, but it's harder there than here.)
retCharset = MimeHeaders_get (msg->hdrs, HEADER_X_SUN_CHARSET,
false, false);
}
}
if (!retCharset)
return strdup("ISO-8859-1");
else
return retCharset;
}
static int
MimeMessage_write_headers_html (MimeObject *obj)
{
MimeMessage *msg = (MimeMessage *) obj;
int status;
if (!obj->options || !obj->options->output_fn)
return 0;
PR_ASSERT(obj->output_p && obj->options->write_html_p);
// To support the no header option! Make sure we are not
// suppressing headers on included email messages...
if ( (obj->options->headers == MimeHeadersNone) &&
(obj == obj->options->state->root) )
{
// Ok, we are going to kick the Emitter for a StartHeader
// operation ONLY WHEN THE CHARSET OF THE ORIGINAL MESSAGE IS
// NOT US-ASCII ("ISO-8859-1")
//
// This is only to notify the emitter of the charset of the
// original message
char *mailCharset = DetermineMailCharset(msg);
if ( (mailCharset) && (PL_strcasecmp(mailCharset, "US-ASCII")) &&
(PL_strcasecmp(mailCharset, "ISO-8859-1")) )
mimeEmitterUpdateCharacterSet(obj->options, mailCharset);
PR_FREEIF(mailCharset);
return 0;
}
if (!obj->options->state->first_data_written_p)
{
status = MimeObject_output_init (obj, TEXT_HTML);
if (status < 0)
{
mimeEmitterEndHeader(obj->options, obj);
return status;
}
PR_ASSERT(obj->options->state->first_data_written_p);
}
// Start the header parsing by the emitter
char *msgID = MimeHeaders_get (msg->hdrs, HEADER_MESSAGE_ID,
false, false);
bool outer_p = !obj->headers; /* is this the outermost message? */
if (!outer_p && obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay &&
obj->options->part_to_load)
{
//Maybe we are displaying a embedded message as outer part!
char *id = mime_part_address(obj);
if (id)
{
outer_p = !strcmp(id, obj->options->part_to_load);
PR_Free(id);
}
}
// Ok, we should really find out the charset of this part. We always
// output UTF-8 for display, but the original charset is necessary for
// reply and forward operations.
//
char *mailCharset = DetermineMailCharset(msg);
mimeEmitterStartHeader(obj->options,
outer_p,
(obj->options->headers == MimeHeadersOnly),
msgID,
mailCharset);
// Change the default_charset by the charset of the original message
// ONLY WHEN THE CHARSET OF THE ORIGINAL MESSAGE IS NOT US-ASCII
// ("ISO-8859-1") and default_charset and mailCharset are different,
// or when there is no default_charset (this can happen with saved messages).
if ( (!obj->options->default_charset ||
((mailCharset) && (PL_strcasecmp(mailCharset, "US-ASCII")) &&
(PL_strcasecmp(mailCharset, "ISO-8859-1")) &&
(PL_strcasecmp(obj->options->default_charset, mailCharset)))) &&
!obj->options->override_charset )
{
PR_FREEIF(obj->options->default_charset);
obj->options->default_charset = strdup(mailCharset);
}
PR_FREEIF(msgID);
PR_FREEIF(mailCharset);
status = MimeHeaders_write_all_headers (msg->hdrs, obj->options, false);
if (status < 0)
{
mimeEmitterEndHeader(obj->options, obj);
return status;
}
if (!msg->crypto_stamped_p)
{
/* If we're not writing a xlation stamp, and this is the outermost
message, then now is the time to run the post_header_html_fn.
(Otherwise, it will be run when the xlation-stamp is finally
closed off, in MimeXlateed_emit_buffered_child() or
MimeMultipartSigned_emit_child().)
*/
if (obj->options &&
obj->options->state &&
obj->options->generate_post_header_html_fn &&
!obj->options->state->post_header_html_run_p)
{
char *html = 0;
PR_ASSERT(obj->options->state->first_data_written_p);
html = obj->options->generate_post_header_html_fn(NULL,
obj->options->html_closure,
msg->hdrs);
obj->options->state->post_header_html_run_p = true;
if (html)
{
status = MimeObject_write(obj, html, strlen(html), false);
PR_Free(html);
if (status < 0)
{
mimeEmitterEndHeader(obj->options, obj);
return status;
}
}
}
}
mimeEmitterEndHeader(obj->options, obj);
// rhp:
// For now, we are going to parse the entire message, even if we are
// only interested in headers...why? Well, because this is the only
// way to build the attachment list. Now we will have the attachment
// list in the output being created by the XML emitter. If we ever
// want to go back to where we were before, just uncomment the conditional
// and it will stop at header parsing.
//
// if (obj->options->headers == MimeHeadersOnly)
// return -1;
// else
return 0;
}
static char *
MimeMessage_partial_message_html(const char *data, void *closure,
MimeHeaders *headers)
{
MimeMessage *msg = (MimeMessage *)closure;
nsAutoCString orig_url(data);
char *uidl = MimeHeaders_get(headers, HEADER_X_UIDL, false, false);
char *msgId = MimeHeaders_get(headers, HEADER_MESSAGE_ID, false,
false);
char *msgIdPtr = PL_strchr(msgId, '<');
int32_t pos = orig_url.Find("mailbox-message");
if (pos != -1)
orig_url.Cut(pos + 7, 8);
pos = orig_url.FindChar('#');
if (pos != -1)
orig_url.Replace(pos, 1, "?number=", 8);
if (msgIdPtr)
msgIdPtr++;
else
msgIdPtr = msgId;
char *gtPtr = PL_strchr(msgIdPtr, '>');
if (gtPtr)
*gtPtr = 0;
bool msgBaseTruncated = (msg->bodyLength > MSG_LINEBREAK_LEN);
nsCString partialMsgHtml;
nsCString item;
partialMsgHtml.AppendLiteral("<div style=\"margin: 1em auto; border: 1px solid black; width: 80%\">");
partialMsgHtml.AppendLiteral("<div style=\"margin: 5px; padding: 10px; border: 1px solid gray; font-weight: bold; text-align: center;\">");
partialMsgHtml.AppendLiteral("<span style=\"font-size: 120%;\">");
if (msgBaseTruncated)
item.Adopt(MimeGetStringByName(u"MIME_MSG_PARTIAL_TRUNCATED"));
else
item.Adopt(MimeGetStringByName(u"MIME_MSG_PARTIAL_NOT_DOWNLOADED"));
partialMsgHtml += item;
partialMsgHtml.AppendLiteral("</span><hr>");
if (msgBaseTruncated)
item.Adopt(MimeGetStringByName(u"MIME_MSG_PARTIAL_TRUNCATED_EXPLANATION"));
else
item.Adopt(MimeGetStringByName(u"MIME_MSG_PARTIAL_NOT_DOWNLOADED_EXPLANATION"));
partialMsgHtml += item;
partialMsgHtml.AppendLiteral("<br><br>");
partialMsgHtml.AppendLiteral("<a href=\"");
partialMsgHtml.Append(orig_url);
if (msgIdPtr) {
partialMsgHtml.AppendLiteral("&messageid=");
MsgEscapeString(nsDependentCString(msgIdPtr), nsINetUtil::ESCAPE_URL_PATH,
item);
partialMsgHtml.Append(item);
}
if (uidl) {
partialMsgHtml.AppendLiteral("&uidl=");
MsgEscapeString(nsDependentCString(uidl), nsINetUtil::ESCAPE_XALPHAS,
item);
partialMsgHtml.Append(item);
}
partialMsgHtml.AppendLiteral("\">");
item.Adopt(MimeGetStringByName(u"MIME_MSG_PARTIAL_CLICK_FOR_REST"));
partialMsgHtml += item;
partialMsgHtml.AppendLiteral("</a>");
partialMsgHtml.AppendLiteral("</div></div>");
return ToNewCString(partialMsgHtml);
}
#if defined(DEBUG) && defined(XP_UNIX)
static int
MimeMessage_debug_print (MimeObject *obj, PRFileDesc *stream, int32_t depth)
{
MimeMessage *msg = (MimeMessage *) obj;
char *addr = mime_part_address(obj);
int i;
for (i=0; i < depth; i++)
PR_Write(stream, " ", 2);
/*
fprintf(stream, "<%s %s%s 0x%08X>\n",
obj->clazz->class_name,
addr ? addr : "???",
(msg->container.nchildren == 0 ? " (no body)" : ""),
(uint32_t) msg);
*/
PR_FREEIF(addr);
#if 0
if (msg->hdrs)
{
char *s;
depth++;
# define DUMP(HEADER) \
for (i=0; i < depth; i++) \
PR_Write(stream, " ", 2); \
s = MimeHeaders_get (msg->hdrs, HEADER, false, true);
/**
\
PR_Write(stream, HEADER ": %s\n", s ? s : ""); \
**/
PR_FREEIF(s)
DUMP(HEADER_SUBJECT);
DUMP(HEADER_DATE);
DUMP(HEADER_FROM);
DUMP(HEADER_TO);
/* DUMP(HEADER_CC); */
DUMP(HEADER_NEWSGROUPS);
DUMP(HEADER_MESSAGE_ID);
# undef DUMP
PR_Write(stream, "\n", 1);
}
#endif
PR_ASSERT(msg->container.nchildren <= 1);
if (msg->container.nchildren == 1)
{
MimeObject *kid = msg->container.children[0];
int status = kid->clazz->debug_print (kid, stream, depth+1);
if (status < 0) return status;
}
return 0;
}
#endif

View file

@ -0,0 +1,43 @@
/* -*- 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/. */
#ifndef _MIMEMSG_H_
#define _MIMEMSG_H_
#include "mimecont.h"
/* The MimeMessage class implements the message/rfc822 and message/news
MIME containers, which is to say, mail and news messages.
*/
typedef struct MimeMessageClass MimeMessageClass;
typedef struct MimeMessage MimeMessage;
struct MimeMessageClass {
MimeContainerClass container;
};
extern MimeMessageClass mimeMessageClass;
struct MimeMessage {
MimeContainer container; /* superclass variables */
MimeHeaders *hdrs; /* headers of this message */
bool newline_p; /* whether the last line ended in a newline */
bool crypto_stamped_p; /* whether the header of this message has been
emitted expecting its child to emit HTML
which says that it is xlated. */
bool crypto_msg_signed_p; /* What the emitted xlation-stamp *says*. */
bool crypto_msg_encrypted_p;
bool grabSubject; /* Should we try to grab the subject of this message */
int32_t bodyLength; /* Used for determining if the body has been truncated */
int32_t sizeSoFar; /* The total size of the MIME message, once parsing is
finished. */
};
#define MimeMessageClassInitializer(ITYPE,CSUPER) \
{ MimeContainerClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEMSG_H_ */

View file

@ -0,0 +1,775 @@
/* -*- 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 "modmimee.h"
#include "mimemsig.h"
#include "nspr.h"
#include "prmem.h"
#include "plstr.h"
#include "prerror.h"
#include "nsMimeTypes.h"
#include "msgCore.h"
#include "nsMimeStringResources.h"
#include "mimemoz2.h"
#include "nsIMimeConverter.h" // for MimeConverterOutputCallback
#include "mozilla/Attributes.h"
#define MIME_SUPERCLASS mimeMultipartClass
MimeDefClass(MimeMultipartSigned, MimeMultipartSignedClass,
mimeMultipartSignedClass, &MIME_SUPERCLASS);
static int MimeMultipartSigned_initialize (MimeObject *);
static int MimeMultipartSigned_create_child (MimeObject *);
static int MimeMultipartSigned_close_child(MimeObject *);
static int MimeMultipartSigned_parse_line (const char *, int32_t, MimeObject *);
static int MimeMultipartSigned_parse_child_line (MimeObject *, const char *, int32_t,
bool);
static int MimeMultipartSigned_parse_eof (MimeObject *, bool);
static void MimeMultipartSigned_finalize (MimeObject *);
static int MimeMultipartSigned_emit_child (MimeObject *obj);
static int
MimeMultipartSignedClassInitialize(MimeMultipartSignedClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeMultipartClass *mclass = (MimeMultipartClass *) clazz;
oclass->initialize = MimeMultipartSigned_initialize;
oclass->parse_line = MimeMultipartSigned_parse_line;
oclass->parse_eof = MimeMultipartSigned_parse_eof;
oclass->finalize = MimeMultipartSigned_finalize;
mclass->create_child = MimeMultipartSigned_create_child;
mclass->parse_child_line = MimeMultipartSigned_parse_child_line;
mclass->close_child = MimeMultipartSigned_close_child;
PR_ASSERT(!oclass->class_initialized);
return 0;
}
static int
MimeMultipartSigned_initialize (MimeObject *object)
{
MimeMultipartSigned *sig = (MimeMultipartSigned *) object;
/* This is an abstract class; it shouldn't be directly instantiated. */
PR_ASSERT(object->clazz != (MimeObjectClass *) &mimeMultipartSignedClass);
sig->state = MimeMultipartSignedPreamble;
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
}
static void
MimeMultipartSigned_cleanup (MimeObject *obj, bool finalizing_p)
{
MimeMultipart *mult = (MimeMultipart *) obj; /* #58075. Fix suggested by jwz */
MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
if (sig->part_buffer)
{
MimePartBufferDestroy(sig->part_buffer);
sig->part_buffer = 0;
}
if (sig->body_hdrs)
{
MimeHeaders_free (sig->body_hdrs);
sig->body_hdrs = 0;
}
if (sig->sig_hdrs)
{
MimeHeaders_free (sig->sig_hdrs);
sig->sig_hdrs = 0;
}
mult->state = MimeMultipartEpilogue; /* #58075. Fix suggested by jwz */
sig->state = MimeMultipartSignedEpilogue;
if (finalizing_p && sig->crypto_closure) {
/* Don't free these until this object is really going away -- keep them
around for the lifetime of the MIME object, so that we can get at the
security info of sub-parts of the currently-displayed message. */
((MimeMultipartSignedClass *) obj->clazz)->crypto_free (sig->crypto_closure);
sig->crypto_closure = 0;
}
if (sig->sig_decoder_data)
{
MimeDecoderDestroy(sig->sig_decoder_data, true);
sig->sig_decoder_data = 0;
}
}
static int
MimeMultipartSigned_parse_eof (MimeObject *obj, bool abort_p)
{
MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
int status = 0;
if (obj->closed_p) return 0;
/* Close off the signature, if we've gotten that far.
*/
if (sig->state == MimeMultipartSignedSignatureHeaders ||
sig->state == MimeMultipartSignedSignatureFirstLine ||
sig->state == MimeMultipartSignedSignatureLine ||
sig->state == MimeMultipartSignedEpilogue)
{
status = (((MimeMultipartSignedClass *) obj->clazz)->crypto_signature_eof) (sig->crypto_closure, abort_p);
if (status < 0) return status;
}
if (!abort_p)
{
/* Now that we've read both the signed object and the signature (and
have presumably verified the signature) write out a blurb, and then
the signed object.
*/
status = MimeMultipartSigned_emit_child(obj);
if (status < 0) return status;
}
MimeMultipartSigned_cleanup(obj, false);
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
}
static void
MimeMultipartSigned_finalize (MimeObject *obj)
{
MimeMultipartSigned_cleanup(obj, true);
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj);
}
static int
MimeMultipartSigned_parse_line (const char *line, int32_t length, MimeObject *obj)
{
MimeMultipart *mult = (MimeMultipart *) obj;
MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
MimeMultipartParseState old_state = mult->state;
bool hash_line_p = true;
bool no_headers_p = false;
int status = 0;
/* First do the parsing for normal multipart/ objects by handing it off to
the superclass method. This includes calling the create_child and
close_child methods.
*/
status = (((MimeObjectClass *)(&MIME_SUPERCLASS))
->parse_line (line, length, obj));
if (status < 0) return status;
/* The instance variable MimeMultipartClass->state tracks motion through
the various stages of multipart/ parsing. The instance variable
MimeMultipartSigned->state tracks the difference between the first
part (the body) and the second part (the signature.) This second,
more specific state variable is updated by noticing the transitions
of the first, more general state variable.
*/
if (old_state != mult->state) /* there has been a state change */
{
switch (mult->state)
{
case MimeMultipartPreamble:
PR_ASSERT(0); /* can't move *in* to preamble state. */
sig->state = MimeMultipartSignedPreamble;
break;
case MimeMultipartHeaders:
/* If we're moving in to the Headers state, then that means
that this line is the preceeding boundary string (and we
should ignore it.)
*/
hash_line_p = false;
if (sig->state == MimeMultipartSignedPreamble)
sig->state = MimeMultipartSignedBodyFirstHeader;
else if (sig->state == MimeMultipartSignedBodyFirstLine ||
sig->state == MimeMultipartSignedBodyLine)
sig->state = MimeMultipartSignedSignatureHeaders;
else if (sig->state == MimeMultipartSignedSignatureFirstLine ||
sig->state == MimeMultipartSignedSignatureLine)
sig->state = MimeMultipartSignedEpilogue;
break;
case MimeMultipartPartFirstLine:
if (sig->state == MimeMultipartSignedBodyFirstHeader)
{
sig->state = MimeMultipartSignedBodyFirstLine;
no_headers_p = true;
}
else if (sig->state == MimeMultipartSignedBodyHeaders)
sig->state = MimeMultipartSignedBodyFirstLine;
else if (sig->state == MimeMultipartSignedSignatureHeaders)
sig->state = MimeMultipartSignedSignatureFirstLine;
else
sig->state = MimeMultipartSignedEpilogue;
break;
case MimeMultipartPartLine:
PR_ASSERT(sig->state == MimeMultipartSignedBodyFirstLine ||
sig->state == MimeMultipartSignedBodyLine ||
sig->state == MimeMultipartSignedSignatureFirstLine ||
sig->state == MimeMultipartSignedSignatureLine);
if (sig->state == MimeMultipartSignedBodyFirstLine)
sig->state = MimeMultipartSignedBodyLine;
else if (sig->state == MimeMultipartSignedSignatureFirstLine)
sig->state = MimeMultipartSignedSignatureLine;
break;
case MimeMultipartEpilogue:
sig->state = MimeMultipartSignedEpilogue;
break;
default: /* bad state */
NS_ERROR("bad state in MultipartSigned parse line");
return -1;
break;
}
}
/* Perform multipart/signed-related actions on this line based on the state
of the parser.
*/
switch (sig->state)
{
case MimeMultipartSignedPreamble:
/* Do nothing. */
break;
case MimeMultipartSignedBodyFirstLine:
/* We have just moved out of the MimeMultipartSignedBodyHeaders
state, so cache away the headers that apply only to the body part.
*/
NS_ASSERTION(mult->hdrs, "null multipart hdrs");
NS_ASSERTION(!sig->body_hdrs, "signed part shouldn't have already have body_hdrs");
sig->body_hdrs = mult->hdrs;
mult->hdrs = 0;
/* fall through. */
MOZ_FALLTHROUGH;
case MimeMultipartSignedBodyFirstHeader:
case MimeMultipartSignedBodyHeaders:
case MimeMultipartSignedBodyLine:
if (!sig->crypto_closure)
{
/* Set error change */
PR_SetError(0, 0);
/* Initialize the signature verification library. */
sig->crypto_closure = (((MimeMultipartSignedClass *) obj->clazz)
->crypto_init) (obj);
if (!sig->crypto_closure)
{
status = PR_GetError();
NS_ASSERTION(status < 0, "got non-negative status");
if (status >= 0)
status = -1;
return status;
}
}
if (hash_line_p)
{
/* this is the first hashed line if this is the first header
(that is, if it's the first line in the header state after
a state change.)
*/
bool first_line_p
= (no_headers_p ||
sig->state == MimeMultipartSignedBodyFirstHeader);
if (sig->state == MimeMultipartSignedBodyFirstHeader)
sig->state = MimeMultipartSignedBodyHeaders;
/* The newline issues here are tricky, since both the newlines
before and after the boundary string are to be considered part
of the boundary: this is so that a part can be specified such
that it does not end in a trailing newline.
To implement this, we send a newline *before* each line instead
of after, except for the first line, which is not preceeded by a
newline.
For purposes of cryptographic hashing, we always hash line
breaks as CRLF -- the canonical, on-the-wire linebreaks, since
we have no idea of knowing what line breaks were used on the
originating system (SMTP rightly destroys that information.)
*/
/* Remove the trailing newline... */
if (length > 0 && line[length-1] == '\n') length--;
if (length > 0 && line[length-1] == '\r') length--;
PR_ASSERT(sig->crypto_closure);
if (!first_line_p)
{
/* Push out a preceeding newline... */
char nl[] = CRLF;
status = (((MimeMultipartSignedClass *) obj->clazz)
->crypto_data_hash (nl, 2, sig->crypto_closure));
if (status < 0) return status;
}
/* Now push out the line sans trailing newline. */
if (length > 0)
status = (((MimeMultipartSignedClass *) obj->clazz)
->crypto_data_hash (line,length, sig->crypto_closure));
if (status < 0) return status;
}
break;
case MimeMultipartSignedSignatureHeaders:
if (sig->crypto_closure &&
old_state != mult->state)
{
/* We have just moved out of the MimeMultipartSignedBodyLine
state, so tell the signature verification library that we've
reached the end of the signed data.
*/
status = (((MimeMultipartSignedClass *) obj->clazz)
->crypto_data_eof) (sig->crypto_closure, false);
if (status < 0) return status;
}
break;
case MimeMultipartSignedSignatureFirstLine:
/* We have just moved out of the MimeMultipartSignedSignatureHeaders
state, so cache away the headers that apply only to the sig part.
*/
PR_ASSERT(mult->hdrs);
PR_ASSERT(!sig->sig_hdrs);
sig->sig_hdrs = mult->hdrs;
mult->hdrs = 0;
/* If the signature block has an encoding, set up a decoder for it.
(Similar logic is in MimeLeafClass->parse_begin.)
*/
{
MimeDecoderData *(*fn) (MimeConverterOutputCallback, void*) = 0;
nsCString encoding;
encoding.Adopt(MimeHeaders_get (sig->sig_hdrs,
HEADER_CONTENT_TRANSFER_ENCODING,
true, false));
if (encoding.IsEmpty())
;
else if (!PL_strcasecmp(encoding.get(), ENCODING_BASE64))
fn = &MimeB64DecoderInit;
else if (!PL_strcasecmp(encoding.get(), ENCODING_QUOTED_PRINTABLE))
{
sig->sig_decoder_data =
MimeQPDecoderInit (((MimeConverterOutputCallback)
(((MimeMultipartSignedClass *) obj->clazz)
->crypto_signature_hash)),
sig->crypto_closure);
if (!sig->sig_decoder_data)
return MIME_OUT_OF_MEMORY;
}
else if (!PL_strcasecmp(encoding.get(), ENCODING_UUENCODE) ||
!PL_strcasecmp(encoding.get(), ENCODING_UUENCODE2) ||
!PL_strcasecmp(encoding.get(), ENCODING_UUENCODE3) ||
!PL_strcasecmp(encoding.get(), ENCODING_UUENCODE4))
fn = &MimeUUDecoderInit;
else if (!PL_strcasecmp(encoding.get(), ENCODING_YENCODE))
fn = &MimeYDecoderInit;
if (fn)
{
sig->sig_decoder_data =
fn (((MimeConverterOutputCallback)
(((MimeMultipartSignedClass *) obj->clazz)
->crypto_signature_hash)),
sig->crypto_closure);
if (!sig->sig_decoder_data)
return MIME_OUT_OF_MEMORY;
}
}
/* Show these headers to the crypto module. */
if (hash_line_p)
{
status = (((MimeMultipartSignedClass *) obj->clazz)
->crypto_signature_init) (sig->crypto_closure,
obj, sig->sig_hdrs);
if (status < 0) return status;
}
/* fall through. */
MOZ_FALLTHROUGH;
case MimeMultipartSignedSignatureLine:
if (hash_line_p)
{
/* Feed this line into the signature verification routines. */
if (sig->sig_decoder_data)
status = MimeDecoderWrite (sig->sig_decoder_data, line, length, nullptr);
else
status = (((MimeMultipartSignedClass *) obj->clazz)
->crypto_signature_hash (line, length,
sig->crypto_closure));
if (status < 0) return status;
}
break;
case MimeMultipartSignedEpilogue:
/* Nothing special to do here. */
break;
default: /* bad state */
PR_ASSERT(0);
return -1;
}
return status;
}
static int
MimeMultipartSigned_create_child (MimeObject *parent)
{
/* Don't actually create a child -- we call the superclass create_child
method later, after we've fully parsed everything. (And we only call
it once, for part #1, and never for part #2 (the signature.))
*/
MimeMultipart *mult = (MimeMultipart *) parent;
mult->state = MimeMultipartPartFirstLine;
return 0;
}
static int
MimeMultipartSigned_close_child (MimeObject *obj)
{
/* The close_child method on MimeMultipartSigned doesn't actually do
anything to the children list, since the create_child method also
doesn't do anything.
*/
MimeMultipart *mult = (MimeMultipart *) obj;
MimeContainer *cont = (MimeContainer *) obj;
MimeMultipartSigned *msig = (MimeMultipartSigned *) obj;
if (msig->part_buffer)
/* Closes the tmp file, if there is one: doesn't free the part_buffer. */
MimePartBufferClose(msig->part_buffer);
if (mult->hdrs) /* duplicated from MimeMultipart_close_child, ugh. */
{
MimeHeaders_free(mult->hdrs);
mult->hdrs = 0;
}
/* Should be no kids yet. */
PR_ASSERT(cont->nchildren == 0);
if (cont->nchildren != 0) return -1;
return 0;
}
static int
MimeMultipartSigned_parse_child_line (MimeObject *obj,
const char *line, int32_t length,
bool first_line_p)
{
MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
MimeContainer *cont = (MimeContainer *) obj;
int status = 0;
/* Shouldn't have made any sub-parts yet. */
PR_ASSERT(cont->nchildren == 0);
if (cont->nchildren != 0) return -1;
switch (sig->state)
{
case MimeMultipartSignedPreamble:
case MimeMultipartSignedBodyFirstHeader:
case MimeMultipartSignedBodyHeaders:
// How'd we get here? Oh well, fall through.
NS_ERROR("wrong state in parse child line");
MOZ_FALLTHROUGH;
case MimeMultipartSignedBodyFirstLine:
PR_ASSERT(first_line_p);
if (!sig->part_buffer)
{
sig->part_buffer = MimePartBufferCreate();
if (!sig->part_buffer)
return MIME_OUT_OF_MEMORY;
}
/* fall through */
MOZ_FALLTHROUGH;
case MimeMultipartSignedBodyLine:
{
/* This is the first part; we are buffering it, and will emit it all
at the end (so that we know whether the signature matches before
showing anything to the user.)
*/
/* The newline issues here are tricky, since both the newlines
before and after the boundary string are to be considered part
of the boundary: this is so that a part can be specified such
that it does not end in a trailing newline.
To implement this, we send a newline *before* each line instead
of after, except for the first line, which is not preceeded by a
newline.
*/
/* Remove the trailing newline... */
if (length > 0 && line[length-1] == '\n') length--;
if (length > 0 && line[length-1] == '\r') length--;
PR_ASSERT(sig->part_buffer);
PR_ASSERT(first_line_p ==
(sig->state == MimeMultipartSignedBodyFirstLine));
if (!first_line_p)
{
/* Push out a preceeding newline... */
char nl[] = MSG_LINEBREAK;
status = MimePartBufferWrite (sig->part_buffer, nl, MSG_LINEBREAK_LEN);
if (status < 0) return status;
}
/* Now push out the line sans trailing newline. */
if (length > 0)
status = MimePartBufferWrite (sig->part_buffer, line, length);
if (status < 0) return status;
}
break;
case MimeMultipartSignedSignatureHeaders:
// How'd we get here? Oh well, fall through.
NS_ERROR("should have already parse sig hdrs");
MOZ_FALLTHROUGH;
case MimeMultipartSignedSignatureFirstLine:
case MimeMultipartSignedSignatureLine:
/* Nothing to do here -- hashing of the signature part is handled up
in MimeMultipartSigned_parse_line().
*/
break;
case MimeMultipartSignedEpilogue:
/* Too many kids? MimeMultipartSigned_create_child() should have
prevented us from getting here. */
NS_ERROR("too many kids?");
return -1;
break;
default: /* bad state */
NS_ERROR("bad state in multipart signed parse line");
return -1;
break;
}
return status;
}
static int
MimeMultipartSigned_emit_child (MimeObject *obj)
{
MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
MimeMultipart *mult = (MimeMultipart *) obj;
MimeContainer *cont = (MimeContainer *) obj;
int status = 0;
MimeObject *body;
NS_ASSERTION(sig->crypto_closure, "no crypto closure");
/* Emit some HTML saying whether the signature was cool.
But don't emit anything if in FO_QUOTE_MESSAGE mode.
*/
if (obj->options &&
obj->options->headers != MimeHeadersCitation &&
obj->options->write_html_p &&
obj->options->output_fn &&
obj->options->headers != MimeHeadersCitation &&
sig->crypto_closure)
{
char *html = (((MimeMultipartSignedClass *) obj->clazz)
->crypto_generate_html (sig->crypto_closure));
#if 0 // XXX For the moment, no HTML output. Fix this XXX //
if (!html) return -1; /* MIME_OUT_OF_MEMORY? */
status = MimeObject_write(obj, html, strlen(html), false);
PR_Free(html);
if (status < 0) return status;
#endif
/* Now that we have written out the crypto stamp, the outermost header
block is well and truly closed. If this is in fact the outermost
message, then run the post_header_html_fn now.
*/
if (obj->options &&
obj->options->state &&
obj->options->generate_post_header_html_fn &&
!obj->options->state->post_header_html_run_p)
{
MimeHeaders *outer_headers=nullptr;
MimeObject *p;
for (p = obj; p->parent; p = p->parent)
outer_headers = p->headers;
NS_ASSERTION(obj->options->state->first_data_written_p,
"should have already written some data");
html = obj->options->generate_post_header_html_fn(NULL,
obj->options->html_closure,
outer_headers);
obj->options->state->post_header_html_run_p = true;
if (html)
{
status = MimeObject_write(obj, html, strlen(html), false);
PR_Free(html);
if (status < 0) return status;
}
}
}
/* Oh, this is fairly nasty. We're skipping over our "create child" method
and using the one our superclass defines. Perhaps instead we should add
a new method on this class, and initialize that method to be the
create_child method of the superclass. Whatever.
*/
/* The superclass method expects to find the headers for the part that it's
to create in mult->hdrs, so ensure that they're there. */
NS_ASSERTION(!mult->hdrs, "shouldn't already have hdrs for multipart");
if (mult->hdrs) MimeHeaders_free(mult->hdrs);
mult->hdrs = sig->body_hdrs;
sig->body_hdrs = 0;
/* Run the superclass create_child method.
*/
status = (((MimeMultipartClass *)(&MIME_SUPERCLASS))->create_child(obj));
if (status < 0) return status;
// Notify the charset of the first part.
if (obj->options && !(obj->options->override_charset)) {
MimeObject *firstChild = ((MimeContainer*) obj)->children[0];
char *disposition = MimeHeaders_get (firstChild->headers,
HEADER_CONTENT_DISPOSITION,
true,
false);
// check if need to show as inline
if (!disposition)
{
const char *content_type = firstChild->content_type;
if (!PL_strcasecmp (content_type, TEXT_PLAIN) ||
!PL_strcasecmp (content_type, TEXT_HTML) ||
!PL_strcasecmp (content_type, TEXT_MDL) ||
!PL_strcasecmp (content_type, MULTIPART_ALTERNATIVE) ||
!PL_strcasecmp (content_type, MULTIPART_RELATED) ||
!PL_strcasecmp (content_type, MESSAGE_NEWS) ||
!PL_strcasecmp (content_type, MESSAGE_RFC822)) {
char *ct = MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE, false, false);
if (ct) {
char *cset = MimeHeaders_get_parameter (ct, "charset", NULL, NULL);
if (cset) {
mimeEmitterUpdateCharacterSet(obj->options, cset);
SetMailCharacterSetToMsgWindow(obj, cset);
PR_Free(cset);
}
PR_Free(ct);
}
}
}
}
// The js emitter wants to know about the newly created child. Because
// MimeMultipartSigned dummies out its create_child operation, the logic
// in MimeMultipart_parse_line that would normally provide this notification
// does not get to fire.
if (obj->options && obj->options->notify_nested_bodies) {
MimeObject *kid = ((MimeContainer*) obj)->children[0];
// The emitter is expecting the content type with parameters; not the fully
// parsed thing, so get it from raw. (We do not do it in the charset
// notification block that just happened because it already has complex
// if-checks that do not jive with us.
char *ct = MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE, false,
false);
mimeEmitterAddHeaderField(obj->options, HEADER_CONTENT_TYPE,
ct ? ct : "text/plain");
PR_Free(ct);
char *part_path = mime_part_address(kid);
if (part_path) {
mimeEmitterAddHeaderField(obj->options,
"x-jsemitter-part-path",
part_path);
PR_Free(part_path);
}
}
/* Retrieve the child that it created.
*/
NS_ASSERTION(cont->nchildren == 1, "should only have one child");
if (cont->nchildren != 1)
return -1;
body = cont->children[0];
NS_ASSERTION(body, "missing body");
if (!body)
return -1;
#ifdef MIME_DRAFTS
if (body->options->decompose_file_p) {
body->options->signed_p = true;
if (!mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
body->options->decompose_file_init_fn)
body->options->decompose_file_init_fn ( body->options->stream_closure, body->headers );
}
#endif /* MIME_DRAFTS */
/* If there's no part_buffer, this is a zero-length signed message? */
if (sig->part_buffer)
{
#ifdef MIME_DRAFTS
if (body->options->decompose_file_p &&
!mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
body->options->decompose_file_output_fn)
status = MimePartBufferRead (sig->part_buffer,
/* The (MimeConverterOutputCallback) cast is to turn the
`void' argument into `MimeObject'. */
((MimeConverterOutputCallback)
body->options->decompose_file_output_fn),
body->options->stream_closure);
else
#endif /* MIME_DRAFTS */
status = MimePartBufferRead (sig->part_buffer,
/* The (MimeConverterOutputCallback) cast is to turn the
`void' argument into `MimeObject'. */
((MimeConverterOutputCallback) body->clazz->parse_buffer),
body);
if (status < 0) return status;
}
MimeMultipartSigned_cleanup(obj, false);
/* Done parsing. */
status = body->clazz->parse_eof(body, false);
if (status < 0) return status;
status = body->clazz->parse_end(body, false);
if (status < 0) return status;
#ifdef MIME_DRAFTS
if (body->options->decompose_file_p &&
!mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
body->options->decompose_file_close_fn)
body->options->decompose_file_close_fn(body->options->stream_closure);
#endif /* MIME_DRAFTS */
/* Put out a separator after every multipart/signed object. */
status = MimeObject_write_separator(obj);
if (status < 0) return status;
return 0;
}

View file

@ -0,0 +1,134 @@
/* -*- 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/. */
#ifndef _MIMEMSIG_H_
#define _MIMEMSIG_H_
#include "mimemult.h"
#include "mimepbuf.h"
#include "modmimee.h"
/* The MimeMultipartSigned class implements the multipart/signed MIME
container, which provides a general method of associating a cryptographic
signature to an arbitrary MIME object.
The MimeMultipartSigned class provides the following methods:
void *crypto_init (MimeObject *multipart_object)
This is called with the object, the object->headers of which should be
used to initialize the dexlateion engine. NULL indicates failure;
otherwise, an opaque closure object should be returned.
int crypto_data_hash (const char *data, int32_t data_size,
void *crypto_closure)
This is called with the raw data, for which a signature has been computed.
The crypto module should examine this, and compute a signature for it.
int crypto_data_eof (void *crypto_closure, bool abort_p)
This is called when no more data remains. If `abort_p' is true, then the
crypto module may choose to discard any data rather than processing it,
as we're terminating abnormally.
int crypto_signature_init (void *crypto_closure,
MimeObject *multipart_object,
MimeHeaders *signature_hdrs)
This is called after crypto_data_eof() and just before the first call to
crypto_signature_hash(). The crypto module may wish to do some
initialization here, or may wish to examine the actual headers of the
signature object itself.
int crypto_signature_hash (const char *data, int32_t data_size,
void *crypto_closure)
This is called with the raw data of the detached signature block. It will
be called after crypto_data_eof() has been called to signify the end of
the data which is signed. This data is the data of the signature itself.
int crypto_signature_eof (void *crypto_closure, bool abort_p)
This is called when no more signature data remains. If `abort_p' is true,
then the crypto module may choose to discard any data rather than
processing it, as we're terminating abnormally.
char * crypto_generate_html (void *crypto_closure)
This is called after `crypto_signature_eof' but before `crypto_free'.
The crypto module should return a newly-allocated string of HTML code
which explains the status of the dexlateion to the user (whether the
signature checks out, etc.)
void crypto_free (void *crypto_closure)
This will be called when we're all done, after `crypto_signature_eof' and
`crypto_emit_html'. It is intended to free any data represented by the
crypto_closure.
*/
typedef struct MimeMultipartSignedClass MimeMultipartSignedClass;
typedef struct MimeMultipartSigned MimeMultipartSigned;
typedef enum {
MimeMultipartSignedPreamble,
MimeMultipartSignedBodyFirstHeader,
MimeMultipartSignedBodyHeaders,
MimeMultipartSignedBodyFirstLine,
MimeMultipartSignedBodyLine,
MimeMultipartSignedSignatureHeaders,
MimeMultipartSignedSignatureFirstLine,
MimeMultipartSignedSignatureLine,
MimeMultipartSignedEpilogue
} MimeMultipartSignedParseState;
struct MimeMultipartSignedClass {
MimeMultipartClass multipart;
/* Callbacks used by dexlateion (really, signature verification) module. */
void * (*crypto_init) (MimeObject *multipart_object);
int (*crypto_data_hash) (const char *data, int32_t data_size,
void *crypto_closure);
int (*crypto_signature_hash) (const char *data, int32_t data_size,
void *crypto_closure);
int (*crypto_data_eof) (void *crypto_closure, bool abort_p);
int (*crypto_signature_eof) (void *crypto_closure, bool abort_p);
int (*crypto_signature_init) (void *crypto_closure,
MimeObject *multipart_object,
MimeHeaders *signature_hdrs);
char * (*crypto_generate_html) (void *crypto_closure);
void (*crypto_free) (void *crypto_closure);
};
extern "C" MimeMultipartSignedClass mimeMultipartSignedClass;
struct MimeMultipartSigned {
MimeMultipart multipart;
MimeMultipartSignedParseState state; /* State of parser */
void *crypto_closure; /* Opaque data used by signature
verification module. */
MimeHeaders *body_hdrs; /* The headers of the signed object. */
MimeHeaders *sig_hdrs; /* The headers of the signature. */
MimePartBufferData *part_buffer; /* The buffered body of the signed
object (see mimepbuf.h) */
MimeDecoderData *sig_decoder_data; /* The signature is probably base64
encoded; this is the decoder used
to get raw bits out of it. */
};
#define MimeMultipartSignedClassInitializer(ITYPE,CSUPER) \
{ MimeMultipartClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEMSIG_H_ */

View file

@ -0,0 +1,748 @@
/* -*- 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 "msgCore.h"
#include "mimemult.h"
#include "mimemoz2.h"
#include "mimeeobj.h"
#include "prlog.h"
#include "prmem.h"
#include "plstr.h"
#include "prio.h"
#include "nsMimeStringResources.h"
#include "nsMimeTypes.h"
#include <ctype.h>
#ifdef XP_MACOSX
extern MimeObjectClass mimeMultipartAppleDoubleClass;
#endif
#define MIME_SUPERCLASS mimeContainerClass
MimeDefClass(MimeMultipart, MimeMultipartClass,
mimeMultipartClass, &MIME_SUPERCLASS);
static int MimeMultipart_initialize (MimeObject *);
static void MimeMultipart_finalize (MimeObject *);
static int MimeMultipart_parse_line (const char *line, int32_t length, MimeObject *);
static int MimeMultipart_parse_eof (MimeObject *object, bool abort_p);
static MimeMultipartBoundaryType MimeMultipart_check_boundary(MimeObject *,
const char *,
int32_t);
static int MimeMultipart_create_child(MimeObject *);
static bool MimeMultipart_output_child_p(MimeObject *, MimeObject *);
static int MimeMultipart_parse_child_line (MimeObject *, const char *, int32_t,
bool);
static int MimeMultipart_close_child(MimeObject *);
extern "C" MimeObjectClass mimeMultipartAlternativeClass;
extern "C" MimeObjectClass mimeMultipartRelatedClass;
extern "C" MimeObjectClass mimeMultipartSignedClass;
extern "C" MimeObjectClass mimeInlineTextVCardClass;
extern "C" MimeExternalObjectClass mimeExternalObjectClass;
#if defined(DEBUG) && defined(XP_UNIX)
static int MimeMultipart_debug_print (MimeObject *, PRFileDesc *, int32_t);
#endif
static int
MimeMultipartClassInitialize(MimeMultipartClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeMultipartClass *mclass = (MimeMultipartClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
oclass->initialize = MimeMultipart_initialize;
oclass->finalize = MimeMultipart_finalize;
oclass->parse_line = MimeMultipart_parse_line;
oclass->parse_eof = MimeMultipart_parse_eof;
mclass->check_boundary = MimeMultipart_check_boundary;
mclass->create_child = MimeMultipart_create_child;
mclass->output_child_p = MimeMultipart_output_child_p;
mclass->parse_child_line = MimeMultipart_parse_child_line;
mclass->close_child = MimeMultipart_close_child;
#if defined(DEBUG) && defined(XP_UNIX)
oclass->debug_print = MimeMultipart_debug_print;
#endif
return 0;
}
static int
MimeMultipart_initialize (MimeObject *object)
{
MimeMultipart *mult = (MimeMultipart *) object;
char *ct;
/* This is an abstract class; it shouldn't be directly instantiated. */
PR_ASSERT(object->clazz != (MimeObjectClass *) &mimeMultipartClass);
ct = MimeHeaders_get (object->headers, HEADER_CONTENT_TYPE, false, false);
mult->boundary = (ct
? MimeHeaders_get_parameter (ct, HEADER_PARM_BOUNDARY, NULL, NULL)
: 0);
PR_FREEIF(ct);
mult->state = MimeMultipartPreamble;
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
}
static void
MimeMultipart_finalize (MimeObject *object)
{
MimeMultipart *mult = (MimeMultipart *) object;
object->clazz->parse_eof(object, false);
PR_FREEIF(mult->boundary);
if (mult->hdrs)
MimeHeaders_free(mult->hdrs);
mult->hdrs = 0;
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
}
int MimeWriteAString(MimeObject *obj, const nsACString &string)
{
const nsCString &flatString = PromiseFlatCString(string);
return MimeObject_write(obj, flatString.get(), flatString.Length(), true);
}
static int
MimeMultipart_parse_line (const char *line, int32_t length, MimeObject *obj)
{
MimeMultipart *mult = (MimeMultipart *) obj;
MimeContainer *container = (MimeContainer*) obj;
int status = 0;
MimeMultipartBoundaryType boundary;
NS_ASSERTION(line && *line, "empty line in multipart parse_line");
if (!line || !*line) return -1;
NS_ASSERTION(!obj->closed_p, "obj shouldn't already be closed");
if (obj->closed_p) return -1;
/* If we're supposed to write this object, but aren't supposed to convert
it to HTML, simply pass it through unaltered. */
if (obj->output_p &&
obj->options &&
!obj->options->write_html_p &&
obj->options->output_fn
&& obj->options->format_out != nsMimeOutput::nsMimeMessageAttach)
return MimeObject_write(obj, line, length, true);
if (mult->state == MimeMultipartEpilogue) /* already done */
boundary = MimeMultipartBoundaryTypeNone;
else
boundary = ((MimeMultipartClass *)obj->clazz)->check_boundary(obj, line,
length);
if (boundary == MimeMultipartBoundaryTypeTerminator ||
boundary == MimeMultipartBoundaryTypeSeparator)
{
/* Match! Close the currently-open part, move on to the next
state, and discard this line.
*/
bool endOfPart = (mult->state != MimeMultipartPreamble);
if (endOfPart)
status = ((MimeMultipartClass *)obj->clazz)->close_child(obj);
if (status < 0) return status;
if (boundary == MimeMultipartBoundaryTypeTerminator)
mult->state = MimeMultipartEpilogue;
else
{
mult->state = MimeMultipartHeaders;
/* Reset the header parser for this upcoming part. */
NS_ASSERTION(!mult->hdrs, "mult->hdrs should be null here");
if (mult->hdrs)
MimeHeaders_free(mult->hdrs);
mult->hdrs = MimeHeaders_new();
if (!mult->hdrs)
return MIME_OUT_OF_MEMORY;
if (obj->options && obj->options->state &&
obj->options->state->partsToStrip.Length() > 0)
{
nsAutoCString newPart(mime_part_address(obj));
newPart.Append('.');
newPart.AppendInt(container->nchildren + 1);
obj->options->state->strippingPart = false;
// check if this is a sub-part of a part we're stripping.
for (uint32_t partIndex = 0; partIndex < obj->options->state->partsToStrip.Length(); partIndex++)
{
nsCString &curPartToStrip = obj->options->state->partsToStrip[partIndex];
if (newPart.Find(curPartToStrip) == 0 && (newPart.Length() == curPartToStrip.Length() || newPart.CharAt(curPartToStrip.Length()) == '.'))
{
obj->options->state->strippingPart = true;
if (partIndex < obj->options->state->detachToFiles.Length())
obj->options->state->detachedFilePath = obj->options->state->detachToFiles[partIndex];
break;
}
}
}
}
// if stripping out attachments, write the boundary line. Otherwise, return
// to ignore it.
if (obj->options && obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)
{
// Because MimeMultipart_parse_child_line strips out the
// the CRLF of the last line before the end of a part, we need to add that
// back in here.
if (endOfPart)
MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK));
status = MimeObject_write(obj, line, length, true);
}
return 0;
}
/* Otherwise, this isn't a boundary string. So do whatever it is we
should do with this line (parse it as a header, feed it to the
child part, ignore it, etc.) */
switch (mult->state)
{
case MimeMultipartPreamble:
case MimeMultipartEpilogue:
/* Ignore this line. */
break;
case MimeMultipartHeaders:
/* Parse this line as a header for the sub-part. */
{
status = MimeHeaders_parse_line(line, length, mult->hdrs);
bool stripping = false;
if (status < 0) return status;
// If this line is blank, we're now done parsing headers, and should
// now examine the content-type to create this "body" part.
//
if (*line == '\r' || *line == '\n')
{
if (obj->options && obj->options->state &&
obj->options->state->strippingPart)
{
stripping = true;
bool detachingPart = obj->options->state->detachedFilePath.Length() > 0;
nsAutoCString fileName;
fileName.Adopt(MimeHeaders_get_name(mult->hdrs, obj->options));
if (detachingPart)
{
char *contentType = MimeHeaders_get(mult->hdrs, "Content-Type", false, false);
if (contentType)
{
MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Type: "));
MimeWriteAString(obj, nsDependentCString(contentType));
PR_Free(contentType);
}
MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK));
MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Disposition: attachment; filename=\""));
MimeWriteAString(obj, fileName);
MimeWriteAString(obj, NS_LITERAL_CSTRING("\"" MSG_LINEBREAK));
MimeWriteAString(obj, NS_LITERAL_CSTRING("X-Mozilla-External-Attachment-URL: "));
MimeWriteAString(obj, obj->options->state->detachedFilePath);
MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK));
MimeWriteAString(obj, NS_LITERAL_CSTRING("X-Mozilla-Altered: AttachmentDetached; date=\""));
}
else
{
nsAutoCString header("Content-Type: text/x-moz-deleted; name=\"Deleted: ");
header.Append(fileName);
status = MimeWriteAString(obj, header);
if (status < 0)
return status;
status = MimeWriteAString(obj, NS_LITERAL_CSTRING("\"" MSG_LINEBREAK "Content-Transfer-Encoding: 8bit" MSG_LINEBREAK));
MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Disposition: inline; filename=\"Deleted: "));
MimeWriteAString(obj, fileName);
MimeWriteAString(obj, NS_LITERAL_CSTRING("\"" MSG_LINEBREAK "X-Mozilla-Altered: AttachmentDeleted; date=\""));
}
nsCString result;
char timeBuffer[128];
PRExplodedTime now;
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
PR_FormatTimeUSEnglish(timeBuffer, sizeof(timeBuffer),
"%a %b %d %H:%M:%S %Y",
&now);
MimeWriteAString(obj, nsDependentCString(timeBuffer));
MimeWriteAString(obj, NS_LITERAL_CSTRING("\"" MSG_LINEBREAK));
MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK "You deleted an attachment from this message. The original MIME headers for the attachment were:" MSG_LINEBREAK));
MimeHeaders_write_raw_headers(mult->hdrs, obj->options, false);
}
int32_t old_nchildren = container->nchildren;
status = ((MimeMultipartClass *) obj->clazz)->create_child(obj);
if (status < 0) return status;
NS_ASSERTION(mult->state != MimeMultipartHeaders,
"mult->state shouldn't be MimeMultipartHeaders");
if (!stripping && container->nchildren > old_nchildren && obj->options &&
!mime_typep(obj, (MimeObjectClass*)&mimeMultipartAlternativeClass)) {
// Notify emitter about content type and part path.
MimeObject *kid = container->children[container->nchildren-1];
MimeMultipart_notify_emitter(kid);
}
}
break;
}
case MimeMultipartPartFirstLine:
/* Hand this line off to the sub-part. */
status = (((MimeMultipartClass *) obj->clazz)->parse_child_line(obj,
line, length, true));
if (status < 0) return status;
mult->state = MimeMultipartPartLine;
break;
case MimeMultipartPartLine:
/* Hand this line off to the sub-part. */
status = (((MimeMultipartClass *) obj->clazz)->parse_child_line(obj,
line, length, false));
if (status < 0) return status;
break;
default:
NS_ERROR("unexpected state in parse line");
return -1;
}
if (obj->options &&
obj->options->format_out == nsMimeOutput::nsMimeMessageAttach &&
(!(obj->options->state && obj->options->state->strippingPart) &&
mult->state != MimeMultipartPartLine))
return MimeObject_write(obj, line, length, false);
return 0;
}
void MimeMultipart_notify_emitter(MimeObject *obj)
{
char *ct = nullptr;
NS_ASSERTION(obj->options, "MimeMultipart_notify_emitter called with null options");
if (! obj->options)
return;
ct = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE,
false, false);
if (obj->options->notify_nested_bodies) {
mimeEmitterAddHeaderField(obj->options, HEADER_CONTENT_TYPE,
ct ? ct : TEXT_PLAIN);
char *part_path = mime_part_address(obj);
if (part_path) {
mimeEmitterAddHeaderField(obj->options, "x-jsemitter-part-path",
part_path);
PR_Free(part_path);
}
}
// Examine the headers and see if there is a special charset
// (i.e. non US-ASCII) for this message. If so, we need to
// tell the emitter that this is the case for use in any
// possible reply or forward operation.
if (ct && (obj->options->notify_nested_bodies ||
MimeObjectIsMessageBody(obj))) {
char *cset = MimeHeaders_get_parameter(ct, "charset", NULL, NULL);
if (cset) {
mimeEmitterUpdateCharacterSet(obj->options, cset);
if (!obj->options->override_charset)
// Also set this charset to msgWindow
SetMailCharacterSetToMsgWindow(obj, cset);
PR_Free(cset);
}
}
PR_FREEIF(ct);
}
static MimeMultipartBoundaryType
MimeMultipart_check_boundary(MimeObject *obj, const char *line, int32_t length)
{
MimeMultipart *mult = (MimeMultipart *) obj;
int32_t blen;
bool term_p;
if (!mult->boundary ||
line[0] != '-' ||
line[1] != '-')
return MimeMultipartBoundaryTypeNone;
/* This is a candidate line to be a boundary. Check it out... */
blen = strlen(mult->boundary);
term_p = false;
/* strip trailing whitespace (including the newline.) */
while(length > 2 && IS_SPACE(line[length-1]))
length--;
/* Could this be a terminating boundary? */
if (length == blen + 4 &&
line[length-1] == '-' &&
line[length-2] == '-')
{
term_p = true;
}
//looks like we have a separator but first, we need to check it's not for one of the part's children.
MimeContainer *cont = (MimeContainer *) obj;
if (cont->nchildren > 0)
{
MimeObject *kid = cont->children[cont->nchildren-1];
if (kid)
if (mime_typep(kid, (MimeObjectClass*) &mimeMultipartClass))
{
//Don't ask the kid to check the boundary if it has already detected a Teminator
MimeMultipart *mult = (MimeMultipart *) kid;
if (mult->state != MimeMultipartEpilogue)
if (MimeMultipart_check_boundary(kid, line, length) != MimeMultipartBoundaryTypeNone)
return MimeMultipartBoundaryTypeNone;
}
}
if (term_p)
length -= 2;
if (blen == length-2 && !strncmp(line+2, mult->boundary, length-2))
return (term_p
? MimeMultipartBoundaryTypeTerminator
: MimeMultipartBoundaryTypeSeparator);
else
return MimeMultipartBoundaryTypeNone;
}
static int
MimeMultipart_create_child(MimeObject *obj)
{
MimeMultipart *mult = (MimeMultipart *) obj;
int status;
char *ct = (mult->hdrs
? MimeHeaders_get (mult->hdrs, HEADER_CONTENT_TYPE,
true, false)
: 0);
const char *dct = (((MimeMultipartClass *) obj->clazz)->default_part_type);
MimeObject *body = NULL;
mult->state = MimeMultipartPartFirstLine;
if (obj->options)
obj->options->is_child = true;
/* Don't pass in NULL as the content-type (this means that the
auto-uudecode-hack won't ever be done for subparts of a
multipart, but only for untyped children of message/rfc822.
*/
body = mime_create(((ct && *ct) ? ct : (dct ? dct: TEXT_PLAIN)),
mult->hdrs, obj->options);
PR_FREEIF(ct);
if (!body) return MIME_OUT_OF_MEMORY;
status = ((MimeContainerClass *) obj->clazz)->add_child(obj, body);
if (status < 0)
{
mime_free(body);
return status;
}
#ifdef MIME_DRAFTS
if ( obj->options &&
obj->options->decompose_file_p &&
obj->options->is_multipart_msg &&
obj->options->decompose_file_init_fn )
{
if ( !mime_typep(obj,(MimeObjectClass*)&mimeMultipartRelatedClass) &&
!mime_typep(obj,(MimeObjectClass*)&mimeMultipartAlternativeClass) &&
!mime_typep(obj,(MimeObjectClass*)&mimeMultipartSignedClass) &&
#ifdef MIME_DETAIL_CHECK
!mime_typep(body, (MimeObjectClass*)&mimeMultipartRelatedClass) &&
!mime_typep(body, (MimeObjectClass*)&mimeMultipartAlternativeClass) &&
!mime_typep(body,(MimeObjectClass*)&mimeMultipartSignedClass)
#else
/* bug 21869 -- due to the fact that we are not generating the
correct mime class object for content-typ multipart/signed part
the above check failed. to solve the problem in general and not
to cause early temination when parsing message for opening as
draft we can simply make sure that the child is not a multipart
mime object. this way we could have a proper decomposing message
part functions set correctly */
!mime_typep(body, (MimeObjectClass*) &mimeMultipartClass)
#endif
&& ! (mime_typep(body, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(body->content_type, "text/x-vcard"))
)
{
status = obj->options->decompose_file_init_fn ( obj->options->stream_closure, mult->hdrs );
if (status < 0) return status;
}
}
#endif /* MIME_DRAFTS */
/* Now that we've added this new object to our list of children,
start its parser going (if we want to display it.)
*/
body->output_p = (((MimeMultipartClass *) obj->clazz)->output_child_p(obj, body));
if (body->output_p)
{
status = body->clazz->parse_begin(body);
#ifdef XP_MACOSX
/* if we are saving an apple double attachment, we need to set correctly the conten type of the channel */
if (mime_typep(obj, (MimeObjectClass *) &mimeMultipartAppleDoubleClass))
{
mime_stream_data *msd = (mime_stream_data *)body->options->stream_closure;
if (!body->options->write_html_p && body->content_type && !PL_strcasecmp(body->content_type, APPLICATION_APPLEFILE))
{
if (msd && msd->channel)
msd->channel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_APPLEFILE));
}
}
#endif
if (status < 0) return status;
}
return 0;
}
static bool
MimeMultipart_output_child_p(MimeObject *obj, MimeObject *child)
{
/* We don't output a child if we're stripping it. */
if (obj->options && obj->options->state && obj->options->state->strippingPart)
return false;
/* if we are saving an apple double attachment, ignore the appledouble wrapper part */
return (obj->options && obj->options->write_html_p) ||
PL_strcasecmp(child->content_type, MULTIPART_APPLEDOUBLE);
}
static int
MimeMultipart_close_child(MimeObject *object)
{
MimeMultipart *mult = (MimeMultipart *) object;
MimeContainer *cont = (MimeContainer *) object;
if (!mult->hdrs)
return 0;
MimeHeaders_free(mult->hdrs);
mult->hdrs = 0;
NS_ASSERTION(cont->nchildren > 0, "badly formed mime message");
if (cont->nchildren > 0)
{
MimeObject *kid = cont->children[cont->nchildren-1];
// If we have a child and it has not already been closed, process it.
// The kid would be already be closed if we encounter a multipart section
// that did not have a fully delineated header block. No header block means
// no creation of a new child, but the termination case still happens and
// we still end up here. Obviously, we don't want to close the child a
// second time and the best thing we can do is nothing.
if (kid && !kid->closed_p)
{
int status;
status = kid->clazz->parse_eof(kid, false);
if (status < 0) return status;
status = kid->clazz->parse_end(kid, false);
if (status < 0) return status;
#ifdef MIME_DRAFTS
if ( object->options &&
object->options->decompose_file_p &&
object->options->is_multipart_msg &&
object->options->decompose_file_close_fn )
{
if ( !mime_typep(object,(MimeObjectClass*)&mimeMultipartRelatedClass) &&
!mime_typep(object,(MimeObjectClass*)&mimeMultipartAlternativeClass) &&
!mime_typep(object,(MimeObjectClass*)&mimeMultipartSignedClass) &&
#ifdef MIME_DETAIL_CHECK
!mime_typep(kid,(MimeObjectClass*)&mimeMultipartRelatedClass) &&
!mime_typep(kid,(MimeObjectClass*)&mimeMultipartAlternativeClass) &&
!mime_typep(kid,(MimeObjectClass*)&mimeMultipartSignedClass)
#else
/* bug 21869 -- due to the fact that we are not generating the
correct mime class object for content-typ multipart/signed part
the above check failed. to solve the problem in general and not
to cause early temination when parsing message for opening as
draft we can simply make sure that the child is not a multipart
mime object. this way we could have a proper decomposing message
part functions set correctly */
!mime_typep(kid,(MimeObjectClass*) &mimeMultipartClass)
#endif
&& !(mime_typep(kid, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(kid->content_type, "text/x-vcard"))
)
{
status = object->options->decompose_file_close_fn ( object->options->stream_closure );
if (status < 0) return status;
}
}
#endif /* MIME_DRAFTS */
}
}
return 0;
}
static int
MimeMultipart_parse_child_line (MimeObject *obj, const char *line, int32_t length,
bool first_line_p)
{
MimeContainer *cont = (MimeContainer *) obj;
int status;
MimeObject *kid;
PR_ASSERT(cont->nchildren > 0);
if (cont->nchildren <= 0)
return -1;
kid = cont->children[cont->nchildren-1];
PR_ASSERT(kid);
if (!kid) return -1;
#ifdef MIME_DRAFTS
if ( obj->options &&
obj->options->decompose_file_p &&
obj->options->is_multipart_msg &&
obj->options->decompose_file_output_fn )
{
if (!mime_typep(obj,(MimeObjectClass*)&mimeMultipartAlternativeClass) &&
!mime_typep(obj,(MimeObjectClass*)&mimeMultipartRelatedClass) &&
!mime_typep(obj,(MimeObjectClass*)&mimeMultipartSignedClass) &&
#ifdef MIME_DETAIL_CHECK
!mime_typep(kid,(MimeObjectClass*)&mimeMultipartAlternativeClass) &&
!mime_typep(kid,(MimeObjectClass*)&mimeMultipartRelatedClass) &&
!mime_typep(kid,(MimeObjectClass*)&mimeMultipartSignedClass)
#else
/* bug 21869 -- due to the fact that we are not generating the
correct mime class object for content-typ multipart/signed part
the above check failed. to solve the problem in general and not
to cause early temination when parsing message for opening as
draft we can simply make sure that the child is not a multipart
mime object. this way we could have a proper decomposing message
part functions set correctly */
!mime_typep(kid, (MimeObjectClass*) &mimeMultipartClass)
#endif
&& !(mime_typep(kid, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(kid->content_type, "text/x-vcard"))
)
return obj->options->decompose_file_output_fn (line, length, obj->options->stream_closure);
}
#endif /* MIME_DRAFTS */
/* The newline issues here are tricky, since both the newlines before
and after the boundary string are to be considered part of the
boundary: this is so that a part can be specified such that it
does not end in a trailing newline.
To implement this, we send a newline *before* each line instead
of after, except for the first line, which is not preceeded by a
newline.
*/
/* Remove the trailing newline... */
if (length > 0 && line[length-1] == '\n') length--;
if (length > 0 && line[length-1] == '\r') length--;
if (!first_line_p)
{
/* Push out a preceeding newline... */
char nl[] = MSG_LINEBREAK;
status = kid->clazz->parse_buffer (nl, MSG_LINEBREAK_LEN, kid);
if (status < 0) return status;
}
/* Now push out the line sans trailing newline. */
return kid->clazz->parse_buffer (line, length, kid);
}
static int
MimeMultipart_parse_eof (MimeObject *obj, bool abort_p)
{
MimeMultipart *mult = (MimeMultipart *) obj;
MimeContainer *cont = (MimeContainer *) obj;
if (obj->closed_p) return 0;
/* Push out the last trailing line if there's one in the buffer. If
this happens, this object does not end in a trailing newline (and
the parse_line method will be called with a string with no trailing
newline, which isn't the usual case.)
*/
if (!abort_p && obj->ibuffer_fp > 0)
{
/* There is leftover data without a terminating newline. */
int status = obj->clazz->parse_line(obj->ibuffer, obj->ibuffer_fp,obj);
obj->ibuffer_fp = 0;
if (status < 0)
{
obj->closed_p = true;
return status;
}
}
/* Now call parse_eof for our active child, if there is one.
*/
if (cont->nchildren > 0 &&
(mult->state == MimeMultipartPartLine ||
mult->state == MimeMultipartPartFirstLine))
{
MimeObject *kid = cont->children[cont->nchildren-1];
NS_ASSERTION(kid, "not expecting null kid");
if (kid)
{
int status = kid->clazz->parse_eof(kid, abort_p);
if (status < 0) return status;
}
}
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
}
#if defined(DEBUG) && defined(XP_UNIX)
static int
MimeMultipart_debug_print (MimeObject *obj, PRFileDesc *stream, int32_t depth)
{
/* MimeMultipart *mult = (MimeMultipart *) obj; */
MimeContainer *cont = (MimeContainer *) obj;
char *addr = mime_part_address(obj);
int i;
for (i=0; i < depth; i++)
PR_Write(stream, " ", 2);
/**
fprintf(stream, "<%s %s (%d kid%s) boundary=%s 0x%08X>\n",
obj->clazz->class_name,
addr ? addr : "???",
cont->nchildren, (cont->nchildren == 1 ? "" : "s"),
(mult->boundary ? mult->boundary : "(none)"),
(uint32_t) mult);
**/
PR_FREEIF(addr);
/*
if (cont->nchildren > 0)
fprintf(stream, "\n");
*/
for (i = 0; i < cont->nchildren; i++)
{
MimeObject *kid = cont->children[i];
int status = kid->clazz->debug_print (kid, stream, depth+1);
if (status < 0) return status;
}
/*
if (cont->nchildren > 0)
fprintf(stream, "\n");
*/
return 0;
}
#endif

View file

@ -0,0 +1,102 @@
/* -*- 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/. */
#ifndef _MIMEMULT_H_
#define _MIMEMULT_H_
#include "mimecont.h"
/* The MimeMultipart class class implements the objects representing all of
the "multipart/" MIME types. In addition to the methods inherited from
MimeContainer, it provides the following methods and class variables:
int create_child (MimeObject *obj)
When it has been determined that a new sub-part should be created,
this method is called to do that. The default value for this method
does it in the usual multipart/mixed way. The headers of the object-
to-be-created may be found in the `hdrs' slot of the `MimeMultipart'
object.
bool output_child_p (MimeObject *parent, MimeObject *child)
Whether this child should be output. Default method always says `yes'.
int parse_child_line (MimeObject *obj, const char *line, int32_t length,
bool first_line_p)
When we have a line which should be handed off to the currently-active
child object, this method is called to do that. The `first_line_p'
variable will be true only for the very first line handed off to this
sub-part. The default method simply passes the line to the most-
recently-added child object.
int close_child (MimeObject *self)
When we reach the end of a sub-part (a separator line) this method is
called to shut down the currently-active child. The default method
simply calls `parse_eof' on the most-recently-added child object.
MimeMultipartBoundaryType check_boundary (MimeObject *obj,
const char *line, int32_t length)
This method is used to examine a line and determine whether it is a
part boundary, and if so, what kind. It should return a member of
the MimeMultipartBoundaryType describing the line.
const char *default_part_type
This is the type which should be assumed for sub-parts which have
no explicit type specified. The default is "text/plain", but the
"multipart/digest" subclass overrides this to "message/rfc822".
*/
typedef struct MimeMultipartClass MimeMultipartClass;
typedef struct MimeMultipart MimeMultipart;
typedef enum {
MimeMultipartPreamble,
MimeMultipartHeaders,
MimeMultipartPartFirstLine,
MimeMultipartPartLine,
MimeMultipartEpilogue
} MimeMultipartParseState;
typedef enum {
MimeMultipartBoundaryTypeNone,
MimeMultipartBoundaryTypeSeparator,
MimeMultipartBoundaryTypeTerminator
} MimeMultipartBoundaryType;
struct MimeMultipartClass {
MimeContainerClass container;
const char *default_part_type;
int (*create_child) (MimeObject *);
bool (*output_child_p) (MimeObject *self, MimeObject *child);
int (*close_child) (MimeObject *);
int (*parse_child_line) (MimeObject *, const char *line, int32_t length,
bool first_line_p);
MimeMultipartBoundaryType (*check_boundary) (MimeObject *, const char *line,
int32_t length);
};
extern MimeMultipartClass mimeMultipartClass;
struct MimeMultipart {
MimeContainer container; /* superclass variables */
char *boundary; /* Inter-part delimiter string */
MimeHeaders *hdrs; /* headers of the part currently
being parsed, if any */
MimeMultipartParseState state; /* State of parser */
};
extern void MimeMultipart_notify_emitter(MimeObject *);
#define MimeMultipartClassInitializer(ITYPE,CSUPER) \
{ MimeContainerClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEMULT_H_ */

View file

@ -0,0 +1,327 @@
/* -*- 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/.
* This Original Code has been modified by IBM Corporation. Modifications made by IBM
* described herein are Copyright (c) International Business Machines Corporation, 2000.
* Modifications to Mozilla code or documentation identified per MPL Section 3.3
*
* Date Modified by Description of modification
* 04/20/2000 IBM Corp. OS/2 VisualAge build.
*/
#include "mimeobj.h"
#include "prmem.h"
#include "plstr.h"
#include "prio.h"
#include "mimebuf.h"
#include "prlog.h"
#include "nsMimeTypes.h"
#include "nsMimeStringResources.h"
#include "nsMsgUtils.h"
#include "mimemsg.h"
#include "mimemapl.h"
/* Way to destroy any notions of modularity or class hierarchy, Terry! */
# include "mimetpla.h"
# include "mimethtm.h"
# include "mimecont.h"
MimeDefClass (MimeObject, MimeObjectClass, mimeObjectClass, NULL);
static int MimeObject_initialize (MimeObject *);
static void MimeObject_finalize (MimeObject *);
static int MimeObject_parse_begin (MimeObject *);
static int MimeObject_parse_buffer (const char *, int32_t, MimeObject *);
static int MimeObject_parse_line (const char *, int32_t, MimeObject *);
static int MimeObject_parse_eof (MimeObject *, bool);
static int MimeObject_parse_end (MimeObject *, bool);
static bool MimeObject_displayable_inline_p (MimeObjectClass *clazz,
MimeHeaders *hdrs);
#if defined(DEBUG) && defined(XP_UNIX)
static int MimeObject_debug_print (MimeObject *, PRFileDesc *, int32_t depth);
#endif
static int
MimeObjectClassInitialize(MimeObjectClass *clazz)
{
NS_ASSERTION(!clazz->class_initialized, "class shouldn't already be initialized");
clazz->initialize = MimeObject_initialize;
clazz->finalize = MimeObject_finalize;
clazz->parse_begin = MimeObject_parse_begin;
clazz->parse_buffer = MimeObject_parse_buffer;
clazz->parse_line = MimeObject_parse_line;
clazz->parse_eof = MimeObject_parse_eof;
clazz->parse_end = MimeObject_parse_end;
clazz->displayable_inline_p = MimeObject_displayable_inline_p;
#if defined(DEBUG) && defined(XP_UNIX)
clazz->debug_print = MimeObject_debug_print;
#endif
return 0;
}
static int
MimeObject_initialize (MimeObject *obj)
{
/* This is an abstract class; it shouldn't be directly instantiated. */
NS_ASSERTION(obj->clazz != &mimeObjectClass, "should directly instantiate abstract class");
/* Set up the content-type and encoding. */
if (!obj->content_type && obj->headers)
obj->content_type = MimeHeaders_get (obj->headers, HEADER_CONTENT_TYPE,
true, false);
if (!obj->encoding && obj->headers)
obj->encoding = MimeHeaders_get (obj->headers,
HEADER_CONTENT_TRANSFER_ENCODING,
true, false);
/* Special case to normalize some types and encodings to a canonical form.
(These are nonstandard types/encodings which have been seen to appear in
multiple forms; we normalize them so that things like looking up icons
and extensions has consistent behavior for the receiver, regardless of
the "alias" type that the sender used.)
*/
if (!obj->content_type || !*(obj->content_type))
;
else if (!PL_strcasecmp(obj->content_type, APPLICATION_UUENCODE2) ||
!PL_strcasecmp(obj->content_type, APPLICATION_UUENCODE3) ||
!PL_strcasecmp(obj->content_type, APPLICATION_UUENCODE4))
{
PR_Free(obj->content_type);
obj->content_type = strdup(APPLICATION_UUENCODE);
}
else if (!PL_strcasecmp(obj->content_type, IMAGE_XBM2) ||
!PL_strcasecmp(obj->content_type, IMAGE_XBM3))
{
PR_Free(obj->content_type);
obj->content_type = strdup(IMAGE_XBM);
}
else {
// MIME-types are case-insenitive, but let's make it lower case internally
// to avoid some hassle later down the road.
nsAutoCString lowerCaseContentType;
ToLowerCase(nsDependentCString(obj->content_type), lowerCaseContentType);
PR_Free(obj->content_type);
obj->content_type = ToNewCString(lowerCaseContentType);
}
if (!obj->encoding)
;
else if (!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE2) ||
!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE3) ||
!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE4))
{
PR_Free(obj->encoding);
obj->encoding = strdup(ENCODING_UUENCODE);
}
else if (!PL_strcasecmp(obj->encoding, ENCODING_COMPRESS2))
{
PR_Free(obj->encoding);
obj->encoding = strdup(ENCODING_COMPRESS);
}
else if (!PL_strcasecmp(obj->encoding, ENCODING_GZIP2))
{
PR_Free(obj->encoding);
obj->encoding = strdup(ENCODING_GZIP);
}
return 0;
}
static void
MimeObject_finalize (MimeObject *obj)
{
obj->clazz->parse_eof (obj, false);
obj->clazz->parse_end (obj, false);
if (obj->headers)
{
MimeHeaders_free(obj->headers);
obj->headers = 0;
}
/* Should have been freed by parse_eof, but just in case... */
NS_ASSERTION(!obj->ibuffer, "buffer not freed");
NS_ASSERTION(!obj->obuffer, "buffer not freed");
PR_FREEIF (obj->ibuffer);
PR_FREEIF (obj->obuffer);
PR_FREEIF(obj->content_type);
PR_FREEIF(obj->encoding);
if (obj->options && obj->options->state)
{
delete obj->options->state;
obj->options->state = nullptr;
}
}
static int
MimeObject_parse_begin (MimeObject *obj)
{
NS_ASSERTION (!obj->closed_p, "object shouldn't be already closed");
/* If we haven't set up the state object yet, then this should be
the outermost object... */
if (obj->options && !obj->options->state)
{
NS_ASSERTION(!obj->headers, "headers should be null"); /* should be the outermost object. */
obj->options->state = new MimeParseStateObject;
if (!obj->options->state) return MIME_OUT_OF_MEMORY;
obj->options->state->root = obj;
obj->options->state->separator_suppressed_p = true; /* no first sep */
const char *delParts = PL_strcasestr(obj->options->url, "&del=");
const char *detachLocations = PL_strcasestr(obj->options->url, "&detachTo=");
if (delParts)
{
const char *delEnd = PL_strcasestr(delParts + 1, "&");
if (!delEnd)
delEnd = delParts + strlen(delParts);
ParseString(Substring(delParts + 5, delEnd), ',', obj->options->state->partsToStrip);
}
if (detachLocations)
{
detachLocations += 10; // advance past "&detachTo="
ParseString(nsDependentCString(detachLocations), ',', obj->options->state->detachToFiles);
}
}
/* Decide whether this object should be output or not... */
if (!obj->options || obj->options->no_output_p || !obj->options->output_fn
/* if we are decomposing the message in files and processing a multipart object,
we must not output it without parsing it first */
|| (obj->options->decompose_file_p && obj->options->decompose_file_output_fn &&
mime_typep(obj, (MimeObjectClass*) &mimeMultipartClass))
)
obj->output_p = false;
else if (!obj->options->part_to_load)
obj->output_p = true;
else
{
char *id = mime_part_address(obj);
if (!id) return MIME_OUT_OF_MEMORY;
// We need to check if a part is the subpart of the part to load.
// If so and this is a raw or body display output operation, then
// we should mark the part for subsequent output.
// First, check for an exact match
obj->output_p = !strcmp(id, obj->options->part_to_load);
if (!obj->output_p && (obj->options->format_out == nsMimeOutput::nsMimeMessageRaw ||
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay ||
obj->options->format_out == nsMimeOutput::nsMimeMessageAttach))
{
// Then, check for subpart
unsigned int partlen = strlen(obj->options->part_to_load);
obj->output_p = (strlen(id) >= partlen + 2) && (id[partlen] == '.') &&
!strncmp(id, obj->options->part_to_load, partlen);
}
PR_Free(id);
}
// If we've decided not to output this part, we also shouldn't be showing it
// as an attachment.
obj->dontShowAsAttachment = !obj->output_p;
return 0;
}
static int
MimeObject_parse_buffer (const char *buffer, int32_t size, MimeObject *obj)
{
NS_ASSERTION(!obj->closed_p, "object shouldn't be closed");
if (obj->closed_p) return -1;
return mime_LineBuffer (buffer, size,
&obj->ibuffer, &obj->ibuffer_size, &obj->ibuffer_fp,
true,
((int (*) (char *, int32_t, void *))
/* This cast is to turn void into MimeObject */
obj->clazz->parse_line),
obj);
}
static int
MimeObject_parse_line (const char *line, int32_t length, MimeObject *obj)
{
NS_ERROR("shouldn't call this method");
return -1;
}
static int
MimeObject_parse_eof (MimeObject *obj, bool abort_p)
{
if (obj->closed_p) return 0;
NS_ASSERTION(!obj->parsed_p, "obj already parsed");
/* If there is still data in the ibuffer, that means that the last line of
this part didn't end in a newline; so push it out anyway (this means that
the parse_line method will be called with a string with no trailing
newline, which isn't the usual case.)
*/
if (!abort_p &&
obj->ibuffer_fp > 0)
{
int status = obj->clazz->parse_line (obj->ibuffer, obj->ibuffer_fp, obj);
obj->ibuffer_fp = 0;
if (status < 0)
{
obj->closed_p = true;
return status;
}
}
obj->closed_p = true;
return 0;
}
static int
MimeObject_parse_end (MimeObject *obj, bool abort_p)
{
if (obj->parsed_p)
{
NS_ASSERTION(obj->closed_p, "object should be closed");
return 0;
}
/* We won't be needing these buffers any more; nuke 'em. */
PR_FREEIF(obj->ibuffer);
obj->ibuffer_fp = 0;
obj->ibuffer_size = 0;
PR_FREEIF(obj->obuffer);
obj->obuffer_fp = 0;
obj->obuffer_size = 0;
obj->parsed_p = true;
return 0;
}
static bool
MimeObject_displayable_inline_p (MimeObjectClass *clazz, MimeHeaders *hdrs)
{
NS_ERROR("shouldn't call this method");
return false;
}
#if defined(DEBUG) && defined(XP_UNIX)
static int
MimeObject_debug_print (MimeObject *obj, PRFileDesc *stream, int32_t depth)
{
int i;
char *addr = mime_part_address(obj);
for (i=0; i < depth; i++)
PR_Write(stream, " ", 2);
/*
fprintf(stream, "<%s %s 0x%08X>\n", obj->clazz->class_name,
addr ? addr : "???",
(uint32_t) obj);
*/
PR_FREEIF(addr);
return 0;
}
#endif

186
mailnews/mime/src/mimeobj.h Normal file
View file

@ -0,0 +1,186 @@
/* -*- 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/. */
#ifndef _MIMEOBJ_H_
#define _MIMEOBJ_H_
#include "mimei.h"
#include "prio.h"
/* MimeObject is the base-class for the objects representing all other
MIME types. It provides several methods:
int initialize (MimeObject *obj)
This is called from mime_new() when a new instance is allocated.
Subclasses should do whatever setup is necessary from this method,
and should call the superclass's initialize method, unless there's
a specific reason not to.
void finalize (MimeObject *obj)
This is called from mime_free() and should free all data associated
with the object. If the object points to other MIME objects, they
should be finalized as well (by calling mime_free(), not by calling
their finalize() methods directly.)
int parse_buffer (const char *buf, int32_t size, MimeObject *obj)
This is the method by which you feed arbitrary data into the parser
for this object. Most subclasses will probably inherit this method
from the MimeObject base-class, which line-buffers the data and then
hands it off to the parse_line() method.
If this object uses a Content-Transfer-Encoding (base64, qp, uue)
then the data may be decoded by parse_buffer() before parse_line()
is called. (The MimeLeaf class provides this functionality.)
int parse_begin (MimeObject *obj)
Called after `init' but before `parse_line' or `parse_buffer'.
Can be used to initialize various parsing machinery.
int parse_line (const char *line, int32_t length, MimeObject *obj)
This method is called (by parse_buffer()) for each complete line of
data handed to the parser, and is the method which most subclasses
will override to implement their parsers.
When handing data off to a MIME object for parsing, one should always
call the parse_buffer() method, and not call the parse_line() method
directly, since the parse_buffer() method may do other transformations
on the data (like base64 decoding.)
One should generally not call parse_line() directly, since that could
bypass decoding. One should call parse_buffer() instead.
int parse_eof (MimeObject *obj, bool abort_p)
This is called when there is no more data to be handed to the object:
when the parent object is done feeding data to an object being parsed.
Implementors of this method should be sure to also call the parse_eof()
methods of any sub-objects to which they have pointers.
This is also called by the finalize() method, just before object
destruction, if it has not already been called.
The `closed_p' instance variable is used to prevent multiple calls to
`parse_eof'.
int parse_end (MimeObject *obj)
Called after `parse_eof' but before `finalize'.
This can be used to free up any memory no longer needed now that parsing
is done (to avoid surprises due to unexpected method combination, it's
best to free things in this method in preference to `parse_eof'.)
Implementors of this method should be sure to also call the parse_end()
methods of any sub-objects to which they have pointers.
This is also called by the finalize() method, just before object
destruction, if it has not already been called.
The `parsed_p' instance variable is used to prevent multiple calls to
`parse_end'.
bool displayable_inline_p (MimeObjectClass *class, MimeHeaders *hdrs)
This method should return true if this class of object will be displayed
directly, as opposed to being displayed as a link. This information is
used by the "multipart/alternative" parser to decide which of its children
is the ``best'' one to display. Note that this is a class method, not
an object method -- there is not yet an instance of this class at the time
that it is called. The `hdrs' provided are the headers of the object that
might be instantiated -- from this, the method may extract additional
infomation that it might need to make its decision.
*/
/* this one is typdedef'ed in mimei.h, since it is the base-class. */
struct MimeObjectClass {
/* Note: the order of these first five slots is known by MimeDefClass().
Technically, these are part of the object system, not the MIME code.
*/
const char *class_name;
int instance_size;
struct MimeObjectClass *superclass;
int (*class_initialize) (MimeObjectClass *clazz);
bool class_initialized;
/* These are the methods shared by all MIME objects. See comment above.
*/
int (*initialize) (MimeObject *obj);
void (*finalize) (MimeObject *obj);
int (*parse_begin) (MimeObject *obj);
int (*parse_buffer) (const char *buf, int32_t size, MimeObject *obj);
int (*parse_line) (const char *line, int32_t length, MimeObject *obj);
int (*parse_eof) (MimeObject *obj, bool abort_p);
int (*parse_end) (MimeObject *obj, bool abort_p);
bool (*displayable_inline_p) (MimeObjectClass *clazz, MimeHeaders *hdrs);
#if defined(DEBUG) && defined(XP_UNIX)
int (*debug_print) (MimeObject *obj, PRFileDesc *stream, int32_t depth);
#endif
};
extern "C" MimeObjectClass mimeObjectClass;
/* this one is typdedef'ed in mimei.h, since it is the base-class. */
struct MimeObject {
MimeObjectClass *clazz; /* Pointer to class object, for `type-of' */
MimeHeaders *headers; /* The header data associated with this object;
this is where the content-type, disposition,
description, and other meta-data live.
For example, the outermost message/rfc822 object
would have NULL here (since it has no parent,
thus no headers to describe it.) However, a
multipart/mixed object, which was the sole
child of that message/rfc822 object, would have
here a copy of the headers which began the
parent object (the headers which describe the
child.)
*/
char *content_type; /* The MIME content-type and encoding. */
char *encoding; /* In most cases, these will be the same as the
values to be found in the `headers' object,
but in some cases, the values in these slots
will be more correct than the headers.
*/
MimeObject *parent; /* Backpointer to a MimeContainer object. */
MimeDisplayOptions *options; /* Display preferences set by caller. */
bool closed_p; /* Whether it's done being written to. */
bool parsed_p; /* Whether the parser has been shut down. */
bool output_p; /* Whether it should be written. */
bool dontShowAsAttachment; /* Force an object to not be shown as attachment,
but when is false, it doesn't mean it will be
shown as attachment; specifically, body parts
are never shown as attachments. */
/* Read-buffer and write-buffer (on input, `parse_buffer' uses ibuffer to
compose calls to `parse_line'; on output, `obuffer' is used in various
ways by various routines.) These buffers are created and grow as needed.
`ibuffer' should be generally be considered hands-off, and `obuffer'
should generally be considered fair game.
*/
char *ibuffer, *obuffer;
int32_t ibuffer_size, obuffer_size;
int32_t ibuffer_fp, obuffer_fp;
};
#define MimeObject_grow_obuffer(obj, desired_size) \
(((desired_size) >= (obj)->obuffer_size) ? \
mime_GrowBuffer ((uint32_t)(desired_size), (uint32_t)sizeof(char), 1024, \
&(obj)->obuffer, (int32_t*)&(obj)->obuffer_size) \
: 0)
#endif /* _MIMEOBJ_H_ */

View file

@ -0,0 +1,296 @@
/* -*- 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 "nsCOMPtr.h"
#include "mimepbuf.h"
#include "mimemoz2.h"
#include "prmem.h"
#include "prio.h"
#include "plstr.h"
#include "nsMimeStringResources.h"
#include "nsNetUtil.h"
#include "nsMsgUtils.h"
//
// External Defines...
//
extern nsresult
nsMsgCreateTempFile(const char *tFileName, nsIFile **tFile);
/* See mimepbuf.h for a description of the mission of this file.
Implementation:
When asked to buffer an object, we first try to malloc() a buffer to
hold the upcoming part. First we try to allocate a 50k buffer, and
then back off by 5k until we are able to complete the allocation,
or are unable to allocate anything.
As data is handed to us, we store it in the memory buffer, until the
size of the memory buffer is exceeded (including the case where no
memory buffer was able to be allocated at all.)
Once we've filled the memory buffer, we open a temp file on disk.
Anything that is currently in the memory buffer is then flushed out
to the disk file (and the memory buffer is discarded.) Subsequent
data that is passed in is appended to the file.
Thus only one of the memory buffer or the disk buffer ever exist at
the same time; and small parts tend to live completely in memory
while large parts tend to live on disk.
When we are asked to read the data back out of the buffer, we call
the provided read-function with either: the contents of the memory
buffer; or blocks read from the disk file.
*/
#define TARGET_MEMORY_BUFFER_SIZE (1024 * 50) /* try for 50k mem buffer */
#define TARGET_MEMORY_BUFFER_QUANTUM (1024 * 5) /* decrease in steps of 5k */
#define DISK_BUFFER_SIZE (1024 * 10) /* read disk in 10k chunks */
struct MimePartBufferData
{
char *part_buffer; /* Buffer used for part-lookahead. */
int32_t part_buffer_fp; /* Active length. */
int32_t part_buffer_size; /* How big it is. */
nsCOMPtr <nsIFile> file_buffer; /* The nsIFile of a temp file used when we
run out of room in the head_buffer. */
nsCOMPtr <nsIInputStream> input_file_stream; /* A stream to it. */
nsCOMPtr <nsIOutputStream> output_file_stream; /* A stream to it. */
};
MimePartBufferData *
MimePartBufferCreate (void)
{
MimePartBufferData *data = PR_NEW(MimePartBufferData);
if (!data) return 0;
memset(data, 0, sizeof(*data));
return data;
}
void
MimePartBufferClose (MimePartBufferData *data)
{
NS_ASSERTION(data, "MimePartBufferClose: no data");
if (!data) return;
if (data->input_file_stream)
{
data->input_file_stream->Close();
data->input_file_stream = nullptr;
}
if (data->output_file_stream)
{
data->output_file_stream->Close();
data->output_file_stream = nullptr;
}
}
void
MimePartBufferReset (MimePartBufferData *data)
{
NS_ASSERTION(data, "MimePartBufferReset: no data");
if (!data) return;
PR_FREEIF(data->part_buffer);
data->part_buffer_fp = 0;
if (data->input_file_stream)
{
data->input_file_stream->Close();
data->input_file_stream = nullptr;
}
if (data->output_file_stream)
{
data->output_file_stream->Close();
data->output_file_stream = nullptr;
}
if (data->file_buffer)
{
data->file_buffer->Remove(false);
data->file_buffer = nullptr;
}
}
void
MimePartBufferDestroy (MimePartBufferData *data)
{
NS_ASSERTION(data, "MimePartBufferDestroy: no data");
if (!data) return;
MimePartBufferReset (data);
PR_Free(data);
}
int
MimePartBufferWrite (MimePartBufferData *data,
const char *buf, int32_t size)
{
NS_ASSERTION(data && buf && size > 0, "MimePartBufferWrite: Bad param");
if (!data || !buf || size <= 0)
return -1;
/* If we don't yet have a buffer (either memory or file) try and make a
memory buffer.
*/
if (!data->part_buffer &&
!data->file_buffer)
{
int target_size = TARGET_MEMORY_BUFFER_SIZE;
while (target_size > 0)
{
data->part_buffer = (char *) PR_MALLOC(target_size);
if (data->part_buffer) break; /* got it! */
target_size -= TARGET_MEMORY_BUFFER_QUANTUM; /* decrease it and try
again */
}
if (data->part_buffer)
data->part_buffer_size = target_size;
else
data->part_buffer_size = 0;
data->part_buffer_fp = 0;
}
/* Ok, if at this point we still don't have either kind of buffer, try and
make a file buffer. */
if (!data->part_buffer && !data->file_buffer)
{
nsCOMPtr <nsIFile> tmpFile;
nsresult rv = nsMsgCreateTempFile("nsma", getter_AddRefs(tmpFile));
NS_ENSURE_SUCCESS(rv, MIME_UNABLE_TO_OPEN_TMP_FILE);
data->file_buffer = do_QueryInterface(tmpFile);
rv = MsgNewBufferedFileOutputStream(getter_AddRefs(data->output_file_stream), data->file_buffer, PR_WRONLY | PR_CREATE_FILE, 00600);
NS_ENSURE_SUCCESS(rv, MIME_UNABLE_TO_OPEN_TMP_FILE);
}
NS_ASSERTION(data->part_buffer || data->output_file_stream, "no part_buffer or file_stream");
/* If this buf will fit in the memory buffer, put it there.
*/
if (data->part_buffer &&
data->part_buffer_fp + size < data->part_buffer_size)
{
memcpy(data->part_buffer + data->part_buffer_fp,
buf, size);
data->part_buffer_fp += size;
}
/* Otherwise it won't fit; write it to the file instead. */
else
{
/* If the file isn't open yet, open it, and dump the memory buffer
to it. */
if (!data->output_file_stream)
{
nsresult rv;
if (!data->file_buffer)
{
nsCOMPtr <nsIFile> tmpFile;
rv = nsMsgCreateTempFile("nsma", getter_AddRefs(tmpFile));
NS_ENSURE_SUCCESS(rv, MIME_UNABLE_TO_OPEN_TMP_FILE);
data->file_buffer = do_QueryInterface(tmpFile);
}
rv = MsgNewBufferedFileOutputStream(getter_AddRefs(data->output_file_stream), data->file_buffer, PR_WRONLY | PR_CREATE_FILE, 00600);
NS_ENSURE_SUCCESS(rv, MIME_UNABLE_TO_OPEN_TMP_FILE);
if (data->part_buffer && data->part_buffer_fp)
{
uint32_t bytesWritten;
nsresult rv = data->output_file_stream->Write(data->part_buffer,
data->part_buffer_fp, &bytesWritten);
NS_ENSURE_SUCCESS(rv, MIME_ERROR_WRITING_FILE);
}
PR_FREEIF(data->part_buffer);
data->part_buffer_fp = 0;
data->part_buffer_size = 0;
}
/* Dump this buf to the file. */
uint32_t bytesWritten;
nsresult rv = data->output_file_stream->Write (buf, size, &bytesWritten);
if (NS_FAILED(rv) || (int32_t) bytesWritten < size)
return MIME_OUT_OF_MEMORY;
}
return 0;
}
int
MimePartBufferRead (MimePartBufferData *data,
MimeConverterOutputCallback read_fn,
void *closure)
{
int status = 0;
NS_ASSERTION(data, "no data");
if (!data) return -1;
if (data->part_buffer)
{
// Read it out of memory.
status = read_fn(data->part_buffer, data->part_buffer_fp, closure);
}
else if (data->file_buffer)
{
/* Read it off disk.
*/
char *buf;
int32_t buf_size = DISK_BUFFER_SIZE;
NS_ASSERTION(data->part_buffer_size == 0 && data->part_buffer_fp == 0, "buffer size is not null");
NS_ASSERTION(data->file_buffer, "no file buffer name");
if (!data->file_buffer)
return -1;
buf = (char *) PR_MALLOC(buf_size);
if (!buf)
return MIME_OUT_OF_MEMORY;
// First, close the output file to open the input file!
if (data->output_file_stream)
data->output_file_stream->Close();
nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(data->input_file_stream), data->file_buffer);
if (NS_FAILED(rv))
{
PR_Free(buf);
return MIME_UNABLE_TO_OPEN_TMP_FILE;
}
while(1)
{
uint32_t bytesRead = 0;
rv = data->input_file_stream->Read(buf, buf_size - 1, &bytesRead);
if (NS_FAILED(rv) || !bytesRead)
{
break;
}
else
{
/* It would be really nice to be able to yield here, and let
some user events and other input sources get processed.
Oh well. */
status = read_fn (buf, bytesRead, closure);
if (status < 0) break;
}
}
PR_Free(buf);
}
return 0;
}

View file

@ -0,0 +1,64 @@
/* -*- 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/. */
#ifndef _MIMEPBUF_H_
#define _MIMEPBUF_H_
#include "mimei.h"
#include "modmimee.h" // for MimeConverterOutputCallback
/* This file provides the ability to save up the entire contents of a MIME
object (of arbitrary size), and then emit it all at once later. The
buffering is done in an efficient way that works well for both very large
and very small objects.
This is used in two places:
= The implementation of multipart/alternative uses this code to do a
one-part-lookahead. As it traverses its children, it moves forward
until it finds a part which cannot be displayed; and then it displays
the *previous* part (the last which *could* be displayed.) This code
is used to hold the previous part until it is needed.
*/
/* An opaque object used to represent the buffered data.
*/
typedef struct MimePartBufferData MimePartBufferData;
/* Create an empty part buffer object.
*/
extern MimePartBufferData *MimePartBufferCreate (void);
/* Assert that the buffer is now full (EOF has been reached on the current
part.) This will free some resources, but leaves the part in the buffer.
After calling MimePartBufferReset, the buffer may be used to store a
different object.
*/
void MimePartBufferClose (MimePartBufferData *data);
/* Reset a part buffer object to the default state, discarding any currently-
buffered data.
*/
extern void MimePartBufferReset (MimePartBufferData *data);
/* Free the part buffer itself, and discard any buffered data.
*/
extern void MimePartBufferDestroy (MimePartBufferData *data);
/* Push a chunk of a MIME object into the buffer.
*/
extern int MimePartBufferWrite (MimePartBufferData *data,
const char *buf, int32_t size);
/* Read the contents of the buffer back out. This will invoke the provided
read_fn with successive chunks of data until the buffer has been drained.
The provided function may be called once, or multiple times.
*/
extern int
MimePartBufferRead (MimePartBufferData *data,
MimeConverterOutputCallback read_fn,
void *closure);
#endif /* _MIMEPBUF_H_ */

View file

@ -0,0 +1,342 @@
/* -*- 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 "mimesun.h"
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "nsMimeTypes.h"
#include "msgCore.h"
#include "nsMimeStringResources.h"
#include <ctype.h>
#define MIME_SUPERCLASS mimeMultipartClass
MimeDefClass(MimeSunAttachment, MimeSunAttachmentClass,
mimeSunAttachmentClass, &MIME_SUPERCLASS);
static MimeMultipartBoundaryType MimeSunAttachment_check_boundary(MimeObject *,
const char *,
int32_t);
static int MimeSunAttachment_create_child(MimeObject *);
static int MimeSunAttachment_parse_child_line (MimeObject *, const char *, int32_t,
bool);
static int MimeSunAttachment_parse_begin (MimeObject *);
static int MimeSunAttachment_parse_eof (MimeObject *, bool);
static int
MimeSunAttachmentClassInitialize(MimeSunAttachmentClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeMultipartClass *mclass = (MimeMultipartClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
oclass->parse_begin = MimeSunAttachment_parse_begin;
oclass->parse_eof = MimeSunAttachment_parse_eof;
mclass->check_boundary = MimeSunAttachment_check_boundary;
mclass->create_child = MimeSunAttachment_create_child;
mclass->parse_child_line = MimeSunAttachment_parse_child_line;
return 0;
}
static int
MimeSunAttachment_parse_begin (MimeObject *obj)
{
int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
if (status < 0) return status;
/* Sun messages always have separators at the beginning. */
return MimeObject_write_separator(obj);
}
static int
MimeSunAttachment_parse_eof (MimeObject *obj, bool abort_p)
{
int status = 0;
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0) return status;
/* Sun messages always have separators at the end. */
if (!abort_p)
{
status = MimeObject_write_separator(obj);
if (status < 0) return status;
}
return 0;
}
static MimeMultipartBoundaryType
MimeSunAttachment_check_boundary(MimeObject *obj, const char *line,
int32_t length)
{
/* ten dashes */
if (line &&
line[0] == '-' && line[1] == '-' && line[2] == '-' && line[3] == '-' &&
line[4] == '-' && line[5] == '-' && line[6] == '-' && line[7] == '-' &&
line[8] == '-' && line[9] == '-' &&
(line[10] == '\r' || line[10] == '\n'))
return MimeMultipartBoundaryTypeSeparator;
else
return MimeMultipartBoundaryTypeNone;
}
static int
MimeSunAttachment_create_child(MimeObject *obj)
{
MimeMultipart *mult = (MimeMultipart *) obj;
int status = 0;
char *sun_data_type = 0;
const char *mime_ct = 0, *sun_enc_info = 0, *mime_cte = 0;
char *mime_ct2 = 0; /* sometimes we need to copy; this is for freeing. */
MimeObject *child = 0;
mult->state = MimeMultipartPartLine;
sun_data_type = (mult->hdrs
? MimeHeaders_get (mult->hdrs, HEADER_X_SUN_DATA_TYPE,
true, false)
: 0);
if (sun_data_type)
{
int i;
static const struct { const char *in, *out; } sun_types[] = {
/* Convert recognised Sun types to the corresponding MIME types,
and convert unrecognized ones based on the file extension and
the mime.types file.
These are the magic types used by MailTool that I can determine.
The only actual written spec I've found only listed the first few.
The rest were found by inspection (both of real-world messages,
and by running `strings' on the MailTool binary, and on the file
/usr/openwin/lib/cetables/cetables (the "Class Engine", Sun's
equivalent to .mailcap and mime.types.)
*/
{ "default", TEXT_PLAIN },
{ "default-doc", TEXT_PLAIN },
{ "text", TEXT_PLAIN },
{ "scribe", TEXT_PLAIN },
{ "sgml", TEXT_PLAIN },
{ "tex", TEXT_PLAIN },
{ "troff", TEXT_PLAIN },
{ "c-file", TEXT_PLAIN },
{ "h-file", TEXT_PLAIN },
{ "readme-file", TEXT_PLAIN },
{ "shell-script", TEXT_PLAIN },
{ "cshell-script", TEXT_PLAIN },
{ "makefile", TEXT_PLAIN },
{ "hidden-docs", TEXT_PLAIN },
{ "message", MESSAGE_RFC822 },
{ "mail-message", MESSAGE_RFC822 },
{ "mail-file", TEXT_PLAIN },
{ "gif-file", IMAGE_GIF },
{ "jpeg-file", IMAGE_JPG },
{ "ppm-file", IMAGE_PPM },
{ "pgm-file", "image/x-portable-graymap" },
{ "pbm-file", "image/x-portable-bitmap" },
{ "xpm-file", "image/x-xpixmap" },
{ "ilbm-file", "image/ilbm" },
{ "tiff-file", "image/tiff" },
{ "photocd-file", "image/x-photo-cd" },
{ "sun-raster", "image/x-sun-raster" },
{ "audio-file", AUDIO_BASIC },
{ "postscript", APPLICATION_POSTSCRIPT },
{ "postscript-file", APPLICATION_POSTSCRIPT },
{ "framemaker-document", "application/x-framemaker" },
{ "sundraw-document", "application/x-sun-draw" },
{ "sunpaint-document", "application/x-sun-paint" },
{ "sunwrite-document", "application/x-sun-write" },
{ "islanddraw-document", "application/x-island-draw" },
{ "islandpaint-document", "application/x-island-paint" },
{ "islandwrite-document", "application/x-island-write" },
{ "sun-executable", APPLICATION_OCTET_STREAM },
{ "default-app", APPLICATION_OCTET_STREAM },
{ 0, 0 }};
for (i = 0; sun_types[i].in; i++)
if (!PL_strcasecmp(sun_data_type, sun_types[i].in))
{
mime_ct = sun_types[i].out;
break;
}
}
/* If we didn't find a type, look at the extension on the file name.
*/
if (!mime_ct &&
obj->options &&
obj->options->file_type_fn)
{
char *name = MimeHeaders_get_name(mult->hdrs, obj->options);
if (name)
{
mime_ct2 = obj->options->file_type_fn(name,
obj->options->stream_closure);
mime_ct = mime_ct2;
PR_Free(name);
if (!mime_ct2 || !PL_strcasecmp (mime_ct2, UNKNOWN_CONTENT_TYPE))
{
PR_FREEIF(mime_ct2);
mime_ct = APPLICATION_OCTET_STREAM;
}
}
}
if (!mime_ct)
mime_ct = APPLICATION_OCTET_STREAM;
PR_FREEIF(sun_data_type);
/* Convert recognised Sun encodings to the corresponding MIME encodings.
However, if the X-Sun-Encoding-Info field contains more than one
encoding (that is, contains a comma) then assign it the encoding of
the *rightmost* element in the list; and change its Content-Type to
application/octet-stream. Examples:
Sun Type: Translates To:
================== ====================
type: TEXT type: text/plain
encoding: COMPRESS encoding: x-compress
type: POSTSCRIPT type: application/x-compress
encoding: COMPRESS,UUENCODE encoding: x-uuencode
type: TEXT type: application/octet-stream
encoding: UNKNOWN,UUENCODE encoding: x-uuencode
*/
sun_data_type = (mult->hdrs
? MimeHeaders_get (mult->hdrs, HEADER_X_SUN_ENCODING_INFO,
false,false)
: 0);
sun_enc_info = sun_data_type;
/* this "adpcm-compress" pseudo-encoding is some random junk that
MailTool adds to the encoding description of .AU files: we can
ignore it if it is the leftmost element of the encoding field.
(It looks like it's created via `audioconvert -f g721'. Why?
Who knows.)
*/
if (sun_enc_info && !PL_strncasecmp (sun_enc_info, "adpcm-compress", 14))
{
sun_enc_info += 14;
while (IS_SPACE(*sun_enc_info) || *sun_enc_info == ',')
sun_enc_info++;
}
/* Extract the last element of the encoding field, changing the content
type if necessary (as described above.)
*/
if (sun_enc_info && *sun_enc_info)
{
const char *prev;
const char *end = PL_strrchr(sun_enc_info, ',');
if (end)
{
const char *start = sun_enc_info;
sun_enc_info = end + 1;
while (IS_SPACE(*sun_enc_info))
sun_enc_info++;
for (prev = end-1; prev > start && *prev != ','; prev--)
;
if (*prev == ',') prev++;
if (!PL_strncasecmp (prev, "uuencode", end-prev))
mime_ct = APPLICATION_UUENCODE;
else if (!PL_strncasecmp (prev, "gzip", end-prev))
mime_ct = APPLICATION_GZIP;
else if (!PL_strncasecmp (prev, "compress", end-prev))
mime_ct = APPLICATION_COMPRESS;
else if (!PL_strncasecmp (prev, "default-compress", end-prev))
mime_ct = APPLICATION_COMPRESS;
else
mime_ct = APPLICATION_OCTET_STREAM;
}
}
/* Convert the remaining Sun encoding to a MIME encoding.
If it isn't known, change the content-type instead.
*/
if (!sun_enc_info || !*sun_enc_info)
;
else if (!PL_strcasecmp(sun_enc_info,"compress")) mime_cte = ENCODING_COMPRESS;
else if (!PL_strcasecmp(sun_enc_info,"uuencode")) mime_cte = ENCODING_UUENCODE;
else if (!PL_strcasecmp(sun_enc_info,"gzip")) mime_cte = ENCODING_GZIP;
else mime_ct = APPLICATION_OCTET_STREAM;
PR_FREEIF(sun_data_type);
/* Now that we know its type and encoding, create a MimeObject to represent
this part.
*/
child = mime_create(mime_ct, mult->hdrs, obj->options);
if (!child)
{
status = MIME_OUT_OF_MEMORY;
goto FAIL;
}
/* Fake out the child's content-type and encoding (it probably doesn't have
one right now, because the X-Sun- headers aren't generally recognised by
the rest of this library.)
*/
PR_FREEIF(child->content_type);
PR_FREEIF(child->encoding);
PR_ASSERT(mime_ct);
child->content_type = (mime_ct ? strdup(mime_ct) : 0);
child->encoding = (mime_cte ? strdup(mime_cte) : 0);
status = ((MimeContainerClass *) obj->clazz)->add_child(obj, child);
if (status < 0)
{
mime_free(child);
child = 0;
goto FAIL;
}
/* Sun attachments always have separators between parts. */
status = MimeObject_write_separator(obj);
if (status < 0) goto FAIL;
/* And now that we've added this new object to our list of
children, start its parser going. */
status = child->clazz->parse_begin(child);
if (status < 0) goto FAIL;
FAIL:
PR_FREEIF(mime_ct2);
PR_FREEIF(sun_data_type);
return status;
}
static int
MimeSunAttachment_parse_child_line (MimeObject *obj, const char *line, int32_t length,
bool first_line_p)
{
MimeContainer *cont = (MimeContainer *) obj;
MimeObject *kid;
/* This is simpler than MimeMultipart->parse_child_line in that it doesn't
play games about body parts without trailing newlines.
*/
PR_ASSERT(cont->nchildren > 0);
if (cont->nchildren <= 0)
return -1;
kid = cont->children[cont->nchildren-1];
PR_ASSERT(kid);
if (!kid) return -1;
return kid->clazz->parse_buffer (line, length, kid);
}

View file

@ -0,0 +1,59 @@
/* -*- 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/. */
#ifndef _MIMESUN_H_
#define _MIMESUN_H_
#include "mimemult.h"
/* MimeSunAttachment is the class for X-Sun-Attachment message contents, which
is the Content-Type assigned by that pile of garbage called MailTool. This
is not a MIME type per se, but it's very similar to multipart/mixed, so it's
easy to parse. Lots of people use MailTool, so what the hell.
The format is this:
= Content-Type is X-Sun-Attachment
= parts are separated by lines of exactly ten dashes
= just after the dashes comes a block of headers, including:
X-Sun-Data-Type: (manditory)
Values are Text, Postscript, Scribe, SGML, TeX, Troff, DVI,
and Message.
X-Sun-Encoding-Info: (optional)
Ordered, comma-separated values, including Compress and Uuencode.
X-Sun-Data-Name: (optional)
File name, maybe.
X-Sun-Data-Description: (optional)
Longer text.
X-Sun-Content-Lines: (manditory, unless Length is present)
Number of lines in the body, not counting headers and the blank
line that follows them.
X-Sun-Content-Length: (manditory, unless Lines is present)
Bytes, presumably using Unix line terminators.
*/
typedef struct MimeSunAttachmentClass MimeSunAttachmentClass;
typedef struct MimeSunAttachment MimeSunAttachment;
struct MimeSunAttachmentClass {
MimeMultipartClass multipart;
};
extern MimeSunAttachmentClass mimeSunAttachmentClass;
struct MimeSunAttachment {
MimeMultipart multipart;
};
#define MimeSunAttachmentClassInitializer(ITYPE,CSUPER) \
{ MimeMultipartClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMESUN_H_ */

View file

@ -0,0 +1,28 @@
/* -*- 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 "mimetenr.h"
#include "prlog.h"
/* All the magic for this class is in mimetric.c; since text/enriched and
text/richtext are so similar, it was easiest to implement them in the
same method (but this is a subclass anyway just for general goodness.)
*/
#define MIME_SUPERCLASS mimeInlineTextRichtextClass
MimeDefClass(MimeInlineTextEnriched, MimeInlineTextEnrichedClass,
mimeInlineTextEnrichedClass, &MIME_SUPERCLASS);
static int
MimeInlineTextEnrichedClassInitialize(MimeInlineTextEnrichedClass *clazz)
{
#ifdef DEBUG
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
#endif
MimeInlineTextRichtextClass *rclass = (MimeInlineTextRichtextClass *) clazz;
rclass->enriched_p = true;
return 0;
}

View file

@ -0,0 +1,32 @@
/* -*- 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/. */
#ifndef _MIMETENR_H_
#define _MIMETENR_H_
#include "mimetric.h"
/* The MimeInlineTextEnriched class implements the text/enriched MIME content
type, as defined in RFC 1563. It does this largely by virtue of being a
subclass of the MimeInlineTextRichtext class.
*/
typedef struct MimeInlineTextEnrichedClass MimeInlineTextEnrichedClass;
typedef struct MimeInlineTextEnriched MimeInlineTextEnriched;
struct MimeInlineTextEnrichedClass {
MimeInlineTextRichtextClass text;
};
extern MimeInlineTextEnrichedClass mimeInlineTextEnrichedClass;
struct MimeInlineTextEnriched {
MimeInlineTextRichtext richtext;
};
#define MimeInlineTextEnrichedClassInitializer(ITYPE,CSUPER) \
{ MimeInlineTextRichtextClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMETENR_H_ */

View file

@ -0,0 +1,544 @@
/* -*- 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/.
* This Original Code has been modified by IBM Corporation. Modifications made by IBM
* described herein are Copyright (c) International Business Machines Corporation, 2000.
* Modifications to Mozilla code or documentation identified per MPL Section 3.3
*
* Date Modified by Description of modification
* 04/20/2000 IBM Corp. OS/2 VisualAge build.
*/
#include "mimetext.h"
#include "mimebuf.h"
#include "mimethtm.h"
#include "comi18n.h"
#include "mimemoz2.h"
#include "prlog.h"
#include "prmem.h"
#include "plstr.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIServiceManager.h"
#include "nsIPrefLocalizedString.h"
#include "nsMsgUtils.h"
#include "nsMimeTypes.h"
#include "nsServiceManagerUtils.h"
#define MIME_SUPERCLASS mimeLeafClass
MimeDefClass(MimeInlineText, MimeInlineTextClass, mimeInlineTextClass,
&MIME_SUPERCLASS);
static int MimeInlineText_initialize (MimeObject *);
static void MimeInlineText_finalize (MimeObject *);
static int MimeInlineText_rot13_line (MimeObject *, char *line, int32_t length);
static int MimeInlineText_parse_eof (MimeObject *obj, bool abort_p);
static int MimeInlineText_parse_end (MimeObject *, bool);
static int MimeInlineText_parse_decoded_buffer (const char *, int32_t, MimeObject *);
static int MimeInlineText_rotate_convert_and_parse_line(char *, int32_t,
MimeObject *);
static int MimeInlineText_open_dam(char *line, int32_t length, MimeObject *obj);
static int MimeInlineText_initializeCharset(MimeObject *obj);
static int
MimeInlineTextClassInitialize(MimeInlineTextClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
MimeLeafClass *lclass = (MimeLeafClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
oclass->initialize = MimeInlineText_initialize;
oclass->finalize = MimeInlineText_finalize;
oclass->parse_eof = MimeInlineText_parse_eof;
oclass->parse_end = MimeInlineText_parse_end;
clazz->rot13_line = MimeInlineText_rot13_line;
clazz->initialize_charset = MimeInlineText_initializeCharset;
lclass->parse_decoded_buffer = MimeInlineText_parse_decoded_buffer;
return 0;
}
static int
MimeInlineText_initialize (MimeObject *obj)
{
/* This is an abstract class; it shouldn't be directly instantiated. */
PR_ASSERT(obj->clazz != (MimeObjectClass *) &mimeInlineTextClass);
((MimeInlineText *) obj)->initializeCharset = false;
((MimeInlineText *) obj)->needUpdateMsgWinCharset = false;
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(obj);
}
static int MimeInlineText_initializeCharset(MimeObject *obj)
{
MimeInlineText *text = (MimeInlineText *) obj;
text->inputAutodetect = false;
text->charsetOverridable = false;
/* Figure out an appropriate charset for this object.
*/
if (!text->charset && obj->headers)
{
if (obj->options && obj->options->override_charset)
{
text->charset = strdup(obj->options->default_charset);
}
else
{
char *ct = MimeHeaders_get (obj->headers, HEADER_CONTENT_TYPE,
false, false);
if (ct)
{
text->charset = MimeHeaders_get_parameter (ct, "charset", NULL, NULL);
PR_Free(ct);
}
if (!text->charset)
{
/* If we didn't find "Content-Type: ...; charset=XX" then look
for "X-Sun-Charset: XX" instead. (Maybe this should be done
in MimeSunAttachmentClass, but it's harder there than here.)
*/
text->charset = MimeHeaders_get (obj->headers,
HEADER_X_SUN_CHARSET,
false, false);
}
/* iMIP entities without an explicit charset parameter default to
US-ASCII (RFC 2447, section 2.4). However, Microsoft Outlook generates
UTF-8 but omits the charset parameter.
When no charset is defined by the container (e.g. iMIP), iCalendar
files default to UTF-8 (RFC 2445, section 4.1.4).
*/
if (!text->charset &&
obj->content_type &&
!PL_strcasecmp(obj->content_type, TEXT_CALENDAR))
text->charset = strdup("UTF-8");
if (!text->charset)
{
nsresult res;
text->charsetOverridable = true;
nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &res));
if (NS_SUCCEEDED(res))
{
nsCOMPtr<nsIPrefLocalizedString> str;
if (NS_SUCCEEDED(prefBranch->GetComplexValue("intl.charset.detector", NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(str)))) {
//only if we can get autodetector name correctly, do we set this to true
text->inputAutodetect = true;
}
}
if (obj->options && obj->options->default_charset)
text->charset = strdup(obj->options->default_charset);
else
{
if (NS_SUCCEEDED(res))
{
nsString value;
NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.view_default_charset", EmptyString(), value);
text->charset = ToNewUTF8String(value);
}
else
text->charset = strdup("");
}
}
}
}
if (text->inputAutodetect)
{
//we need to prepare lineDam for charset detection
text->lineDamBuffer = (char*)PR_Malloc(DAM_MAX_BUFFER_SIZE);
text->lineDamPtrs = (char**)PR_Malloc(DAM_MAX_LINES*sizeof(char*));
text->curDamOffset = 0;
text->lastLineInDam = 0;
if (!text->lineDamBuffer || !text->lineDamPtrs)
{
text->inputAutodetect = false;
PR_FREEIF(text->lineDamBuffer);
PR_FREEIF(text->lineDamPtrs);
}
}
text->initializeCharset = true;
return 0;
}
static void
MimeInlineText_finalize (MimeObject *obj)
{
MimeInlineText *text = (MimeInlineText *) obj;
obj->clazz->parse_eof (obj, false);
obj->clazz->parse_end (obj, false);
text->inputDecoder = nullptr;
text->utf8Encoder = nullptr;
PR_FREEIF(text->charset);
/* Should have been freed by parse_eof, but just in case... */
PR_ASSERT(!text->cbuffer);
PR_FREEIF (text->cbuffer);
if (text->inputAutodetect) {
PR_FREEIF(text->lineDamBuffer);
PR_FREEIF(text->lineDamPtrs);
text->inputAutodetect = false;
}
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize (obj);
}
static int
MimeInlineText_parse_eof (MimeObject *obj, bool abort_p)
{
int status;
if (obj->closed_p) return 0;
NS_ASSERTION(!obj->parsed_p, "obj already parsed");
MimeInlineText *text = (MimeInlineText *) obj;
/* Flush any buffered data from the MimeLeaf's decoder */
status = ((MimeLeafClass*)&MIME_SUPERCLASS)->close_decoder(obj);
if (status < 0) return status;
/* If there is still data in the ibuffer, that means that the last
line of this part didn't end in a newline; so push it out anyway
(this means that the parse_line method will be called with a string
with no trailing newline, which isn't the usual case). We do this
here, rather than in MimeObject_parse_eof, because MimeObject isn't
aware of the rotating-and-converting / charset detection we need to
do first.
*/
if (!abort_p && obj->ibuffer_fp > 0)
{
status = MimeInlineText_rotate_convert_and_parse_line (obj->ibuffer,
obj->ibuffer_fp,
obj);
obj->ibuffer_fp = 0;
if (status < 0)
{
//we haven't find charset yet? Do it before return
if (text->inputAutodetect)
status = MimeInlineText_open_dam(nullptr, 0, obj);
obj->closed_p = true;
return status;
}
}
//we haven't find charset yet? now its the time
if (text->inputAutodetect)
status = MimeInlineText_open_dam(nullptr, 0, obj);
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof (obj, abort_p);
}
static int
MimeInlineText_parse_end (MimeObject *obj, bool abort_p)
{
MimeInlineText *text = (MimeInlineText *) obj;
if (obj->parsed_p)
{
PR_ASSERT(obj->closed_p);
return 0;
}
/* We won't be needing this buffer any more; nuke it. */
PR_FREEIF(text->cbuffer);
text->cbuffer_size = 0;
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_end (obj, abort_p);
}
/* This maps A-M to N-Z and N-Z to A-M. All other characters are left alone.
(Comments in GNUS imply that for Japanese, one should rotate by 47?)
*/
static const unsigned char MimeInlineText_rot13_table[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
59, 60, 61, 62, 63, 64, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 91, 92, 93, 94, 95, 96,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 97, 98,
99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 123, 124, 125, 126,
127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201,
202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216,
217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231,
232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246,
247, 248, 249, 250, 251, 252, 253, 254, 255 };
static int
MimeInlineText_rot13_line (MimeObject *obj, char *line, int32_t length)
{
unsigned char *s, *end;
PR_ASSERT(line);
if (!line) return -1;
s = (unsigned char *) line;
end = s + length;
while (s < end)
{
*s = MimeInlineText_rot13_table[*s];
s++;
}
return 0;
}
static int
MimeInlineText_parse_decoded_buffer (const char *buf, int32_t size, MimeObject *obj)
{
PR_ASSERT(!obj->closed_p);
if (obj->closed_p) return -1;
/* MimeLeaf takes care of this. */
PR_ASSERT(obj->output_p && obj->options && obj->options->output_fn);
if (!obj->options) return -1;
/* If we're supposed to write this object, but aren't supposed to convert
it to HTML, simply pass it through unaltered. */
if (!obj->options->write_html_p && obj->options->format_out != nsMimeOutput::nsMimeMessageAttach)
return MimeObject_write(obj, buf, size, true);
/* This is just like the parse_decoded_buffer method we inherit from the
MimeLeaf class, except that we line-buffer to our own wrapper on the
`parse_line' method instead of calling the `parse_line' method directly.
*/
return mime_LineBuffer (buf, size,
&obj->ibuffer, &obj->ibuffer_size, &obj->ibuffer_fp,
true,
((int (*) (char *, int32_t, void *))
/* This cast is to turn void into MimeObject */
MimeInlineText_rotate_convert_and_parse_line),
obj);
}
#define MimeInlineText_grow_cbuffer(text, desired_size) \
(((desired_size) >= (text)->cbuffer_size) ? \
mime_GrowBuffer ((desired_size), sizeof(char), 100, \
&(text)->cbuffer, &(text)->cbuffer_size) \
: 0)
static int
MimeInlineText_convert_and_parse_line(char *line, int32_t length, MimeObject *obj)
{
int status;
char *converted = 0;
int32_t converted_len = 0;
MimeInlineText *text = (MimeInlineText *) obj;
//in case of charset autodetection, charset can be override by meta charset
if (text->charsetOverridable) {
if (mime_typep(obj, (MimeObjectClass *) &mimeInlineTextHTMLClass))
{
MimeInlineTextHTML *textHTML = (MimeInlineTextHTML *) obj;
if (textHTML->charset &&
*textHTML->charset &&
strcmp(textHTML->charset, text->charset))
{
//if meta tag specified charset is different from our detected result, use meta charset.
//but we don't want to redo previous lines
MIME_get_unicode_decoder(textHTML->charset, getter_AddRefs(text->inputDecoder));
PR_FREEIF(text->charset);
text->charset = strdup(textHTML->charset);
//update MsgWindow charset if we are instructed to do so
if (text->needUpdateMsgWinCharset && *text->charset)
SetMailCharacterSetToMsgWindow(obj, text->charset);
}
}
}
//initiate decoder if not yet
if (text->inputDecoder == nullptr)
MIME_get_unicode_decoder(text->charset, getter_AddRefs(text->inputDecoder));
// If no decoder found, use ""UTF-8"", that will map most non-US-ASCII chars as invalid
// A pure-ASCII only decoder would be better, but there is none
if (text->inputDecoder == nullptr)
MIME_get_unicode_decoder("UTF-8", getter_AddRefs(text->inputDecoder));
if (text->utf8Encoder == nullptr)
MIME_get_unicode_encoder("UTF-8", getter_AddRefs(text->utf8Encoder));
bool useInputCharsetConverter = obj->options->m_inputCharsetToUnicodeDecoder && !PL_strcasecmp(text->charset, obj->options->charsetForCachedInputDecoder.get());
if (useInputCharsetConverter)
status = obj->options->charset_conversion_fn(line, length,
text->charset,
"UTF-8",
&converted,
&converted_len,
obj->options->stream_closure, obj->options->m_inputCharsetToUnicodeDecoder,
obj->options->m_unicodeToUTF8Encoder);
else
status = obj->options->charset_conversion_fn(line, length,
text->charset,
"UTF-8",
&converted,
&converted_len,
obj->options->stream_closure, (nsIUnicodeDecoder*)text->inputDecoder,
(nsIUnicodeEncoder*)text->utf8Encoder);
if (status < 0)
{
PR_FREEIF(converted);
return status;
}
if (converted)
{
line = converted;
length = converted_len;
}
/* Now that the line has been converted, call the subclass's parse_line
method with the decoded data. */
status = obj->clazz->parse_line(line, length, obj);
PR_FREEIF(converted);
return status;
}
//In this function call, all buffered lines in lineDam will be sent to charset detector
// and a charset will be used to parse all those line and following lines in this mime obj.
static int
MimeInlineText_open_dam(char *line, int32_t length, MimeObject *obj)
{
MimeInlineText *text = (MimeInlineText *) obj;
const char* detectedCharset = nullptr;
nsresult res = NS_OK;
int status = 0;
int32_t i;
if (text->curDamOffset <= 0) {
//there is nothing in dam, use current line for detection
if (length > 0) {
res = MIME_detect_charset(line, length, &detectedCharset);
}
} else {
//we have stuff in dam, use the one
res = MIME_detect_charset(text->lineDamBuffer, text->curDamOffset, &detectedCharset);
}
//set the charset for this obj
if (NS_SUCCEEDED(res) && detectedCharset && *detectedCharset) {
PR_FREEIF(text->charset);
text->charset = strdup(detectedCharset);
//update MsgWindow charset if we are instructed to do so
if (text->needUpdateMsgWinCharset && *text->charset)
SetMailCharacterSetToMsgWindow(obj, text->charset);
}
//process dam and line using the charset
if (text->curDamOffset) {
for (i = 0; i < text->lastLineInDam-1; i++)
{
status = MimeInlineText_convert_and_parse_line(
text->lineDamPtrs[i],
text->lineDamPtrs[i+1] - text->lineDamPtrs[i],
obj );
}
status = MimeInlineText_convert_and_parse_line(
text->lineDamPtrs[i],
text->lineDamBuffer + text->curDamOffset - text->lineDamPtrs[i],
obj );
}
if (length)
status = MimeInlineText_convert_and_parse_line(line, length, obj);
PR_Free(text->lineDamPtrs);
PR_Free(text->lineDamBuffer);
text->lineDamPtrs = nullptr;
text->lineDamBuffer = nullptr;
text->inputAutodetect = false;
return status;
}
static int
MimeInlineText_rotate_convert_and_parse_line(char *line, int32_t length,
MimeObject *obj)
{
int status = 0;
MimeInlineTextClass *textc = (MimeInlineTextClass *) obj->clazz;
PR_ASSERT(!obj->closed_p);
if (obj->closed_p) return -1;
/* Rotate the line, if desired (this happens on the raw data, before any
charset conversion.) */
if (obj->options && obj->options->rot13_p)
{
status = textc->rot13_line(obj, line, length);
if (status < 0) return status;
}
// Now convert to the canonical charset, if desired.
//
bool doConvert = true;
// Don't convert vCard data
if ( ( (obj->content_type) && (!PL_strcasecmp(obj->content_type, TEXT_VCARD)) ) ||
(obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs)
|| obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)
doConvert = false;
// Only convert if the user prefs is false
if ( (obj->options && obj->options->charset_conversion_fn) &&
(!obj->options->force_user_charset) &&
(doConvert)
)
{
MimeInlineText *text = (MimeInlineText *) obj;
if (!text->initializeCharset)
{
MimeInlineText_initializeCharset(obj);
//update MsgWindow charset if we are instructed to do so
if (text->needUpdateMsgWinCharset && *text->charset)
SetMailCharacterSetToMsgWindow(obj, text->charset);
}
//if autodetect is on, push line to dam
if (text->inputAutodetect)
{
//see if we reach the lineDam buffer limit, if so, there is no need to keep buffering
if (text->lastLineInDam >= DAM_MAX_LINES ||
DAM_MAX_BUFFER_SIZE - text->curDamOffset <= length) {
//we let open dam process this line as well as thing that already in Dam
//In case there is nothing in dam because this line is too big, we need to
//perform autodetect on this line
status = MimeInlineText_open_dam(line, length, obj);
}
else {
//buffering current line
text->lineDamPtrs[text->lastLineInDam] = text->lineDamBuffer + text->curDamOffset;
memcpy(text->lineDamPtrs[text->lastLineInDam], line, length);
text->lastLineInDam++;
text->curDamOffset += length;
}
}
else
status = MimeInlineText_convert_and_parse_line(line, length, obj);
}
else
status = obj->clazz->parse_line(line, length, obj);
return status;
}

View file

@ -0,0 +1,82 @@
/* -*- 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/. */
#ifndef _MIMETEXT_H_
#define _MIMETEXT_H_
#include "mimeleaf.h"
/* The MimeInlineText class is the superclass of all handlers for the
MIME text/ content types (which convert various text formats to HTML,
in one form or another.)
It provides two services:
= if ROT13 decoding is desired, the text will be rotated before
the `parse_line' method it called;
= text will be converted from the message's charset to the "target"
charset before the `parse_line' method is called.
The contract with charset-conversion is that the converted data will
be such that one may interpret any octets (8-bit bytes) in the data
which are in the range of the ASCII characters (0-127) as ASCII
characters. It is explicitly legal, for example, to scan through
the string for "<" and replace it with "&lt;", and to search for things
that look like URLs and to wrap them with interesting HTML tags.
The charset to which we convert will probably be UTF-8 (an encoding of
the Unicode character set, with the feature that all octets with the
high bit off have the same interpretations as ASCII.)
#### NOTE: if it turns out that we use JIS (ISO-2022-JP) as the target
encoding, then this is not quite true; it is safe to search for the
low ASCII values (under hex 0x40, octal 0100, which is '@') but it
is NOT safe to search for values higher than that -- they may be
being used as the subsequent bytes in a multi-byte escape sequence.
It's a nice coincidence that HTML's critical characters ("<", ">",
and "&") have values under 0x40...
*/
typedef struct MimeInlineTextClass MimeInlineTextClass;
typedef struct MimeInlineText MimeInlineText;
struct MimeInlineTextClass {
MimeLeafClass leaf;
int (*rot13_line) (MimeObject *obj, char *line, int32_t length);
int (*convert_line_charset) (MimeObject *obj, char *line, int32_t length);
int (*initialize_charset) (MimeObject *obj);
};
extern MimeInlineTextClass mimeInlineTextClass;
#define DAM_MAX_BUFFER_SIZE 8*1024
#define DAM_MAX_LINES 1024
struct MimeInlineText {
MimeLeaf leaf; /* superclass variables */
char *charset; /* The charset from the content-type of this
object, or the caller-specified overrides
or defaults. */
bool charsetOverridable;
bool needUpdateMsgWinCharset;
char *cbuffer; /* Buffer used for charset conversion. */
int32_t cbuffer_size;
nsCOMPtr<nsIUnicodeDecoder> inputDecoder;
nsCOMPtr<nsIUnicodeEncoder> utf8Encoder;
bool inputAutodetect;
bool initializeCharset;
int32_t lastLineInDam;
int32_t curDamOffset;
char *lineDamBuffer;
char **lineDamPtrs;
};
#define MimeInlineTextClassInitializer(ITYPE,CSUPER) \
{ MimeLeafClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMETEXT_H_ */

View file

@ -0,0 +1,165 @@
/* -*- 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/. */
/* TODO:
- If you Save As File .html with this mode, you get a total mess.
- Print is untested (crashes in all modes).
*/
/* If you fix a bug here, check, if the same is also in mimethsa, because that
class is based on this class. */
#include "mimethpl.h"
#include "prlog.h"
#include "msgCore.h"
#include "mimemoz2.h"
#include "nsStringGlue.h"
#include "nsIDocumentEncoder.h" // for output flags
#define MIME_SUPERCLASS mimeInlineTextPlainClass
/* I should use the Flowed class as base (because our HTML->TXT converter
can generate flowed, and we tell it to) - this would get a bit nicer
rendering. However, that class is more picky about line endings
and I currently don't feel like splitting up the generated plaintext
into separate lines again. So, I just throw the whole message at once
at the TextPlain_parse_line function - it happens to work *g*. */
MimeDefClass(MimeInlineTextHTMLAsPlaintext, MimeInlineTextHTMLAsPlaintextClass,
mimeInlineTextHTMLAsPlaintextClass, &MIME_SUPERCLASS);
static int MimeInlineTextHTMLAsPlaintext_parse_line (const char *, int32_t,
MimeObject *);
static int MimeInlineTextHTMLAsPlaintext_parse_begin (MimeObject *obj);
static int MimeInlineTextHTMLAsPlaintext_parse_eof (MimeObject *, bool);
static void MimeInlineTextHTMLAsPlaintext_finalize (MimeObject *obj);
static int
MimeInlineTextHTMLAsPlaintextClassInitialize(MimeInlineTextHTMLAsPlaintextClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
NS_ASSERTION(!oclass->class_initialized, "problem with superclass");
oclass->parse_line = MimeInlineTextHTMLAsPlaintext_parse_line;
oclass->parse_begin = MimeInlineTextHTMLAsPlaintext_parse_begin;
oclass->parse_eof = MimeInlineTextHTMLAsPlaintext_parse_eof;
oclass->finalize = MimeInlineTextHTMLAsPlaintext_finalize;
return 0;
}
static int
MimeInlineTextHTMLAsPlaintext_parse_begin (MimeObject *obj)
{
MimeInlineTextHTMLAsPlaintext *textHTMLPlain =
(MimeInlineTextHTMLAsPlaintext *) obj;
textHTMLPlain->complete_buffer = new nsString();
// Let's just hope that libmime won't have the idea to call begin twice...
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
}
static int
MimeInlineTextHTMLAsPlaintext_parse_eof (MimeObject *obj, bool abort_p)
{
if (obj->closed_p)
return 0;
// This is a hack. We need to call parse_eof() of the super class to flush out any buffered data.
// We can't call it yet for our direct super class, because it would "close" the output
// (write tags such as </pre> and </div>). We'll do that after parsing the buffer.
int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->superclass->parse_eof(obj, abort_p);
if (status < 0)
return status;
MimeInlineTextHTMLAsPlaintext *textHTMLPlain =
(MimeInlineTextHTMLAsPlaintext *) obj;
if (!textHTMLPlain || !textHTMLPlain->complete_buffer)
return 0;
nsString& cb = *(textHTMLPlain->complete_buffer);
// could be empty, e.g., if part isn't actually being displayed
if (cb.Length())
{
nsString asPlaintext;
uint32_t flags = nsIDocumentEncoder::OutputFormatted
| nsIDocumentEncoder::OutputWrap
| nsIDocumentEncoder::OutputFormatFlowed
| nsIDocumentEncoder::OutputLFLineBreak
| nsIDocumentEncoder::OutputNoScriptContent
| nsIDocumentEncoder::OutputNoFramesContent
| nsIDocumentEncoder::OutputBodyOnly;
HTML2Plaintext(cb, asPlaintext, flags, 80);
NS_ConvertUTF16toUTF8 resultCStr(asPlaintext);
// TODO parse each line independently
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_line(
resultCStr.BeginWriting(),
resultCStr.Length(),
obj);
cb.Truncate();
}
if (status < 0)
return status;
// Second part of the flush hack. Pretend obj wasn't closed yet, so that our super class
// gets a chance to write the closing.
bool save_closed_p = obj->closed_p;
obj->closed_p = false;
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
// Restore closed_p.
obj->closed_p = save_closed_p;
return status;
}
void
MimeInlineTextHTMLAsPlaintext_finalize (MimeObject *obj)
{
MimeInlineTextHTMLAsPlaintext *textHTMLPlain =
(MimeInlineTextHTMLAsPlaintext *) obj;
if (textHTMLPlain && textHTMLPlain->complete_buffer)
{
// If there's content in the buffer, make sure that we output it.
// don't care about return codes
obj->clazz->parse_eof(obj, false);
delete textHTMLPlain->complete_buffer;
textHTMLPlain->complete_buffer = NULL;
/* It is important to zero the pointer, so we can reliably check for
the validity of it in the other functions. See above. */
}
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize (obj);
}
static int
MimeInlineTextHTMLAsPlaintext_parse_line (const char *line, int32_t length,
MimeObject *obj)
{
MimeInlineTextHTMLAsPlaintext *textHTMLPlain =
(MimeInlineTextHTMLAsPlaintext *) obj;
if (!textHTMLPlain || !(textHTMLPlain->complete_buffer))
{
#if DEBUG
printf("Can't output: %s\n", line);
#endif
return -1;
}
/*
To convert HTML->TXT syncronously, I need the full source at once,
not line by line (how do you convert "<li>foo\n" to plaintext?).
parse_decoded_buffer claims to give me that, but in fact also gives
me single lines.
It might be theoretically possible to drive this asyncronously, but
I don't know, which odd circumstances might arise and how libmime
will behave then. It's not worth the trouble for me to figure this all out.
*/
nsCString linestr(line, length);
NS_ConvertUTF8toUTF16 line_ucs2(linestr.get());
if (length && line_ucs2.IsEmpty())
CopyASCIItoUTF16 (linestr, line_ucs2);
(textHTMLPlain->complete_buffer)->Append(line_ucs2);
return 0;
}

View file

@ -0,0 +1,35 @@
/* -*- 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/. */
/* The MimeInlineTextHTMLAsPlaintext class converts HTML->TXT->HTML, i.e.
HTML to Plaintext and the result to HTML again.
This might sound crazy, maybe it is, but it is for the "View as Plaintext"
option, if the sender didn't supply a plaintext alternative (bah!).
*/
#ifndef _MIMETHPL_H_
#define _MIMETHPL_H_
#include "mimetpla.h"
#include "nsStringGlue.h"
typedef struct MimeInlineTextHTMLAsPlaintextClass MimeInlineTextHTMLAsPlaintextClass;
typedef struct MimeInlineTextHTMLAsPlaintext MimeInlineTextHTMLAsPlaintext;
struct MimeInlineTextHTMLAsPlaintextClass {
MimeInlineTextPlainClass plaintext;
};
extern MimeInlineTextHTMLAsPlaintextClass mimeInlineTextHTMLAsPlaintextClass;
struct MimeInlineTextHTMLAsPlaintext {
MimeInlineTextPlain plaintext;
nsString *complete_buffer; // Gecko parser expects wide strings
};
#define MimeInlineTextHTMLAsPlaintextClassInitializer(ITYPE,CSUPER) \
{ MimeInlineTextPlainClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMETHPL_H_ */

View file

@ -0,0 +1,143 @@
/* -*- 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/. */
/* Most of this code is copied from mimethpl; see there for source comments.
If you find a bug here, check that class, too.
*/
/* The MimeInlineTextHTMLSanitized class cleans up HTML
This removes offending HTML features that have no business in mail.
It is a low-level stop gap for many classes of attacks,
and intended for security conscious users.
Paranoia is a feature here, and has served very well in practice.
It has already prevented countless serious exploits.
It pushes the HTML that we get from the sender of the message
through a sanitizer (nsTreeSanitizer), which lets only allowed tags through.
With the appropriate configuration, this protects from most of the
security and visual-formatting problems that otherwise usually come with HTML
(and which partly gave HTML in email the bad reputation that it has).
However, due to the parsing and serializing (and later parsing again)
required, there is an inherent, significant performance hit, when doing the
santinizing here at the MIME / HTML source level. But users of this class
will most likely find it worth the cost.
*/
#include "mimethsa.h"
#include "prmem.h"
#include "prlog.h"
#include "msgCore.h"
#include "mimemoz2.h"
#include "nsIPrefBranch.h"
#include "mimethtm.h"
#define MIME_SUPERCLASS mimeInlineTextHTMLClass
MimeDefClass(MimeInlineTextHTMLSanitized, MimeInlineTextHTMLSanitizedClass,
mimeInlineTextHTMLSanitizedClass, &MIME_SUPERCLASS);
static int MimeInlineTextHTMLSanitized_parse_line(const char *, int32_t,
MimeObject *);
static int MimeInlineTextHTMLSanitized_parse_begin(MimeObject *obj);
static int MimeInlineTextHTMLSanitized_parse_eof(MimeObject *, bool);
static void MimeInlineTextHTMLSanitized_finalize(MimeObject *obj);
static int
MimeInlineTextHTMLSanitizedClassInitialize(MimeInlineTextHTMLSanitizedClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *)clazz;
NS_ASSERTION(!oclass->class_initialized, "problem with superclass");
oclass->parse_line = MimeInlineTextHTMLSanitized_parse_line;
oclass->parse_begin = MimeInlineTextHTMLSanitized_parse_begin;
oclass->parse_eof = MimeInlineTextHTMLSanitized_parse_eof;
oclass->finalize = MimeInlineTextHTMLSanitized_finalize;
return 0;
}
static int
MimeInlineTextHTMLSanitized_parse_begin(MimeObject *obj)
{
MimeInlineTextHTMLSanitized *me = (MimeInlineTextHTMLSanitized *)obj;
me->complete_buffer = new nsString();
int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
if (status < 0)
return status;
return 0;
}
static int
MimeInlineTextHTMLSanitized_parse_eof(MimeObject *obj, bool abort_p)
{
if (obj->closed_p)
return 0;
int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0)
return status;
MimeInlineTextHTMLSanitized *me = (MimeInlineTextHTMLSanitized *)obj;
// We have to cache all lines and parse the whole document at once.
// There's a useful sounding function parseFromStream(), but it only allows XML
// mimetypes, not HTML. Methinks that's because the HTML soup parser
// needs the entire doc to make sense of the gibberish that people write.
if (!me || !me->complete_buffer)
return 0;
nsString& cb = *(me->complete_buffer);
if (cb.IsEmpty())
return 0;
nsString sanitized;
// Sanitize.
HTMLSanitize(cb, sanitized);
// Write it out.
NS_ConvertUTF16toUTF8 resultCStr(sanitized);
MimeInlineTextHTML_insert_lang_div(obj, resultCStr);
// Call to MimeInlineTextHTML_remove_plaintext_tag() not needed since
// sanitization already removes that tag.
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_line(
resultCStr.BeginWriting(),
resultCStr.Length(),
obj);
cb.Truncate();
return status;
}
void
MimeInlineTextHTMLSanitized_finalize(MimeObject *obj)
{
MimeInlineTextHTMLSanitized *me = (MimeInlineTextHTMLSanitized *)obj;
if (me && me->complete_buffer)
{
obj->clazz->parse_eof(obj, false);
delete me->complete_buffer;
me->complete_buffer = NULL;
}
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj);
}
static int
MimeInlineTextHTMLSanitized_parse_line(const char *line, int32_t length,
MimeObject *obj)
{
MimeInlineTextHTMLSanitized *me = (MimeInlineTextHTMLSanitized *)obj;
if (!me || !(me->complete_buffer))
return -1;
nsCString linestr(line, length);
NS_ConvertUTF8toUTF16 line_ucs2(linestr.get());
if (length && line_ucs2.IsEmpty())
CopyASCIItoUTF16(linestr, line_ucs2);
(me->complete_buffer)->Append(line_ucs2);
return 0;
}

View file

@ -0,0 +1,28 @@
/* -*- 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/. */
#ifndef _MIMETHSA_H_
#define _MIMETHSA_H_
#include "mimethtm.h"
typedef struct MimeInlineTextHTMLSanitizedClass MimeInlineTextHTMLSanitizedClass;
typedef struct MimeInlineTextHTMLSanitized MimeInlineTextHTMLSanitized;
struct MimeInlineTextHTMLSanitizedClass {
MimeInlineTextHTMLClass html;
};
extern MimeInlineTextHTMLSanitizedClass mimeInlineTextHTMLSanitizedClass;
struct MimeInlineTextHTMLSanitized {
MimeInlineTextHTML html;
nsString *complete_buffer; // Gecko parser expects wide strings
};
#define MimeInlineTextHTMLSanitizedClassInitializer(ITYPE,CSUPER) \
{ MimeInlineTextHTMLClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMETHPL_H_ */

View file

@ -0,0 +1,254 @@
/* -*- 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 "mimethtm.h"
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "prprf.h"
#include "msgCore.h"
#include "nsMimeStringResources.h"
#include "mimemoz2.h"
#include <ctype.h>
#define MIME_SUPERCLASS mimeInlineTextClass
MimeDefClass(MimeInlineTextHTML, MimeInlineTextHTMLClass,
mimeInlineTextHTMLClass, &MIME_SUPERCLASS);
static int MimeInlineTextHTML_parse_line (const char *, int32_t, MimeObject *);
static int MimeInlineTextHTML_parse_eof (MimeObject *, bool);
static int MimeInlineTextHTML_parse_begin (MimeObject *obj);
static int
MimeInlineTextHTMLClassInitialize(MimeInlineTextHTMLClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
oclass->parse_begin = MimeInlineTextHTML_parse_begin;
oclass->parse_line = MimeInlineTextHTML_parse_line;
oclass->parse_eof = MimeInlineTextHTML_parse_eof;
return 0;
}
static int
MimeInlineTextHTML_parse_begin (MimeObject *obj)
{
int status = ((MimeObjectClass*)&mimeLeafClass)->parse_begin(obj);
if (status < 0) return status;
if (!obj->output_p) return 0;
status = MimeObject_write_separator(obj);
if (status < 0) return status;
MimeInlineTextHTML *textHTML = (MimeInlineTextHTML *) obj;
textHTML->charset = nullptr;
/* If this HTML part has a Content-Base header, and if we're displaying
to the screen (that is, not writing this part "raw") then translate
that Content-Base header into a <BASE> tag in the HTML.
*/
if (obj->options &&
obj->options->write_html_p &&
obj->options->output_fn)
{
char *base_hdr = MimeHeaders_get (obj->headers, HEADER_CONTENT_BASE,
false, false);
/* rhp - for MHTML Spec changes!!! */
if (!base_hdr)
{
base_hdr = MimeHeaders_get (obj->headers, HEADER_CONTENT_LOCATION, false, false);
}
/* rhp - for MHTML Spec changes!!! */
if (base_hdr)
{
uint32_t buflen = strlen(base_hdr) + 20;
char *buf = (char *) PR_MALLOC(buflen);
const char *in;
char *out;
if (!buf)
return MIME_OUT_OF_MEMORY;
/* The value of the Content-Base header is a number of "words".
Whitespace in this header is not significant -- it is assumed
that any real whitespace in the URL has already been encoded,
and whitespace has been inserted to allow the lines in the
mail header to be wrapped reasonably. Creators are supposed
to insert whitespace every 40 characters or less.
*/
PL_strncpyz(buf, "<BASE HREF=\"", buflen);
out = buf + strlen(buf);
for (in = base_hdr; *in; in++)
/* ignore whitespace and quotes */
if (!IS_SPACE(*in) && *in != '"')
*out++ = *in;
/* Close the tag and argument. */
*out++ = '"';
*out++ = '>';
*out++ = 0;
PR_Free(base_hdr);
status = MimeObject_write(obj, buf, strlen(buf), false);
PR_Free(buf);
if (status < 0) return status;
}
}
return 0;
}
static int
MimeInlineTextHTML_parse_line (const char *line, int32_t length, MimeObject *obj)
{
MimeInlineTextHTML *textHTML = (MimeInlineTextHTML *) obj;
if (!obj->output_p)
return 0;
if (!obj->options || !obj->options->output_fn)
return 0;
if (!textHTML->charset)
{
char * cp;
// First, try to detect a charset via a META tag!
if ((cp = PL_strncasestr(line, "META", length)) &&
(cp = PL_strncasestr(cp, "HTTP-EQUIV=", length - (int)(cp - line))) &&
(cp = PL_strncasestr(cp, "CONTENT=", length - (int)(cp - line))) &&
(cp = PL_strncasestr(cp, "CHARSET=", length - (int)(cp - line)))
)
{
char* cp1 = cp + 8; //8 for the length of "CHARSET="
char* cp2 = PL_strnpbrk(cp1, " \"\'", length - (int)(cp1 - line));
if (cp2)
{
char* charset = PL_strndup(cp1, (int)(cp2 - cp1));
// Fix bug 101434, in this case since this parsing is a char*
// operation, a real UTF-16 or UTF-32 document won't be parse
// correctly, if it got parse, it cannot be UTF-16 nor UTF-32
// there fore, we ignore them if somehow we got that value
// 6 == strlen("UTF-16") or strlen("UTF-32"), this will cover
// UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, UTF-32LE
if ((charset != nullptr) &&
PL_strncasecmp(charset, "UTF-16", 6) &&
PL_strncasecmp(charset, "UTF-32", 6))
{
textHTML->charset = charset;
// write out the data without the charset part...
if (textHTML->charset)
{
int err = MimeObject_write(obj, line, cp - line, true);
if (err == 0)
err = MimeObject_write(obj, cp2, length - (int)(cp2 - line), true);
return err;
}
}
PR_FREEIF(charset);
}
}
}
// Now, just write out the data...
return MimeObject_write(obj, line, length, true);
}
static int
MimeInlineTextHTML_parse_eof (MimeObject *obj, bool abort_p)
{
int status;
MimeInlineTextHTML *textHTML = (MimeInlineTextHTML *) obj;
if (obj->closed_p) return 0;
PR_FREEIF(textHTML->charset);
/* Run parent method first, to flush out any buffered data. */
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0) return status;
return 0;
}
/*
* The following function adds <div class="moz-text-html" lang="..."> or
* <div class="moz-text-html"> as the first tag following the <body> tag in the
* serialised HTML of a message. This becomes a no-op if no <body> tag is found.
*/
void
MimeInlineTextHTML_insert_lang_div(MimeObject *obj, nsCString &message)
{
if (obj->options->format_out != nsMimeOutput::nsMimeMessageBodyDisplay &&
obj->options->format_out != nsMimeOutput::nsMimeMessagePrintOutput)
return;
// Make sure we have a <body> before we start.
int32_t index = message.Find("<body", /* ignoreCase = */ true);
if (index == kNotFound)
return;
index = message.FindChar('>', index) + 1;
// Insert <div class="moz-text-html" lang="..."> for the following two purposes:
// 1) Users can configure their HTML display via CSS for .moz-text-html.
// 2) The language group in the 'lang' attribure is used by Gecko to determine
// which font to use.
int32_t fontSize; // default font size
int32_t fontSizePercentage; // size percentage
nsAutoCString fontLang; // langgroup of the font.
if (NS_SUCCEEDED(GetMailNewsFont(obj, false, &fontSize, &fontSizePercentage, fontLang)))
{
message.Insert(NS_LITERAL_CSTRING("<div class=\"moz-text-html\" lang=\"") +
fontLang +
NS_LITERAL_CSTRING("\">"),
index);
}
else
{
message.Insert(NS_LITERAL_CSTRING("<div class=\"moz-text-html\">"),
index);
}
index = message.RFind("</body>", /* ignoreCase = */ true);
if (index != kNotFound)
message.Insert(NS_LITERAL_CSTRING("</div>"), index);
}
/*
* The following function replaces <plaintext> tags with <x-plaintext>.
* <plaintext> is a funny beast: It leads to everything following it
* being displayed verbatim, even a </plaintext> tag is ignored.
*/
void
MimeInlineTextHTML_remove_plaintext_tag(MimeObject *obj, nsCString &message)
{
if (obj->options->format_out != nsMimeOutput::nsMimeMessageBodyDisplay &&
obj->options->format_out != nsMimeOutput::nsMimeMessagePrintOutput)
return;
// Replace all <plaintext> and </plaintext> tags.
int32_t index = 0;
bool replaced = false;
while ((index = message.Find("<plaintext", /* ignoreCase = */ true, index)) != kNotFound) {
message.Insert("x-", index+1);
index += 12;
replaced = true;
}
if (replaced) {
index = 0;
while ((index = message.Find("</plaintext", /* ignoreCase = */ true, index)) != kNotFound) {
message.Insert("x-", index+2);
index += 13;
}
}
}

View file

@ -0,0 +1,35 @@
/* -*- 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/. */
#ifndef _MIMETHTM_H_
#define _MIMETHTM_H_
#include "mimetext.h"
/* The MimeInlineTextHTML class implements the text/html MIME content type.
*/
typedef struct MimeInlineTextHTMLClass MimeInlineTextHTMLClass;
typedef struct MimeInlineTextHTML MimeInlineTextHTML;
struct MimeInlineTextHTMLClass {
MimeInlineTextClass text;
};
extern MimeInlineTextHTMLClass mimeInlineTextHTMLClass;
struct MimeInlineTextHTML {
MimeInlineText text;
char *charset; /* If we sniffed a charset, do some converting! */
};
#define MimeInlineTextHTMLClassInitializer(ITYPE,CSUPER) \
{ MimeInlineTextClassInitializer(ITYPE,CSUPER) }
void
MimeInlineTextHTML_insert_lang_div(MimeObject *obj, nsCString &message);
void
MimeInlineTextHTML_remove_plaintext_tag(MimeObject *obj, nsCString &message);
#endif /* _MIMETHTM_H_ */

View file

@ -0,0 +1,630 @@
/* -*- 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 "mimetpfl.h"
#include "mimebuf.h"
#include "prmem.h"
#include "plstr.h"
#include "mozITXTToHTMLConv.h"
#include "nsStringGlue.h"
#include "nsMimeStringResources.h"
#include "nsIPrefBranch.h"
#include "nsIServiceManager.h"
#include "mimemoz2.h"
#include "prprf.h"
#include "nsMsgI18N.h"
static const uint32_t kSpacesForATab = 4; // Must be at least 1.
#define MIME_SUPERCLASS mimeInlineTextClass
MimeDefClass(MimeInlineTextPlainFlowed, MimeInlineTextPlainFlowedClass,
mimeInlineTextPlainFlowedClass, &MIME_SUPERCLASS);
static int MimeInlineTextPlainFlowed_parse_begin (MimeObject *);
static int MimeInlineTextPlainFlowed_parse_line (const char *, int32_t, MimeObject *);
static int MimeInlineTextPlainFlowed_parse_eof (MimeObject *, bool);
static MimeInlineTextPlainFlowedExData *MimeInlineTextPlainFlowedExDataList = nullptr;
// From mimetpla.cpp
extern "C" void MimeTextBuildPrefixCSS(
int32_t quotedSizeSetting, // mail.quoted_size
int32_t quotedStyleSetting, // mail.quoted_style
char *citationColor, // mail.citation_color
nsACString &style);
// Definition below
static
nsresult Line_convert_whitespace(const nsString& a_line,
const bool a_convert_all_whitespace,
nsString& a_out_line);
static int
MimeInlineTextPlainFlowedClassInitialize(MimeInlineTextPlainFlowedClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
NS_ASSERTION(!oclass->class_initialized, "class not initialized");
oclass->parse_begin = MimeInlineTextPlainFlowed_parse_begin;
oclass->parse_line = MimeInlineTextPlainFlowed_parse_line;
oclass->parse_eof = MimeInlineTextPlainFlowed_parse_eof;
return 0;
}
static int
MimeInlineTextPlainFlowed_parse_begin (MimeObject *obj)
{
int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
if (status < 0) return status;
status = MimeObject_write(obj, "", 0, true); /* force out any separators... */
if(status<0) return status;
bool quoting = ( obj->options
&& ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
) ); // The output will be inserted in the composer as quotation
bool plainHTML = quoting || (obj->options &&
obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs);
// Just good(tm) HTML. No reliance on CSS.
// Setup the data structure that is connected to the actual document
// Saved in a linked list in case this is called with several documents
// at the same time.
/* This memory is freed when parse_eof is called. So it better be! */
struct MimeInlineTextPlainFlowedExData *exdata =
(MimeInlineTextPlainFlowedExData *)PR_MALLOC(sizeof(struct MimeInlineTextPlainFlowedExData));
if(!exdata) return MIME_OUT_OF_MEMORY;
MimeInlineTextPlainFlowed *text = (MimeInlineTextPlainFlowed *) obj;
// Link it up.
exdata->next = MimeInlineTextPlainFlowedExDataList;
MimeInlineTextPlainFlowedExDataList = exdata;
// Initialize data
exdata->ownerobj = obj;
exdata->inflow = false;
exdata->quotelevel = 0;
exdata->isSig = false;
// check for DelSp=yes (RFC 3676)
char *content_type_row =
(obj->headers
? MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE, false, false)
: 0);
char *content_type_delsp =
(content_type_row
? MimeHeaders_get_parameter(content_type_row, "delsp", NULL,NULL)
: 0);
((MimeInlineTextPlainFlowed *)obj)->delSp = content_type_delsp && !PL_strcasecmp(content_type_delsp, "yes");
PR_Free(content_type_delsp);
PR_Free(content_type_row);
// Get Prefs for viewing
exdata->fixedwidthfont = false;
// Quotes
text->mQuotedSizeSetting = 0; // mail.quoted_size
text->mQuotedStyleSetting = 0; // mail.quoted_style
text->mCitationColor = nullptr; // mail.citation_color
text->mStripSig = true; // mail.strip_sig_on_reply
nsIPrefBranch *prefBranch = GetPrefBranch(obj->options);
if (prefBranch)
{
prefBranch->GetIntPref("mail.quoted_size", &(text->mQuotedSizeSetting));
prefBranch->GetIntPref("mail.quoted_style", &(text->mQuotedStyleSetting));
prefBranch->GetCharPref("mail.citation_color", &(text->mCitationColor));
prefBranch->GetBoolPref("mail.strip_sig_on_reply", &(text->mStripSig));
mozilla::DebugOnly<nsresult> rv =
prefBranch->GetBoolPref("mail.fixed_width_messages", &(exdata->fixedwidthfont));
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get pref");
// Check at least the success of one
}
// Get font
// only used for viewing (!plainHTML)
nsAutoCString fontstyle;
nsAutoCString fontLang; // langgroup of the font
// generic font-family name ( -moz-fixed for fixed font and NULL for
// variable font ) is sufficient now that bug 105199 has been fixed.
if (exdata->fixedwidthfont)
fontstyle = "font-family: -moz-fixed";
if (nsMimeOutput::nsMimeMessageBodyDisplay == obj->options->format_out ||
nsMimeOutput::nsMimeMessagePrintOutput == obj->options->format_out)
{
int32_t fontSize; // default font size
int32_t fontSizePercentage; // size percentage
nsresult rv = GetMailNewsFont(obj, exdata->fixedwidthfont,
&fontSize, &fontSizePercentage, fontLang);
if (NS_SUCCEEDED(rv))
{
if ( ! fontstyle.IsEmpty() ) {
fontstyle += "; ";
}
fontstyle += "font-size: ";
fontstyle.AppendInt(fontSize);
fontstyle += "px;";
}
}
// Opening <div>.
if (!quoting)
/* 4.x' editor can't break <div>s (e.g. to interleave comments).
We'll add the class to the <blockquote type=cite> later. */
{
nsAutoCString openingDiv("<div class=\"moz-text-flowed\"");
// We currently have to add formatting here. :-(
if (!plainHTML && !fontstyle.IsEmpty())
{
openingDiv += " style=\"";
openingDiv += fontstyle;
openingDiv += '"';
}
if (!plainHTML && !fontLang.IsEmpty())
{
openingDiv += " lang=\"";
openingDiv += fontLang;
openingDiv += '\"';
}
openingDiv += ">";
status = MimeObject_write(obj, openingDiv.get(), openingDiv.Length(), false);
if (status < 0) return status;
}
return 0;
}
static int
MimeInlineTextPlainFlowed_parse_eof (MimeObject *obj, bool abort_p)
{
int status = 0;
struct MimeInlineTextPlainFlowedExData *exdata = nullptr;
bool quoting = ( obj->options
&& ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
) ); // see above
// Has this method already been called for this object?
// In that case return.
if (obj->closed_p) return 0;
/* Run parent method first, to flush out any buffered data. */
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0) goto EarlyOut;
// Look up and unlink "our" extended data structure
// We do it in the beginning so that if an error occur, we can
// just free |exdata|.
struct MimeInlineTextPlainFlowedExData **prevexdata;
prevexdata = &MimeInlineTextPlainFlowedExDataList;
while ((exdata = *prevexdata) != nullptr) {
if (exdata->ownerobj == obj) {
// Fill hole
*prevexdata = exdata->next;
break;
}
prevexdata = &exdata->next;
}
NS_ASSERTION (exdata, "The extra data has disappeared!");
if (!obj->output_p) {
status = 0;
goto EarlyOut;
}
for(; exdata->quotelevel > 0; exdata->quotelevel--) {
status = MimeObject_write(obj, "</blockquote>", 13, false);
if(status<0) goto EarlyOut;
}
if (exdata->isSig && !quoting) {
status = MimeObject_write(obj, "</div>", 6, false); // .moz-txt-sig
if (status<0) goto EarlyOut;
}
if (!quoting) // HACK (see above)
{
status = MimeObject_write(obj, "</div>", 6, false); // .moz-text-flowed
if (status<0) goto EarlyOut;
}
status = 0;
EarlyOut:
PR_Free(exdata);
// Free mCitationColor
MimeInlineTextPlainFlowed *text = (MimeInlineTextPlainFlowed *) obj;
PR_FREEIF(text->mCitationColor);
text->mCitationColor = nullptr;
return status;
}
static int
MimeInlineTextPlainFlowed_parse_line (const char *aLine, int32_t length, MimeObject *obj)
{
int status;
bool quoting = ( obj->options
&& ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
) ); // see above
bool plainHTML = quoting || (obj->options &&
obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs);
// see above
struct MimeInlineTextPlainFlowedExData *exdata;
exdata = MimeInlineTextPlainFlowedExDataList;
while(exdata && (exdata->ownerobj != obj)) {
exdata = exdata->next;
}
NS_ASSERTION(exdata, "The extra data has disappeared!");
NS_ASSERTION(length > 0, "zero length");
if (length <= 0) return 0;
uint32_t linequotelevel = 0;
nsAutoCString real_line(aLine, length);
char *line = real_line.BeginWriting();
const char *linep = real_line.BeginReading();
// Space stuffed?
if(' ' == *linep) {
linep++;
} else {
// count '>':s before the first non-'>'
while('>' == *linep) {
linep++;
linequotelevel++;
}
// Space stuffed?
if(' ' == *linep) {
linep++;
}
}
// Look if the last character (after stripping ending end
// of lines and quoting stuff) is a SPACE. If it is, we are looking at a
// flowed line. Normally we assume that the last two chars
// are CR and LF as said in RFC822, but that doesn't seem to
// be the case always.
bool flowed = false;
bool sigSeparator = false;
int32_t index = length-1;
while(index >= 0 && ('\r' == line[index] || '\n' == line[index])) {
index--;
}
if (index > linep - line && ' ' == line[index])
/* Ignore space stuffing, i.e. lines with just
(quote marks and) a space count as empty */
{
flowed = true;
sigSeparator = (index - (linep - line) + 1 == 3) && !strncmp(linep, "-- ", 3);
if (((MimeInlineTextPlainFlowed *) obj)->delSp && !sigSeparator)
/* If line is flowed and DelSp=yes, logically
delete trailing space. Line consisting of
dash dash space ("-- "), commonly used as
signature separator, gets special handling
(RFC 3676) */
{
length = index;
line[index] = '\0';
}
}
if (obj->options &&
obj->options->decompose_file_p &&
obj->options->decompose_file_output_fn)
{
return obj->options->decompose_file_output_fn(line,
length,
obj->options->stream_closure);
}
mozITXTToHTMLConv *conv = GetTextConverter(obj->options);
bool skipConversion = !conv ||
(obj->options && obj->options->force_user_charset);
nsAutoString lineSource;
nsString lineResult;
char *mailCharset = NULL;
nsresult rv;
if (!skipConversion)
{
// Convert only if the source string is not empty
if (length - (linep - line) > 0)
{
uint32_t whattodo = obj->options->whattodo;
if (plainHTML)
{
if (quoting)
whattodo = 0;
else
whattodo = whattodo & ~mozITXTToHTMLConv::kGlyphSubstitution;
/* Do recognition for the case, the result is viewed in
Mozilla, but not GlyphSubstitution, because other UAs
might not be able to display the glyphs. */
}
const nsDependentCSubstring& inputStr = Substring(linep, linep + (length - (linep - line)));
// For 'SaveAs', |line| is in |mailCharset|.
// convert |line| to UTF-16 before 'html'izing (calling ScanTXT())
if (obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs)
{
// Get the mail charset of this message.
MimeInlineText *inlinetext = (MimeInlineText *) obj;
if (!inlinetext->initializeCharset)
((MimeInlineTextClass*)&mimeInlineTextClass)->initialize_charset(obj);
mailCharset = inlinetext->charset;
if (mailCharset && *mailCharset) {
rv = nsMsgI18NConvertToUnicode(mailCharset, PromiseFlatCString(inputStr), lineSource);
NS_ENSURE_SUCCESS(rv, -1);
}
else // this probably never happens...
CopyUTF8toUTF16(inputStr, lineSource);
}
else // line is in UTF-8
CopyUTF8toUTF16(inputStr, lineSource);
// This is the main TXT to HTML conversion:
// escaping (very important), eventually recognizing etc.
rv = conv->ScanTXT(lineSource.get(), whattodo, getter_Copies(lineResult));
NS_ENSURE_SUCCESS(rv, -1);
}
}
else
{
CopyUTF8toUTF16(nsDependentCString(line, length), lineResult);
status = 0;
}
nsAutoCString preface;
/* Correct number of blockquotes */
int32_t quoteleveldiff=linequotelevel - exdata->quotelevel;
if((quoteleveldiff != 0) && flowed && exdata->inflow) {
// From RFC 2646 4.5
// The receiver SHOULD handle this error by using the 'quote-depth-wins' rule,
// which is to ignore the flowed indicator and treat the line as fixed. That
// is, the change in quote depth ends the paragraph.
// We get that behaviour by just going on.
}
// Cast so we have access to the prefs we need.
MimeInlineTextPlainFlowed *tObj = (MimeInlineTextPlainFlowed *) obj;
while(quoteleveldiff>0) {
quoteleveldiff--;
preface += "<blockquote type=cite";
nsAutoCString style;
MimeTextBuildPrefixCSS(tObj->mQuotedSizeSetting, tObj->mQuotedStyleSetting,
tObj->mCitationColor, style);
if (!plainHTML && !style.IsEmpty())
{
preface += " style=\"";
preface += style;
preface += '"';
}
preface += '>';
}
while(quoteleveldiff<0) {
quoteleveldiff++;
preface += "</blockquote>";
}
exdata->quotelevel = linequotelevel;
nsAutoString lineResult2;
if(flowed) {
// Check RFC 2646 "4.3. Usenet Signature Convention": "-- "+CRLF is
// not a flowed line
if (sigSeparator)
{
if (linequotelevel > 0 || exdata->isSig)
{
preface += "--&nbsp;<br>";
} else {
exdata->isSig = true;
preface += "<div class=\"moz-txt-sig\"><span class=\"moz-txt-tag\">"
"--&nbsp;<br></span>";
}
} else {
Line_convert_whitespace(lineResult, false /* Allow wraps */,
lineResult2);
}
exdata->inflow=true;
} else {
// Fixed paragraph.
Line_convert_whitespace(lineResult,
!plainHTML && !obj->options->wrap_long_lines_p
/* If wrap, convert all spaces but the last in
a row into nbsp, otherwise all. */,
lineResult2);
lineResult2.AppendLiteral("<br>");
exdata->inflow = false;
} // End Fixed line
if (!(exdata->isSig && quoting && tObj->mStripSig))
{
status = MimeObject_write(obj, preface.get(), preface.Length(), true);
if (status < 0) return status;
nsAutoCString outString;
if (obj->options->format_out != nsMimeOutput::nsMimeMessageSaveAs ||
!mailCharset || !*mailCharset)
CopyUTF16toUTF8(lineResult2, outString);
else
{ // convert back to mailCharset before writing.
rv = nsMsgI18NConvertFromUnicode(mailCharset, lineResult2, outString);
NS_ENSURE_SUCCESS(rv, -1);
}
status = MimeObject_write(obj, outString.get(), outString.Length(), true);
return status;
}
return 0;
}
/**
* Maintains a small state machine with three states. "Not in tag",
* "In tag, but not in quote" and "In quote inside a tag". It also
* remembers what character started the quote (" or '). The state
* variables are kept outside this function and are included as
* parameters.
*
* @param in/out a_in_tag, if we are in a tag right now.
* @param in/out a_in_quote_in_tag, if we are in a quote inside a tag.
* @param in/out a_quote_char, the kind of quote (" or ').
* @param in a_current_char, the next char. It decides which state
* will be next.
*/
static void Update_in_tag_info(bool *a_in_tag, /* IN/OUT */
bool *a_in_quote_in_tag, /* IN/OUT */
char16_t *a_quote_char, /* IN/OUT (pointer to single char) */
char16_t a_current_char) /* IN */
{
if(*a_in_tag) {
// Keep us informed of what's quoted so that we
// don't end the tag too soon. For instance in
// <font face="weird>font<name">
if(*a_in_quote_in_tag) {
// We are in a quote. A quote is ended by the same
// character that started it ('...' or "...")
if(*a_quote_char == a_current_char) {
*a_in_quote_in_tag = false;
}
} else {
// We are not currently in a quote, but we may enter
// one right this minute.
switch(a_current_char) {
case '"':
case '\'':
*a_in_quote_in_tag = true;
*a_quote_char = a_current_char;
break;
case '>':
// Tag is ended
*a_in_tag = false;
break;
default:
// Do nothing
;
}
}
return;
}
// Not in a tag.
// Check if we are entering a tag by looking for '<'.
// All normal occurrences of '<' should have been replaced
// by &lt;
if ('<' == a_current_char) {
*a_in_tag = true;
*a_in_quote_in_tag = false;
}
}
/**
* Converts whitespace to |&nbsp;|, if appropriate.
*
* @param in a_current_char, the char to convert.
* @param in a_next_char, the char after the char to convert.
* @param in a_convert_all_whitespace, if also the last whitespace
* in a sequence should be
* converted.
* @param out a_out_string, result will be appended.
*/
static void Convert_whitespace(const char16_t a_current_char,
const char16_t a_next_char,
const bool a_convert_all_whitespace,
nsString& a_out_string)
{
NS_ASSERTION('\t' == a_current_char || ' ' == a_current_char,
"Convert_whitespace got something else than a whitespace!");
uint32_t number_of_nbsp = 0;
uint32_t number_of_space = 1; // Assume we're going to output one space.
/* Output the spaces for a tab. All but the last are made into &nbsp;.
The last is treated like a normal space.
*/
if('\t' == a_current_char) {
number_of_nbsp = kSpacesForATab - 1;
}
if(' ' == a_next_char || '\t' == a_next_char || a_convert_all_whitespace) {
number_of_nbsp += number_of_space;
number_of_space = 0;
}
while(number_of_nbsp--) {
a_out_string.AppendLiteral("&nbsp;");
}
while(number_of_space--) {
// a_out_string += ' '; gives error
a_out_string.AppendLiteral(" ");
}
return;
}
/**
* Passes over the line and converts whitespace to |&nbsp;|, if appropriate
*
* @param in a_convert_all_whitespace, if also the last whitespace
* in a sequence should be
* converted.
* @param out a_out_string, result will be appended.
*/
static
nsresult Line_convert_whitespace(const nsString& a_line,
const bool a_convert_all_whitespace,
nsString& a_out_line)
{
bool in_tag = false;
bool in_quote_in_tag = false;
char16_t quote_char;
for (uint32_t i = 0; a_line.Length() > i; i++)
{
const char16_t ic = a_line[i]; // Cache
Update_in_tag_info(&in_tag, &in_quote_in_tag, &quote_char, ic);
// We don't touch anything inside a tag.
if (!in_tag) {
if (ic == ' ' || ic == '\t') {
// Convert the whitespace to something appropriate
Convert_whitespace(ic, a_line.Length() > i + 1 ? a_line[i + 1] : '\0',
a_convert_all_whitespace ||
!i, // First char on line
a_out_line);
} else if (ic == '\r') {
// strip CRs
} else {
a_out_line += ic;
}
} else {
// In tag. Don't change anything
a_out_line += ic;
}
}
return NS_OK;
}

View file

@ -0,0 +1,52 @@
/* -*- 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/. */
#ifndef _MIMETPFL_H_
#define _MIMETPFL_H_
#include "mimetext.h"
/* The MimeInlineTextPlainFlowed class implements the
text/plain MIME content type for the special case of a supplied
format=flowed. See
ftp://ftp.ietf.org/internet-drafts/draft-gellens-format-06.txt for
more information.
*/
typedef struct MimeInlineTextPlainFlowedClass MimeInlineTextPlainFlowedClass;
typedef struct MimeInlineTextPlainFlowed MimeInlineTextPlainFlowed;
struct MimeInlineTextPlainFlowedClass {
MimeInlineTextClass text;
};
extern MimeInlineTextPlainFlowedClass mimeInlineTextPlainFlowedClass;
struct MimeInlineTextPlainFlowed {
MimeInlineText text;
bool delSp; // DelSp=yes (RFC 3676)
int32_t mQuotedSizeSetting; // mail.quoted_size
int32_t mQuotedStyleSetting; // mail.quoted_style
char *mCitationColor; // mail.citation_color
bool mStripSig; // mail.strip_sig_on_reply
};
/*
* Made to contain information to be kept during the whole message parsing.
*/
struct MimeInlineTextPlainFlowedExData {
struct MimeObject *ownerobj; /* The owner of this struct */
bool inflow; /* If we currently are in flow */
bool fixedwidthfont; /* If we output text for fixed width font */
uint32_t quotelevel; /* How deep is your love, uhr, quotelevel I meen. */
bool isSig; // we're currently in a signature
struct MimeInlineTextPlainFlowedExData *next;
};
#define MimeInlineTextPlainFlowedClassInitializer(ITYPE,CSUPER) \
{ MimeInlineTextClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMETPFL_H_ */

View file

@ -0,0 +1,451 @@
/* -*- 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 "mimetpla.h"
#include "mimebuf.h"
#include "prmem.h"
#include "plstr.h"
#include "mozITXTToHTMLConv.h"
#include "nsCOMPtr.h"
#include "nsIComponentManager.h"
#include "nsStringGlue.h"
#include "nsMimeStringResources.h"
#include "mimemoz2.h"
#include "nsIServiceManager.h"
#include "nsIPrefBranch.h"
#include "prprf.h"
#include "nsMsgI18N.h"
#define MIME_SUPERCLASS mimeInlineTextClass
MimeDefClass(MimeInlineTextPlain, MimeInlineTextPlainClass,
mimeInlineTextPlainClass, &MIME_SUPERCLASS);
static int MimeInlineTextPlain_parse_begin (MimeObject *);
static int MimeInlineTextPlain_parse_line (const char *, int32_t, MimeObject *);
static int MimeInlineTextPlain_parse_eof (MimeObject *, bool);
static int
MimeInlineTextPlainClassInitialize(MimeInlineTextPlainClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
NS_ASSERTION(!oclass->class_initialized, "class not initialized");
oclass->parse_begin = MimeInlineTextPlain_parse_begin;
oclass->parse_line = MimeInlineTextPlain_parse_line;
oclass->parse_eof = MimeInlineTextPlain_parse_eof;
return 0;
}
extern "C"
void
MimeTextBuildPrefixCSS(int32_t quotedSizeSetting, // mail.quoted_size
int32_t quotedStyleSetting, // mail.quoted_style
char *citationColor, // mail.citation_color
nsACString &style)
{
switch (quotedStyleSetting)
{
case 0: // regular
break;
case 1: // bold
style.Append("font-weight: bold; ");
break;
case 2: // italic
style.Append("font-style: italic; ");
break;
case 3: // bold-italic
style.Append("font-weight: bold; font-style: italic; ");
break;
}
switch (quotedSizeSetting)
{
case 0: // regular
break;
case 1: // large
style.Append("font-size: large; ");
break;
case 2: // small
style.Append("font-size: small; ");
break;
}
if (citationColor && *citationColor)
{
style += "color: ";
style += citationColor;
style += ';';
}
}
static int
MimeInlineTextPlain_parse_begin (MimeObject *obj)
{
int status = 0;
bool quoting = ( obj->options
&& ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
) ); // The output will be inserted in the composer as quotation
bool plainHTML = quoting || (obj->options &&
(obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs));
// Just good(tm) HTML. No reliance on CSS.
bool rawPlainText = obj->options &&
(obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer
|| obj->options->format_out == nsMimeOutput::nsMimeMessageAttach);
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
if (status < 0) return status;
if (!obj->output_p) return 0;
if (obj->options &&
obj->options->write_html_p &&
obj->options->output_fn)
{
MimeInlineTextPlain *text = (MimeInlineTextPlain *) obj;
text->mCiteLevel = 0;
// Get the prefs
// Quoting
text->mBlockquoting = true; // mail.quoteasblock
// Viewing
text->mQuotedSizeSetting = 0; // mail.quoted_size
text->mQuotedStyleSetting = 0; // mail.quoted_style
text->mCitationColor = nullptr; // mail.citation_color
text->mStripSig = true; // mail.strip_sig_on_reply
bool graphicalQuote = true; // mail.quoted_graphical
nsIPrefBranch *prefBranch = GetPrefBranch(obj->options);
if (prefBranch)
{
prefBranch->GetIntPref("mail.quoted_size", &(text->mQuotedSizeSetting));
prefBranch->GetIntPref("mail.quoted_style", &(text->mQuotedStyleSetting));
prefBranch->GetCharPref("mail.citation_color", &(text->mCitationColor));
prefBranch->GetBoolPref("mail.strip_sig_on_reply", &(text->mStripSig));
prefBranch->GetBoolPref("mail.quoted_graphical", &graphicalQuote);
prefBranch->GetBoolPref("mail.quoteasblock", &(text->mBlockquoting));
}
if (!rawPlainText)
{
// Get font
// only used for viewing (!plainHTML)
nsAutoCString fontstyle;
nsAutoCString fontLang; // langgroup of the font
// generic font-family name ( -moz-fixed for fixed font and NULL for
// variable font ) is sufficient now that bug 105199 has been fixed.
if (!obj->options->variable_width_plaintext_p)
fontstyle = "font-family: -moz-fixed";
if (nsMimeOutput::nsMimeMessageBodyDisplay == obj->options->format_out ||
nsMimeOutput::nsMimeMessagePrintOutput == obj->options->format_out)
{
int32_t fontSize; // default font size
int32_t fontSizePercentage; // size percentage
nsresult rv = GetMailNewsFont(obj,
!obj->options->variable_width_plaintext_p,
&fontSize, &fontSizePercentage, fontLang);
if (NS_SUCCEEDED(rv))
{
if ( ! fontstyle.IsEmpty() ) {
fontstyle += "; ";
}
fontstyle += "font-size: ";
fontstyle.AppendInt(fontSize);
fontstyle += "px;";
}
}
// Opening <div>. We currently have to add formatting here. :-(
nsAutoCString openingDiv;
if (!quoting)
/* 4.x' editor can't break <div>s (e.g. to interleave comments).
We'll add the class to the <blockquote type=cite> later. */
{
openingDiv = "<div class=\"moz-text-plain\"";
if (!plainHTML)
{
if (obj->options->wrap_long_lines_p)
openingDiv += " wrap=true";
else
openingDiv += " wrap=false";
if (graphicalQuote)
openingDiv += " graphical-quote=true";
else
openingDiv += " graphical-quote=false";
if (!fontstyle.IsEmpty())
{
openingDiv += " style=\"";
openingDiv += fontstyle;
openingDiv += '\"';
}
if (!fontLang.IsEmpty())
{
openingDiv += " lang=\"";
openingDiv += fontLang;
openingDiv += '\"';
}
}
openingDiv += "><pre wrap>\n";
}
else
openingDiv = "<pre wrap>\n";
/* text/plain objects always have separators before and after them.
Note that this is not the case for text/enriched objects. */
status = MimeObject_write_separator(obj);
if (status < 0) return status;
status = MimeObject_write(obj, openingDiv.get(), openingDiv.Length(), true);
if (status < 0) return status;
}
}
return 0;
}
static int
MimeInlineTextPlain_parse_eof (MimeObject *obj, bool abort_p)
{
int status;
// Has this method already been called for this object?
// In that case return.
if (obj->closed_p) return 0;
nsCString citationColor;
MimeInlineTextPlain *text = (MimeInlineTextPlain *) obj;
if (text && text->mCitationColor)
citationColor.Adopt(text->mCitationColor);
bool quoting = ( obj->options
&& ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
) ); // see above
bool rawPlainText = obj->options &&
(obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer
|| obj->options->format_out == nsMimeOutput::nsMimeMessageAttach);
/* Run parent method first, to flush out any buffered data. */
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0) return status;
if (!obj->output_p) return 0;
if (obj->options &&
obj->options->write_html_p &&
obj->options->output_fn &&
!abort_p && !rawPlainText)
{
MimeInlineTextPlain *text = (MimeInlineTextPlain *) obj;
if (text->mIsSig && !quoting)
{
status = MimeObject_write(obj, "</div>", 6, false); // .moz-txt-sig
if (status < 0) return status;
}
status = MimeObject_write(obj, "</pre>", 6, false);
if (status < 0) return status;
if (!quoting)
{
status = MimeObject_write(obj, "</div>", 6, false);
// .moz-text-plain
if (status < 0) return status;
}
/* text/plain objects always have separators before and after them.
Note that this is not the case for text/enriched objects.
*/
status = MimeObject_write_separator(obj);
if (status < 0) return status;
}
return 0;
}
static int
MimeInlineTextPlain_parse_line (const char *line, int32_t length, MimeObject *obj)
{
int status;
bool quoting = ( obj->options
&& ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
) ); // see above
bool plainHTML = quoting || (obj->options &&
obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs);
// see above
bool rawPlainText = obj->options &&
(obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer
|| obj->options->format_out == nsMimeOutput::nsMimeMessageAttach);
// this routine gets called for every line of data that comes through the
// mime converter. It's important to make sure we are efficient with
// how we allocate memory in this routine. be careful if you go to add
// more to this routine.
NS_ASSERTION(length > 0, "zero length");
if (length <= 0) return 0;
mozITXTToHTMLConv *conv = GetTextConverter(obj->options);
MimeInlineTextPlain *text = (MimeInlineTextPlain *) obj;
bool skipConversion = !conv || rawPlainText ||
(obj->options && obj->options->force_user_charset);
char *mailCharset = NULL;
nsresult rv;
if (!skipConversion)
{
nsDependentCString inputStr(line, length);
nsAutoString lineSourceStr;
// For 'SaveAs', |line| is in |mailCharset|.
// convert |line| to UTF-16 before 'html'izing (calling ScanTXT())
if (obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs)
{ // Get the mail charset of this message.
MimeInlineText *inlinetext = (MimeInlineText *) obj;
if (!inlinetext->initializeCharset)
((MimeInlineTextClass*)&mimeInlineTextClass)->initialize_charset(obj);
mailCharset = inlinetext->charset;
if (mailCharset && *mailCharset) {
rv = nsMsgI18NConvertToUnicode(mailCharset, inputStr, lineSourceStr);
NS_ENSURE_SUCCESS(rv, -1);
}
else // this probably never happens ...
CopyUTF8toUTF16(inputStr, lineSourceStr);
}
else // line is in UTF-8
CopyUTF8toUTF16(inputStr, lineSourceStr);
nsAutoCString prefaceResultStr; // Quoting stuff before the real text
// Recognize quotes
uint32_t oldCiteLevel = text->mCiteLevel;
uint32_t logicalLineStart = 0;
rv = conv->CiteLevelTXT(lineSourceStr.get(),
&logicalLineStart, &(text->mCiteLevel));
NS_ENSURE_SUCCESS(rv, -1);
// Find out, which recognitions to do
uint32_t whattodo = obj->options->whattodo;
if (plainHTML)
{
if (quoting)
whattodo = 0; // This is done on Send. Don't do it twice.
else
whattodo = whattodo & ~mozITXTToHTMLConv::kGlyphSubstitution;
/* Do recognition for the case, the result is viewed in
Mozilla, but not GlyphSubstitution, because other UAs
might not be able to display the glyphs. */
if (!text->mBlockquoting)
text->mCiteLevel = 0;
}
// Write blockquote
if (text->mCiteLevel > oldCiteLevel)
{
prefaceResultStr += "</pre>";
for (uint32_t i = 0; i < text->mCiteLevel - oldCiteLevel; i++)
{
nsAutoCString style;
MimeTextBuildPrefixCSS(text->mQuotedSizeSetting, text->mQuotedStyleSetting,
text->mCitationColor, style);
if (!plainHTML && !style.IsEmpty())
{
prefaceResultStr += "<blockquote type=cite style=\"";
prefaceResultStr += style;
prefaceResultStr += "\">";
}
else
prefaceResultStr += "<blockquote type=cite>";
}
prefaceResultStr += "<pre wrap>\n";
}
else if (text->mCiteLevel < oldCiteLevel)
{
prefaceResultStr += "</pre>";
for (uint32_t i = 0; i < oldCiteLevel - text->mCiteLevel; i++)
prefaceResultStr += "</blockquote>";
prefaceResultStr += "<pre wrap>\n";
}
// Write plain text quoting tags
if (logicalLineStart != 0 && !(plainHTML && text->mBlockquoting))
{
if (!plainHTML)
prefaceResultStr += "<span class=\"moz-txt-citetags\">";
nsString citeTagsSource(StringHead(lineSourceStr, logicalLineStart));
// Convert to HTML
nsString citeTagsResultUnichar;
rv = conv->ScanTXT(citeTagsSource.get(), 0 /* no recognition */,
getter_Copies(citeTagsResultUnichar));
if (NS_FAILED(rv)) return -1;
prefaceResultStr.Append(NS_ConvertUTF16toUTF8(citeTagsResultUnichar));
if (!plainHTML)
prefaceResultStr += "</span>";
}
// recognize signature
if ((lineSourceStr.Length() >= 4)
&& lineSourceStr.First() == '-'
&& Substring(lineSourceStr, 0, 3).EqualsLiteral("-- ")
&& (lineSourceStr[3] == '\r' || lineSourceStr[3] == '\n') )
{
text->mIsSig = true;
if (!quoting)
prefaceResultStr += "<div class=\"moz-txt-sig\">";
}
/* This is the main TXT to HTML conversion:
escaping (very important), eventually recognizing etc. */
nsString lineResultUnichar;
rv = conv->ScanTXT(lineSourceStr.get() + logicalLineStart,
whattodo, getter_Copies(lineResultUnichar));
NS_ENSURE_SUCCESS(rv, -1);
if (!(text->mIsSig && quoting && text->mStripSig))
{
status = MimeObject_write(obj, prefaceResultStr.get(), prefaceResultStr.Length(), true);
if (status < 0) return status;
nsAutoCString outString;
if (obj->options->format_out != nsMimeOutput::nsMimeMessageSaveAs ||
!mailCharset || !*mailCharset)
CopyUTF16toUTF8(lineResultUnichar, outString);
else
{ // convert back to mailCharset before writing.
rv = nsMsgI18NConvertFromUnicode(mailCharset,
lineResultUnichar, outString);
NS_ENSURE_SUCCESS(rv, -1);
}
status = MimeObject_write(obj, outString.get(), outString.Length(), true);
}
else
{
status = 0;
}
}
else
{
status = MimeObject_write(obj, line, length, true);
}
return status;
}

View file

@ -0,0 +1,39 @@
/* -*- 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/. */
/* The MimeInlineTextPlain class implements the text/plain MIME content type,
and is also used for all otherwise-unknown text/ subtypes.
*/
#ifndef _MIMETPLA_H_
#define _MIMETPLA_H_
#include "mimetext.h"
typedef struct MimeInlineTextPlainClass MimeInlineTextPlainClass;
typedef struct MimeInlineTextPlain MimeInlineTextPlain;
struct MimeInlineTextPlainClass {
MimeInlineTextClass text;
};
extern MimeInlineTextPlainClass mimeInlineTextPlainClass;
struct MimeInlineTextPlain {
MimeInlineText text;
uint32_t mCiteLevel;
bool mBlockquoting;
//bool mInsideQuote;
int32_t mQuotedSizeSetting; // mail.quoted_size
int32_t mQuotedStyleSetting; // mail.quoted_style
char *mCitationColor; // mail.citation_color
bool mStripSig; // mail.strip_sig_on_reply
bool mIsSig;
};
#define MimeInlineTextPlainClassInitializer(ITYPE,CSUPER) \
{ MimeInlineTextClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMETPLA_H_ */

View file

@ -0,0 +1,353 @@
/* -*- 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 "mimetric.h"
#include "mimebuf.h"
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "msgCore.h"
#include <ctype.h>
#define MIME_SUPERCLASS mimeInlineTextClass
MimeDefClass(MimeInlineTextRichtext, MimeInlineTextRichtextClass,
mimeInlineTextRichtextClass, &MIME_SUPERCLASS);
static int MimeInlineTextRichtext_parse_line (const char *, int32_t, MimeObject *);
static int MimeInlineTextRichtext_parse_begin (MimeObject *);
static int MimeInlineTextRichtext_parse_eof (MimeObject *, bool);
static int
MimeInlineTextRichtextClassInitialize(MimeInlineTextRichtextClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
oclass->parse_begin = MimeInlineTextRichtext_parse_begin;
oclass->parse_line = MimeInlineTextRichtext_parse_line;
oclass->parse_eof = MimeInlineTextRichtext_parse_eof;
return 0;
}
/* This function has this clunky interface because it needs to be called
from outside this module (no MimeObject, etc.)
*/
int
MimeRichtextConvert (const char *line, int32_t length,
MimeObject *obj,
char **obufferP,
int32_t *obuffer_sizeP,
bool enriched_p)
{
/* RFC 1341 (the original MIME spec) defined text/richtext.
RFC 1563 superceded text/richtext with text/enriched.
The changes from text/richtext to text/enriched are:
- CRLF semantics are different
- << maps to <
- These tags were added:
<VERBATIM>, <NOFILL>, <PARAM>, <FLUSHBOTH>
- These tags were removed:
<COMMENT>, <OUTDENT>, <OUTDENTRIGHT>, <SAMEPAGE>, <SUBSCRIPT>,
<SUPERSCRIPT>, <HEADING>, <FOOTING>, <PARAGRAPH>, <SIGNATURE>,
<LT>, <NL>, <NP>
This method implements them both.
draft-resnick-text-enriched-03.txt is a proposed update to 1563.
- These tags were added:
<FONTFAMILY>, <COLOR>, <PARAINDENT>, <LANG>.
However, all of these rely on the magic <PARAM> tag, which we
don't implement, so we're ignoring all of these.
Interesting fact: it's by Peter W. Resnick from Qualcomm (Eudora).
And it also says "It is fully expected that other text formatting
standards like HTML and SGML will supplant text/enriched in
Internet mail."
*/
int status = 0;
char *out;
const char *data_end;
const char *last_end;
const char *this_start;
const char *this_end;
unsigned int desired_size;
// The code below must never expand the input by more than 5x;
// if it does, the desired_size multiplier (5) below must be changed too
#define BGROWTH 5
if ( (uint32_t)length >= ( (uint32_t) 0xfffffffe)/BGROWTH )
return -1;
desired_size = (length * BGROWTH) + 1;
#undef BGROWTH
if (desired_size >= (uint32_t) *obuffer_sizeP)
status = mime_GrowBuffer (desired_size, sizeof(char), 1024,
obufferP, obuffer_sizeP);
if (status < 0) return status;
if (enriched_p)
{
for (this_start = line; this_start < line + length; this_start++)
if (!IS_SPACE (*this_start)) break;
if (this_start >= line + length) /* blank line */
{
PL_strncpyz (*obufferP, "<BR>", *obuffer_sizeP);
return MimeObject_write(obj, *obufferP, strlen(*obufferP), true);
}
}
uint32_t outlen = (uint32_t) *obuffer_sizeP;
out = *obufferP;
*out = 0;
data_end = line + length;
last_end = line;
this_start = last_end;
this_end = this_start;
uint32_t addedlen = 0;
while (this_end < data_end)
{
/* Skip forward to next special character. */
while (this_start < data_end &&
*this_start != '<' && *this_start != '>' &&
*this_start != '&')
this_start++;
this_end = this_start;
/* Skip to the end of the tag. */
if (this_start < data_end && *this_start == '<')
{
this_end++;
while (this_end < data_end &&
!IS_SPACE(*this_end) &&
*this_end != '<' && *this_end != '>' &&
*this_end != '&')
this_end++;
}
this_end++;
/* Push out the text preceeding the tag. */
if (last_end && last_end != this_start)
{
memcpy (out, last_end, this_start - last_end);
out += this_start - last_end;
*out = 0;
outlen -= (this_start - last_end);
}
if (this_start >= data_end)
break;
else if (*this_start == '&')
{
PL_strncpyz (out, "&amp;", outlen);
addedlen = strlen(out);
outlen -= addedlen;
out += addedlen;
}
else if (*this_start == '>')
{
PL_strncpyz (out, "&gt;", outlen);
addedlen = strlen(out);
outlen -= addedlen;
out += addedlen;
}
else if (enriched_p &&
this_start < data_end + 1 &&
this_start[0] == '<' &&
this_start[1] == '<')
{
PL_strncpyz (out, "&lt;", outlen);
addedlen = strlen(out);
outlen -= addedlen;
out += addedlen;
}
else if (this_start != this_end)
{
/* Push out this ID. */
const char *old = this_start + 1;
const char *tag_open = 0;
const char *tag_close = 0;
if (*old == '/')
{
/* This is </tag> */
old++;
}
switch (*old)
{
case 'b': case 'B':
if (!PL_strncasecmp ("BIGGER>", old, 7))
tag_open = "<FONT SIZE=\"+1\">", tag_close = "</FONT>";
else if (!PL_strncasecmp ("BLINK>", old, 5))
/* Of course, both text/richtext and text/enriched must be
enhanced *somehow*... Or else what would people think. */
tag_open = "<BLINK>", tag_close = "</BLINK>";
else if (!PL_strncasecmp ("BOLD>", old, 5))
tag_open = "<B>", tag_close = "</B>";
break;
case 'c': case 'C':
if (!PL_strncasecmp ("CENTER>", old, 7))
tag_open = "<CENTER>", tag_close = "</CENTER>";
else if (!enriched_p &&
!PL_strncasecmp ("COMMENT>", old, 8))
tag_open = "<!-- ", tag_close = " -->";
break;
case 'e': case 'E':
if (!PL_strncasecmp ("EXCERPT>", old, 8))
tag_open = "<BLOCKQUOTE>", tag_close = "</BLOCKQUOTE>";
break;
case 'f': case 'F':
if (!PL_strncasecmp ("FIXED>", old, 6))
tag_open = "<TT>", tag_close = "</TT>";
else if (enriched_p &&
!PL_strncasecmp ("FLUSHBOTH>", old, 10))
tag_open = "<P ALIGN=LEFT>", tag_close = "</P>";
else if (!PL_strncasecmp ("FLUSHLEFT>", old, 10))
tag_open = "<P ALIGN=LEFT>", tag_close = "</P>";
else if (!PL_strncasecmp ("FLUSHRIGHT>", old, 11))
tag_open = "<P ALIGN=RIGHT>", tag_close = "</P>";
else if (!enriched_p &&
!PL_strncasecmp ("FOOTING>", old, 8))
tag_open = "<H6>", tag_close = "</H6>";
break;
case 'h': case 'H':
if (!enriched_p &&
!PL_strncasecmp ("HEADING>", old, 8))
tag_open = "<H6>", tag_close = "</H6>";
break;
case 'i': case 'I':
if (!PL_strncasecmp ("INDENT>", old, 7))
tag_open = "<UL>", tag_close = "</UL>";
else if (!PL_strncasecmp ("INDENTRIGHT>", old, 12))
tag_open = 0, tag_close = 0;
/* else if (!enriched_p &&
!PL_strncasecmp ("ISO-8859-", old, 9))
tag_open = 0, tag_close = 0; */
else if (!PL_strncasecmp ("ITALIC>", old, 7))
tag_open = "<I>", tag_close = "</I>";
break;
case 'l': case 'L':
if (!enriched_p &&
!PL_strncasecmp ("LT>", old, 3))
tag_open = "&lt;", tag_close = 0;
break;
case 'n': case 'N':
if (!enriched_p &&
!PL_strncasecmp ("NL>", old, 3))
tag_open = "<BR>", tag_close = 0;
if (enriched_p &&
!PL_strncasecmp ("NOFILL>", old, 7))
tag_open = "<NOBR>", tag_close = "</NOBR>";
/* else if (!enriched_p &&
!PL_strncasecmp ("NO-OP>", old, 6))
tag_open = 0, tag_close = 0; */
/* else if (!enriched_p &&
!PL_strncasecmp ("NP>", old, 3))
tag_open = 0, tag_close = 0; */
break;
case 'o': case 'O':
if (!enriched_p &&
!PL_strncasecmp ("OUTDENT>", old, 8))
tag_open = 0, tag_close = 0;
else if (!enriched_p &&
!PL_strncasecmp ("OUTDENTRIGHT>", old, 13))
tag_open = 0, tag_close = 0;
break;
case 'p': case 'P':
if (enriched_p &&
!PL_strncasecmp ("PARAM>", old, 6))
tag_open = "<!-- ", tag_close = " -->";
else if (!enriched_p &&
!PL_strncasecmp ("PARAGRAPH>", old, 10))
tag_open = "<P>", tag_close = 0;
break;
case 's': case 'S':
if (!enriched_p &&
!PL_strncasecmp ("SAMEPAGE>", old, 9))
tag_open = 0, tag_close = 0;
else if (!enriched_p &&
!PL_strncasecmp ("SIGNATURE>", old, 10))
tag_open = "<I><FONT SIZE=\"-1\">", tag_close = "</FONT></I>";
else if (!PL_strncasecmp ("SMALLER>", old, 8))
tag_open = "<FONT SIZE=\"-1\">", tag_close = "</FONT>";
else if (!enriched_p &&
!PL_strncasecmp ("SUBSCRIPT>", old, 10))
tag_open = "<SUB>", tag_close = "</SUB>";
else if (!enriched_p &&
!PL_strncasecmp ("SUPERSCRIPT>", old, 12))
tag_open = "<SUP>", tag_close = "</SUP>";
break;
case 'u': case 'U':
if (!PL_strncasecmp ("UNDERLINE>", old, 10))
tag_open = "<U>", tag_close = "</U>";
/* else if (!enriched_p &&
!PL_strncasecmp ("US-ASCII>", old, 10))
tag_open = 0, tag_close = 0; */
break;
case 'v': case 'V':
if (enriched_p &&
!PL_strncasecmp ("VERBATIM>", old, 9))
tag_open = "<PRE>", tag_close = "</PRE>";
break;
}
if (this_start[1] == '/')
{
if (tag_close) PL_strncpyz (out, tag_close, outlen);
addedlen = strlen (out);
outlen -= addedlen;
out += addedlen;
}
else
{
if (tag_open) PL_strncpyz (out, tag_open, outlen);
addedlen = strlen (out);
outlen -= addedlen;
out += addedlen;
}
}
/* now go around again */
last_end = this_end;
this_start = last_end;
}
*out = 0;
return MimeObject_write(obj, *obufferP, out - *obufferP, true);
}
static int
MimeInlineTextRichtext_parse_line (const char *line, int32_t length, MimeObject *obj)
{
bool enriched_p = (((MimeInlineTextRichtextClass *) obj->clazz)
->enriched_p);
return MimeRichtextConvert (line, length,
obj,
&obj->obuffer, &obj->obuffer_size,
enriched_p);
}
static int
MimeInlineTextRichtext_parse_begin (MimeObject *obj)
{
int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
char s[] = "";
if (status < 0) return status;
return MimeObject_write(obj, s, 0, true); /* force out any separators... */
}
static int
MimeInlineTextRichtext_parse_eof (MimeObject *obj, bool abort_p)
{
int status;
if (obj->closed_p) return 0;
/* Run parent method first, to flush out any buffered data. */
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
if (status < 0) return status;
return 0;
}

View file

@ -0,0 +1,33 @@
/* -*- 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/. */
#ifndef _MIMETRIC_H_
#define _MIMETRIC_H_
#include "mimetext.h"
/* The MimeInlineTextRichtext class implements the (obsolete and deprecated)
text/richtext MIME content type, as defined in RFC 1341, and also the
text/enriched MIME content type, as defined in RFC 1563.
*/
typedef struct MimeInlineTextRichtextClass MimeInlineTextRichtextClass;
typedef struct MimeInlineTextRichtext MimeInlineTextRichtext;
struct MimeInlineTextRichtextClass {
MimeInlineTextClass text;
bool enriched_p; /* Whether we should act like text/enriched instead. */
};
extern MimeInlineTextRichtextClass mimeInlineTextRichtextClass;
struct MimeInlineTextRichtext {
MimeInlineText text;
};
#define MimeInlineTextRichtextClassInitializer(ITYPE,CSUPER) \
{ MimeInlineTextClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMETRIC_H_ */

View file

@ -0,0 +1,588 @@
/* -*- 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 "mimeunty.h"
#include "prmem.h"
#include "plstr.h"
#include "prlog.h"
#include "nsMimeTypes.h"
#include "msgCore.h"
#include "nsMimeStringResources.h"
#include <ctype.h>
#define MIME_SUPERCLASS mimeContainerClass
MimeDefClass(MimeUntypedText, MimeUntypedTextClass,
mimeUntypedTextClass, &MIME_SUPERCLASS);
static int MimeUntypedText_initialize (MimeObject *);
static void MimeUntypedText_finalize (MimeObject *);
static int MimeUntypedText_parse_begin (MimeObject *);
static int MimeUntypedText_parse_line (const char *, int32_t, MimeObject *);
static int MimeUntypedText_open_subpart (MimeObject *obj,
MimeUntypedTextSubpartType ttype,
const char *type,
const char *enc,
const char *name,
const char *desc);
static int MimeUntypedText_close_subpart (MimeObject *obj);
static bool MimeUntypedText_uu_begin_line_p(const char *line, int32_t length,
MimeDisplayOptions *opt,
char **type_ret,
char **name_ret);
static bool MimeUntypedText_uu_end_line_p(const char *line, int32_t length);
static bool MimeUntypedText_yenc_begin_line_p(const char *line, int32_t length,
MimeDisplayOptions *opt,
char **type_ret,
char **name_ret);
static bool MimeUntypedText_yenc_end_line_p(const char *line, int32_t length);
static bool MimeUntypedText_binhex_begin_line_p(const char *line,
int32_t length,
MimeDisplayOptions *opt);
static bool MimeUntypedText_binhex_end_line_p(const char *line,
int32_t length);
static int
MimeUntypedTextClassInitialize(MimeUntypedTextClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *) clazz;
PR_ASSERT(!oclass->class_initialized);
oclass->initialize = MimeUntypedText_initialize;
oclass->finalize = MimeUntypedText_finalize;
oclass->parse_begin = MimeUntypedText_parse_begin;
oclass->parse_line = MimeUntypedText_parse_line;
return 0;
}
static int
MimeUntypedText_initialize (MimeObject *object)
{
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
}
static void
MimeUntypedText_finalize (MimeObject *object)
{
MimeUntypedText *uty = (MimeUntypedText *) object;
if (uty->open_hdrs)
{
/* Oops, those shouldn't still be here... */
MimeHeaders_free(uty->open_hdrs);
uty->open_hdrs = 0;
}
/* What about the open_subpart? We're gonna have to assume that it
is also on the MimeContainer->children list, and will get cleaned
up by that class. */
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
}
static int
MimeUntypedText_parse_begin (MimeObject *obj)
{
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
}
static int
MimeUntypedText_parse_line (const char *line, int32_t length, MimeObject *obj)
{
MimeUntypedText *uty = (MimeUntypedText *) obj;
int status = 0;
char *name = 0, *type = 0;
bool begin_line_p = false;
NS_ASSERTION(line && *line, "empty line in mime untyped parse_line");
if (!line || !*line) return -1;
/* If we're supposed to write this object, but aren't supposed to convert
it to HTML, simply pass it through unaltered. */
if (obj->output_p &&
obj->options &&
!obj->options->write_html_p &&
obj->options->output_fn)
return MimeObject_write(obj, line, length, true);
/* Open a new sub-part if this line demands it.
*/
if (line[0] == 'b' &&
MimeUntypedText_uu_begin_line_p(line, length, obj->options,
&type, &name))
{
/* Close the old part and open a new one. */
status = MimeUntypedText_open_subpart (obj,
MimeUntypedTextSubpartTypeUUE,
type, ENCODING_UUENCODE,
name, NULL);
PR_FREEIF(name);
PR_FREEIF(type);
if (status < 0) return status;
begin_line_p = true;
}
else if (line[0] == '=' &&
MimeUntypedText_yenc_begin_line_p(line, length, obj->options,
&type, &name))
{
/* Close the old part and open a new one. */
status = MimeUntypedText_open_subpart (obj,
MimeUntypedTextSubpartTypeYEnc,
type, ENCODING_YENCODE,
name, NULL);
PR_FREEIF(name);
PR_FREEIF(type);
if (status < 0) return status;
begin_line_p = true;
}
else if (line[0] == '(' && line[1] == 'T' &&
MimeUntypedText_binhex_begin_line_p(line, length, obj->options))
{
/* Close the old part and open a new one. */
status = MimeUntypedText_open_subpart (obj,
MimeUntypedTextSubpartTypeBinhex,
APPLICATION_BINHEX, NULL,
NULL, NULL);
if (status < 0) return status;
begin_line_p = true;
}
/* Open a text/plain sub-part if there is no sub-part open.
*/
if (!uty->open_subpart)
{
// rhp: If we get here and we are being fed a line ending, we should
// just eat it and continue and if we really get more data, we'll open
// up the subpart then.
//
if (line[0] == '\r') return 0;
if (line[0] == '\n') return 0;
PR_ASSERT(!begin_line_p);
status = MimeUntypedText_open_subpart (obj,
MimeUntypedTextSubpartTypeText,
TEXT_PLAIN, NULL, NULL, NULL);
PR_ASSERT(uty->open_subpart);
if (!uty->open_subpart) return -1;
if (status < 0) return status;
}
/* Hand this line to the currently-open sub-part.
*/
status = uty->open_subpart->clazz->parse_buffer(line, length,
uty->open_subpart);
if (status < 0) return status;
/* Close this sub-part if this line demands it.
*/
if (begin_line_p)
;
else if (line[0] == 'e' &&
uty->type == MimeUntypedTextSubpartTypeUUE &&
MimeUntypedText_uu_end_line_p(line, length))
{
status = MimeUntypedText_close_subpart (obj);
if (status < 0) return status;
NS_ASSERTION(!uty->open_subpart, "no open subpart");
}
else if (line[0] == '=' &&
uty->type == MimeUntypedTextSubpartTypeYEnc &&
MimeUntypedText_yenc_end_line_p(line, length))
{
status = MimeUntypedText_close_subpart (obj);
if (status < 0) return status;
NS_ASSERTION(!uty->open_subpart, "no open subpart");
}
else if (uty->type == MimeUntypedTextSubpartTypeBinhex &&
MimeUntypedText_binhex_end_line_p(line, length))
{
status = MimeUntypedText_close_subpart (obj);
if (status < 0) return status;
NS_ASSERTION(!uty->open_subpart, "no open subpart");
}
return 0;
}
static int
MimeUntypedText_close_subpart (MimeObject *obj)
{
MimeUntypedText *uty = (MimeUntypedText *) obj;
int status;
if (uty->open_subpart)
{
status = uty->open_subpart->clazz->parse_eof(uty->open_subpart, false);
uty->open_subpart = 0;
PR_ASSERT(uty->open_hdrs);
if (uty->open_hdrs)
{
MimeHeaders_free(uty->open_hdrs);
uty->open_hdrs = 0;
}
uty->type = MimeUntypedTextSubpartTypeText;
if (status < 0) return status;
/* Never put out a separator between sub-parts of UntypedText.
(This bypasses the rule that text/plain subparts always
have separators before and after them.)
*/
if (obj->options && obj->options->state)
obj->options->state->separator_suppressed_p = true;
}
PR_ASSERT(!uty->open_hdrs);
return 0;
}
static int
MimeUntypedText_open_subpart (MimeObject *obj,
MimeUntypedTextSubpartType ttype,
const char *type,
const char *enc,
const char *name,
const char *desc)
{
MimeUntypedText *uty = (MimeUntypedText *) obj;
int status = 0;
char *h = 0;
if (!type || !*type || !PL_strcasecmp(type, UNKNOWN_CONTENT_TYPE))
type = APPLICATION_OCTET_STREAM;
if (enc && !*enc)
enc = 0;
if (desc && !*desc)
desc = 0;
if (name && !*name)
name = 0;
if (uty->open_subpart)
{
status = MimeUntypedText_close_subpart (obj);
if (status < 0) return status;
}
NS_ASSERTION(!uty->open_subpart, "no open subpart");
NS_ASSERTION(!uty->open_hdrs, "no open headers");
/* To make one of these implicitly-typed sub-objects, we make up a fake
header block, containing only the minimum number of MIME headers needed.
We could do most of this (Type and Encoding) by making a null header
block, and simply setting obj->content_type and obj->encoding; but making
a fake header block is better for two reasons: first, it means that
something will actually be displayed when in `Show All Headers' mode;
and second, it's the only way to communicate the filename parameter,
aside from adding a new slot to MimeObject (which is something to be
avoided when possible.)
*/
uty->open_hdrs = MimeHeaders_new();
if (!uty->open_hdrs) return MIME_OUT_OF_MEMORY;
uint32_t hlen = strlen(type) +
(enc ? strlen(enc) : 0) +
(desc ? strlen(desc) : 0) +
(name ? strlen(name) : 0) +
100;
h = (char *) PR_MALLOC(hlen);
if (!h) return MIME_OUT_OF_MEMORY;
PL_strncpyz(h, HEADER_CONTENT_TYPE ": ", hlen);
PL_strcatn(h, hlen, type);
PL_strcatn(h, hlen, MSG_LINEBREAK);
status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
if (status < 0) goto FAIL;
if (enc)
{
PL_strncpyz(h, HEADER_CONTENT_TRANSFER_ENCODING ": ", hlen);
PL_strcatn(h, hlen, enc);
PL_strcatn(h, hlen, MSG_LINEBREAK);
status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
if (status < 0) goto FAIL;
}
if (desc)
{
PL_strncpyz(h, HEADER_CONTENT_DESCRIPTION ": ", hlen);
PL_strcatn(h, hlen, desc);
PL_strcatn(h, hlen, MSG_LINEBREAK);
status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
if (status < 0) goto FAIL;
}
if (name)
{
PL_strncpyz(h, HEADER_CONTENT_DISPOSITION ": inline; filename=\"", hlen);
PL_strcatn(h, hlen, name);
PL_strcatn(h, hlen, "\"" MSG_LINEBREAK);
status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
if (status < 0) goto FAIL;
}
/* push out a blank line. */
PL_strncpyz(h, MSG_LINEBREAK, hlen);
status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
if (status < 0) goto FAIL;
/* Create a child... */
{
bool horrid_kludge = (obj->options && obj->options->state &&
obj->options->state->first_part_written_p);
if (horrid_kludge)
obj->options->state->first_part_written_p = false;
uty->open_subpart = mime_create(type, uty->open_hdrs, obj->options);
if (horrid_kludge)
obj->options->state->first_part_written_p = true;
if (!uty->open_subpart)
{
status = MIME_OUT_OF_MEMORY;
goto FAIL;
}
}
/* Add it to the list... */
status = ((MimeContainerClass *) obj->clazz)->add_child(obj,
uty->open_subpart);
if (status < 0)
{
mime_free(uty->open_subpart);
uty->open_subpart = 0;
goto FAIL;
}
/* And start its parser going. */
status = uty->open_subpart->clazz->parse_begin(uty->open_subpart);
if (status < 0)
{
/* MimeContainer->finalize will take care of shutting it down now. */
uty->open_subpart = 0;
goto FAIL;
}
uty->type = ttype;
FAIL:
PR_FREEIF(h);
if (status < 0 && uty->open_hdrs)
{
MimeHeaders_free(uty->open_hdrs);
uty->open_hdrs = 0;
}
return status;
}
static bool
MimeUntypedText_uu_begin_line_p(const char *line, int32_t length,
MimeDisplayOptions *opt,
char **type_ret, char **name_ret)
{
const char *s;
char *name = 0;
char *type = 0;
if (type_ret) *type_ret = 0;
if (name_ret) *name_ret = 0;
if (strncmp (line, "begin ", 6)) return false;
/* ...then three or four octal digits. */
s = line + 6;
if (*s < '0' || *s > '7') return false;
s++;
if (*s < '0' || *s > '7') return false;
s++;
if (*s < '0' || *s > '7') return false;
s++;
if (*s == ' ')
s++;
else
{
if (*s < '0' || *s > '7') return false;
s++;
if (*s != ' ') return false;
}
while (IS_SPACE(*s))
s++;
name = (char *) PR_MALLOC(((line+length)-s) + 1);
if (!name) return false; /* grr... */
memcpy(name, s, (line+length)-s);
name[(line+length)-s] = 0;
/* take off newline. */
if (name[strlen(name)-1] == '\n') name[strlen(name)-1] = 0;
if (name[strlen(name)-1] == '\r') name[strlen(name)-1] = 0;
/* Now try and figure out a type.
*/
if (opt && opt->file_type_fn)
type = opt->file_type_fn(name, opt->stream_closure);
else
type = 0;
if (name_ret)
*name_ret = name;
else
PR_FREEIF(name);
if (type_ret)
*type_ret = type;
else
PR_FREEIF(type);
return true;
}
static bool
MimeUntypedText_uu_end_line_p(const char *line, int32_t length)
{
#if 0
/* A strictly conforming uuencode end line. */
return (line[0] == 'e' &&
line[1] == 'n' &&
line[2] == 'd' &&
(line[3] == 0 || IS_SPACE(line[3])));
#else
/* ...but, why don't we accept any line that begins with the three
letters "END" in any case: I've seen lots of partial messages
that look like
BEGIN----- Cut Here-----
begin 644 foo.gif
Mxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Mxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Mxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
END------- Cut Here-----
so let's be lenient here. (This is only for the untyped-text-plain
case -- the uudecode parser itself is strict.)
*/
return (line[0] == ' ' ||
line[0] == '\t' ||
((line[0] == 'e' || line[0] == 'E') &&
(line[1] == 'n' || line[1] == 'N') &&
(line[2] == 'd' || line[2] == 'D')));
#endif
}
static bool
MimeUntypedText_yenc_begin_line_p(const char *line, int32_t length,
MimeDisplayOptions *opt,
char **type_ret, char **name_ret)
{
const char *s;
const char *endofline = line + length;
char *name = 0;
char *type = 0;
if (type_ret) *type_ret = 0;
if (name_ret) *name_ret = 0;
/* we don't support yenc V2 neither multipart yencode,
therefore the second parameter should always be "line="*/
if (length < 13 || strncmp (line, "=ybegin line=", 13)) return false;
/* ...then couple digits. */
for (s = line + 13; s < endofline; s ++)
if (*s < '0' || *s > '9')
break;
/* ...next, look for <space>size= */
if ((endofline - s) < 6 || strncmp (s, " size=", 6)) return false;
/* ...then couple digits. */
for (s += 6; s < endofline; s ++)
if (*s < '0' || *s > '9')
break;
/* ...next, look for <space>name= */
if ((endofline - s) < 6 || strncmp (s, " name=", 6)) return false;
/* anything left is the file name */
s += 6;
name = (char *) PR_MALLOC((endofline-s) + 1);
if (!name) return false; /* grr... */
memcpy(name, s, endofline-s);
name[endofline-s] = 0;
/* take off newline. */
if (name[strlen(name)-1] == '\n') name[strlen(name)-1] = 0;
if (name[strlen(name)-1] == '\r') name[strlen(name)-1] = 0;
/* Now try and figure out a type.
*/
if (opt && opt->file_type_fn)
type = opt->file_type_fn(name, opt->stream_closure);
else
type = 0;
if (name_ret)
*name_ret = name;
else
PR_FREEIF(name);
if (type_ret)
*type_ret = type;
else
PR_FREEIF(type);
return true;
}
static bool
MimeUntypedText_yenc_end_line_p(const char *line, int32_t length)
{
if (length < 11 || strncmp (line, "=yend size=", 11)) return false;
return true;
}
#define BINHEX_MAGIC "(This file must be converted with BinHex 4.0)"
#define BINHEX_MAGIC_LEN 45
static bool
MimeUntypedText_binhex_begin_line_p(const char *line, int32_t length,
MimeDisplayOptions *opt)
{
if (length <= BINHEX_MAGIC_LEN)
return false;
while(length > 0 && IS_SPACE(line[length-1]))
length--;
if (length != BINHEX_MAGIC_LEN)
return false;
if (!strncmp(line, BINHEX_MAGIC, BINHEX_MAGIC_LEN))
return true;
else
return false;
}
static bool
MimeUntypedText_binhex_end_line_p(const char *line, int32_t length)
{
if (length > 0 && line[length-1] == '\n') length--;
if (length > 0 && line[length-1] == '\r') length--;
if (length != 0 && length != 64)
return true;
else
return false;
}

View file

@ -0,0 +1,70 @@
/* -*- 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/. */
#ifndef _MIMEUNTY_H_
#define _MIMEUNTY_H_
#include "mimecont.h"
/* The MimeUntypedText class is used for untyped message contents, that is,
it is the class used for the body of a message/rfc822 object which had
*no* Content-Type header, as opposed to an unrecognized content-type.
Such a message, technically, does not contain MIME data (it follows only
RFC 822, not RFC 1521.)
This is a container class, and the reason for that is that it loosely
parses the body of the message looking for ``sub-parts'' and then
creates appropriate containers for them.
More specifically, it looks for uuencoded data. It may do more than that
some day.
Basically, the algorithm followed is:
if line is "begin 644 foo.gif"
if there is an open sub-part, close it
add a sub-part with type: image/gif; encoding: x-uue
hand this line to it
and hand subsequent lines to that subpart
else if there is an open uuencoded sub-part, and line is "end"
hand this line to it
close off the uuencoded sub-part
else if there is an open sub-part
hand this line to it
else
open a text/plain subpart
hand this line to it
Adding other types than uuencode to this (for example, PGP) would be
pretty straightforward.
*/
typedef struct MimeUntypedTextClass MimeUntypedTextClass;
typedef struct MimeUntypedText MimeUntypedText;
struct MimeUntypedTextClass {
MimeContainerClass container;
};
extern MimeUntypedTextClass mimeUntypedTextClass;
typedef enum {
MimeUntypedTextSubpartTypeText, /* text/plain */
MimeUntypedTextSubpartTypeUUE, /* uuencoded data */
MimeUntypedTextSubpartTypeYEnc, /* yencoded data */
MimeUntypedTextSubpartTypeBinhex /* Mac BinHex data */
} MimeUntypedTextSubpartType;
struct MimeUntypedText {
MimeContainer container; /* superclass variables */
MimeObject *open_subpart; /* The part still-being-parsed */
MimeUntypedTextSubpartType type; /* What kind of type it is */
MimeHeaders *open_hdrs; /* The faked-up headers describing it */
};
#define MimeUntypedTextClassInitializer(ITYPE,CSUPER) \
{ MimeContainerClassInitializer(ITYPE,CSUPER) }
#endif /* _MIMEUNTY_H_ */

View file

@ -0,0 +1,398 @@
/* -*- 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/. */
#ifndef _LIBMIME_H_
#define _LIBMIME_H_
#ifdef XP_UNIX
#undef Bool
#endif
#include "nsStringGlue.h"
#include "nsMailHeaders.h"
#include "nsIMimeStreamConverter.h"
#include "nsIUnicodeDecoder.h"
#include "nsIUnicodeEncoder.h"
#include "nsIPrefBranch.h"
#include "mozITXTToHTMLConv.h"
#include "nsCOMPtr.h"
#include "modmimee.h" // for MimeConverterOutputCallback
#define MIME_DRAFTS
/* Opaque object describing a block of message headers, and a couple of
routines for extracting data from one.
*/
typedef struct MimeHeaders
{
char *all_headers; /* A char* of the entire header section. */
int32_t all_headers_fp; /* The length (it is not NULL-terminated.) */
int32_t all_headers_size; /* The size of the allocated block. */
bool done_p; /* Whether we've read the end-of-headers marker
(the terminating blank line.) */
char **heads; /* An array of length n_headers which points
to the beginning of each distinct header:
just after the newline which terminated
the previous one. This is to speed search.
This is not initialized until all the
headers have been read.
*/
int32_t heads_size; /* The length (and consequently, how many
distinct headers are in here.) */
char *obuffer; /* This buffer is used for output. */
int32_t obuffer_size;
int32_t obuffer_fp;
char *munged_subject; /* What a hack. This is a place to write down
the subject header, after it's been
charset-ified and stuff. Remembered so that
we can later use it to generate the
<TITLE> tag. (Also works for giving names to RFC822 attachments) */
} MimeHeaders;
class MimeDisplayOptions;
class MimeParseStateObject;
typedef struct MSG_AttachmentData MSG_AttachmentData;
/* Given the name of a header, returns the contents of that header as
a newly-allocated string (which the caller must free.) If the header
is not present, or has no contents, NULL is returned.
If `strip_p' is true, then the data returned will be the first token
of the header; else it will be the full text of the header. (This is
useful for getting just "text/plain" from "text/plain; name=foo".)
If `all_p' is false, then the first header encountered is used, and
any subsequent headers of the same name are ignored. If true, then
all headers of the same name are appended together (this is useful
for gathering up all CC headers into one, for example.)
*/
extern char *MimeHeaders_get(MimeHeaders *hdrs,
const char *header_name,
bool strip_p,
bool all_p);
/* Given a header of the form of the MIME "Content-" headers, extracts a
named parameter from it, if it exists. For example,
MimeHeaders_get_parameter("text/plain; charset=us-ascii", "charset")
would return "us-ascii".
Returns NULL if there is no match, or if there is an allocation failure.
RFC2231 - MIME Parameter Value and Encoded Word Extensions: Character Sets,
Languages, and Continuations
RFC2231 has added the character sets, languages, and continuations mechanism.
charset, and language information may also be returned to the caller.
Note that charset and language should be free()'d while
the return value (parameter) has to be PR_FREE'd.
For example,
MimeHeaders_get_parameter("text/plain; name*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A", "name")
MimeHeaders_get_parameter("text/plain; name*0*=us-ascii'en-us'This%20is%20; CRLFLWSPname*1*=%2A%2A%2Afun%2A%2A%2A", "name")
would return "This is ***fun***" and *charset = "us-ascii", *language = "en-us"
*/
extern char *MimeHeaders_get_parameter (const char *header_value,
const char *parm_name,
char **charset,
char **language);
extern MimeHeaders *MimeHeaders_copy (MimeHeaders *srcHeaders);
extern void MimeHeaders_free (MimeHeaders *hdrs);
typedef enum {
MimeHeadersAll, /* Show all headers */
MimeHeadersSome, /* Show all "interesting" headers */
MimeHeadersSomeNoRef, /* Same, but suppress the `References' header
(for when we're printing this message.) */
MimeHeadersMicro, /* Show a one-line header summary */
MimeHeadersMicroPlus, /* Same, but show the full recipient list as
well (To, CC, etc.) */
MimeHeadersCitation, /* A one-line summary geared toward use in a
reply citation ("So-and-so wrote:") */
MimeHeadersOnly, /* Just parse and output headers...nothing else! */
MimeHeadersNone /* Skip showing any headers */
} MimeHeadersState;
/* The signature for various callbacks in the MimeDisplayOptions structure.
*/
typedef char *(*MimeHTMLGeneratorFunction) (const char *data, void *closure,
MimeHeaders *headers);
class MimeDisplayOptions
{
public:
MimeDisplayOptions();
virtual ~MimeDisplayOptions();
mozITXTToHTMLConv *conv; // For text conversion...
nsCOMPtr<nsIPrefBranch> m_prefBranch; /* prefBranch-service */
nsMimeOutputType format_out; // The format out type
nsCString charsetForCachedInputDecoder;
nsCOMPtr<nsIUnicodeDecoder> m_inputCharsetToUnicodeDecoder;
nsCOMPtr<nsIUnicodeEncoder> m_unicodeToUTF8Encoder;
const char *url; /* Base URL for the document. This string should
be freed by the caller, after the parser
completes (possibly at the same time as the
MimeDisplayOptions itself.) */
MimeHeadersState headers; /* How headers should be displayed. */
bool fancy_headers_p; /* Whether to do clever formatting of headers
using tables, instead of spaces. */
bool output_vcard_buttons_p; /* Whether to output the buttons */
/* on vcards. */
bool variable_width_plaintext_p; /* Whether text/plain messages should
be in variable width, or fixed. */
bool wrap_long_lines_p; /* Whether to wrap long lines in text/plain
messages. */
bool rot13_p; /* Whether text/plain parts should be rotated
Set by "?rot13=true" */
char *part_to_load; /* The particular part of the multipart which
we are extracting. Set by "?part=3.2.4" */
bool no_output_p; /* Will never write output when this is true.
(When false, output or not may depend on other things.)
This needs to be in the options, because the method
MimeObject_parse_begin is controlling the property "output_p"
(on the MimeObject) so we need a way for other functions to
override it and tell that we do not want output. */
bool write_html_p; /* Whether the output should be HTML, or raw. */
bool decrypt_p; /* Whether all traces of xlateion should be
eradicated -- this is only meaningful when
write_html_p is false; we set this when
attaching a message for forwarding, since
forwarding someone else a message that wasn't
xlated for them doesn't work. We have to
dexlate it before sending it.
*/
/* Whether this MIME part is a child of another part (and not top level). */
bool is_child = false;
uint32_t whattodo ; /* from the prefs, we'll get if the user wants to do glyph or structure substitutions and set this member variable. */
char *default_charset; /* If this is non-NULL, then it is the charset to
assume when no other one is specified via a
`charset' parameter.
*/
bool override_charset; /* If this is true, then we will assume that
all data is in the default_charset, regardless
of what the `charset' parameter of that part
says. (This is to cope with the fact that, in
the real world, many messages are mislabelled
with the wrong charset.)
*/
bool force_user_charset; /* this is the new strategy to deal with incorrectly
labeled attachments */
/* =======================================================================
Stream-related callbacks; for these functions, the `closure' argument
is what is found in `options->stream_closure'. (One possible exception
is for output_fn; see "output_closure" below.)
*/
void *stream_closure;
/* For setting up the display stream, so that the MIME parser can inform
the caller of the type of the data it will be getting. */
int (*output_init_fn) (const char *type,
const char *charset,
const char *name,
const char *x_mac_type,
const char *x_mac_creator,
void *stream_closure);
/* How the MIME parser feeds its output (HTML or raw) back to the caller. */
MimeConverterOutputCallback output_fn;
/* Closure to pass to the above output_fn. If NULL, then the
stream_closure is used. */
void *output_closure;
/* A hook for the caller to perform charset-conversion before HTML is
returned. Each set of characters which originated in a mail message
(body or headers) will be run through this filter before being converted
into HTML. (This should return bytes which may appear in an HTML file,
ie, we must be able to scan through the string to search for "<" and
turn it in to "&lt;", and so on.)
`input' is a non-NULL-terminated string of a single line from the message.
`input_length' is how long it is.
`input_charset' is a string representing the charset of this string (as
specified by MIME headers.)
`output_charset' is the charset to which conversion is desired.
`output_ret' is where a newly-malloced string is returned. It may be
NULL if no translation is needed.
`output_size_ret' is how long the returned string is (it need not be
NULL-terminated.).
*/
int (*charset_conversion_fn) (const char *input_line,
int32_t input_length, const char *input_charset,
const char *output_charset,
char **output_ret, int32_t *output_size_ret,
void *stream_closure, nsIUnicodeDecoder *decoder, nsIUnicodeEncoder *encoder);
/* If true, perform both charset-conversion and decoding of
MIME-2 header fields (using RFC-1522 encoding.)
*/
bool rfc1522_conversion_p;
/* A hook for the caller to turn a file name into a content-type. */
char *(*file_type_fn) (const char *filename, void *stream_closure);
/* A hook by which the user may be prompted for a password by the security
library. (This is really of type `SECKEYGetPasswordKey'; see sec.h.) */
void *(*passwd_prompt_fn)(void *arg1, void *arg2);
/* =======================================================================
Various callbacks; for all of these functions, the `closure' argument
is what is found in `html_closure'.
*/
void *html_closure;
/* For emitting some HTML before the start of the outermost message
(this is called before any HTML is written to layout.) */
MimeHTMLGeneratorFunction generate_header_html_fn;
/* For emitting some HTML after the outermost header block, but before
the body of the first message. */
MimeHTMLGeneratorFunction generate_post_header_html_fn;
/* For emitting some HTML at the very end (this is called after libmime
has written everything it's going to write.) */
MimeHTMLGeneratorFunction generate_footer_html_fn;
/* For turning a message ID into a loadable URL. */
MimeHTMLGeneratorFunction generate_reference_url_fn;
/* For turning a mail address into a mailto URL. */
MimeHTMLGeneratorFunction generate_mailto_url_fn;
/* For turning a newsgroup name into a news URL. */
MimeHTMLGeneratorFunction generate_news_url_fn;
/* =======================================================================
Callbacks to handle the backend-specific inlined image display
(internal-external-reconnect junk.) For `image_begin', the `closure'
argument is what is found in `stream_closure'; but for all of the
others, the `closure' argument is the data that `image_begin' returned.
*/
/* Begins processing an embedded image; the URL and content_type are of the
image itself. */
void *(*image_begin) (const char *image_url, const char *content_type,
void *stream_closure);
/* Stop processing an image. */
void (*image_end) (void *image_closure, int status);
/* Dump some raw image data down the stream. */
int (*image_write_buffer) (const char *buf, int32_t size, void *image_closure);
/* What HTML should be dumped out for this image. */
char *(*make_image_html) (void *image_closure);
/* =======================================================================
Other random opaque state.
*/
MimeParseStateObject *state; /* Some state used by libmime internals;
initialize this to 0 and leave it alone.
*/
#ifdef MIME_DRAFTS
/* =======================================================================
Mail Draft hooks -- 09-19-1996
*/
bool decompose_file_p; /* are we decomposing a mime msg
into separate files */
bool done_parsing_outer_headers; /* are we done parsing the outer message
headers; this is really useful when
we have multiple Message/RFC822
headers */
bool is_multipart_msg; /* are we decomposing a multipart
message */
int decompose_init_count; /* used for non multipart message only
*/
bool signed_p; /* to tell draft this is a signed
message */
bool caller_need_root_headers; /* set it to true to receive the message main
headers through the callback
decompose_headers_info_fn */
/* Callback to gather the outer most headers so we could use the
information to initialize the addressing/subject/newsgroups fields
for the composition window. */
int (*decompose_headers_info_fn) (void *closure,
MimeHeaders *headers);
/* Callbacks to create temporary files for drafts attachments. */
int (*decompose_file_init_fn) (void *stream_closure,
MimeHeaders *headers );
MimeConverterOutputCallback decompose_file_output_fn;
int (*decompose_file_close_fn) (void *stream_closure);
#endif /* MIME_DRAFTS */
int32_t attachment_icon_layer_id; /* Hackhackhack. This is zero if we have
not yet emitted the attachment layer
stuff. If we have, then this is the
id number for that layer, which is a
unique random number every time, to keep
evil people from writing javascript code
to hack it. */
bool missing_parts; /* Whether or not this message is going to contain
missing parts (from IMAP Mime Parts On Demand) */
bool show_attachment_inline_p; /* Whether or not we should display attachment inline (whatever say
the content-disposition) */
bool quote_attachment_inline_p; /* Whether or not we should include inlined attachments in
quotes of replies) */
int32_t html_as_p; /* How we should display HTML, which allows us to know if we should display all body parts */
/**
* Should StartBody/EndBody events be generated for nested MimeMessages. If
* false (the default value), the events are only generated for the outermost
* MimeMessage.
*/
bool notify_nested_bodies;
/**
* When true, compels mime parts to only write the actual body
* payload and not display-gunk like links to attachments. This was
* primarily introduced for the benefit of the javascript emitter.
*/
bool write_pure_bodies;
/**
* When true, only processes metadata (i.e. size) for streamed attachments.
* Mime emitters that expect any attachment data (including inline text and
* image attachments) should leave this as false (the default value). At
* the moment, only the JS mime emitter uses this.
*/
bool metadata_only;
};
#endif /* _MODLMIME_H_ */

View file

@ -0,0 +1,56 @@
/* -*- 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/. */
/* -*- Mode: C; tab-width: 4 -*-
mimeenc.c --- MIME encoders and decoders, version 2 (see mimei.h)
Copyright (c) 1996 Netscape Communications Corporation, all rights reserved.
Created: Jamie Zawinski <jwz@netscape.com>, 15-May-96.
*/
#ifndef _MIMEENC_H_
#define _MIMEENC_H_
#include "nsError.h"
#include "nscore.h" // for nullptr
typedef int (*MimeConverterOutputCallback)
(const char *buf, int32_t size, void *closure);
/* This file defines interfaces to generic implementations of Base64,
Quoted-Printable, and UU decoders; and of Base64 and Quoted-Printable
encoders.
*/
/* Opaque objects used by the encoder/decoder to store state. */
typedef struct MimeDecoderData MimeDecoderData;
struct MimeObject;
/* functions for creating that opaque data.
*/
MimeDecoderData *MimeB64DecoderInit(MimeConverterOutputCallback output_fn,
void *closure);
MimeDecoderData *MimeQPDecoderInit (MimeConverterOutputCallback output_fn,
void *closure, MimeObject *object = nullptr);
MimeDecoderData *MimeUUDecoderInit (MimeConverterOutputCallback output_fn,
void *closure);
MimeDecoderData *MimeYDecoderInit (MimeConverterOutputCallback output_fn,
void *closure);
/* Push data through the encoder/decoder, causing the above-provided write_fn
to be called with encoded/decoded data. */
int MimeDecoderWrite (MimeDecoderData *data, const char *buffer, int32_t size,
int32_t *outSize);
/* When you're done encoding/decoding, call this to free the data. If
abort_p is false, then calling this may cause the write_fn to be called
one last time (as the last buffered data is flushed out.)
*/
int MimeDecoderDestroy(MimeDecoderData *data, bool abort_p);
#endif /* _MODMIMEE_H_ */

View file

@ -0,0 +1,92 @@
# vim: set filetype=python:
# 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/.
EXPORTS += [
'mimecont.h',
'mimecryp.h',
'mimecth.h',
'mimehdrs.h',
'mimei.h',
'mimeleaf.h',
'mimemoz2.h',
'mimemsig.h',
'mimemult.h',
'mimeobj.h',
'mimepbuf.h',
'mimetext.h',
'modlmime.h',
'modmimee.h',
'nsMimeStringResources.h',
'nsStreamConverter.h',
]
SOURCES += [
'comi18n.cpp',
'mimebuf.cpp',
'mimecms.cpp',
'mimecom.cpp',
'mimecont.cpp',
'mimecryp.cpp',
'mimecth.cpp',
'mimedrft.cpp',
'mimeebod.cpp',
'mimeenc.cpp',
'mimeeobj.cpp',
'mimehdrs.cpp',
'MimeHeaderParser.cpp',
'mimei.cpp',
'mimeiimg.cpp',
'mimeleaf.cpp',
'mimemalt.cpp',
'mimemapl.cpp',
'mimemcms.cpp',
'mimemdig.cpp',
'mimemmix.cpp',
'mimemoz2.cpp',
'mimempar.cpp',
'mimemrel.cpp',
'mimemsg.cpp',
'mimemsig.cpp',
'mimemult.cpp',
'mimeobj.cpp',
'mimepbuf.cpp',
'mimesun.cpp',
'mimetenr.cpp',
'mimetext.cpp',
'mimeTextHTMLParsed.cpp',
'mimethpl.cpp',
'mimethsa.cpp',
'mimethtm.cpp',
'mimetpfl.cpp',
'mimetpla.cpp',
'mimetric.cpp',
'mimeunty.cpp',
'nsCMS.cpp',
'nsCMSSecureMessage.cpp',
'nsMimeObjectClassAccess.cpp',
'nsSimpleMimeConverterStub.cpp',
'nsStreamConverter.cpp',
]
LOCAL_INCLUDES += [
'/mozilla/security/certverifier',
'/mozilla/security/manager/ssl',
'/mozilla/security/pkix/include',
]
EXTRA_COMPONENTS += [
'mimeJSComponents.js',
'msgMime.manifest',
]
EXTRA_JS_MODULES += [
'extraMimeParsers.jsm',
'jsmime.jsm',
'mimeParser.jsm'
]
FINAL_LIBRARY = 'mail'
DEFINES['ENABLE_SMIME'] = True

View file

@ -0,0 +1,9 @@
component {d1258011-f391-44fd-992e-c6f4b461a42f} mimeJSComponents.js
component {96bd8769-2d0e-4440-963d-22b97fb3ba77} mimeJSComponents.js
component {93f8c049-80ed-4dda-9000-94ad8daba44c} mimeJSComponents.js
component {c560806a-425f-4f0f-bf69-397c58c599a7} mimeJSComponents.js
contract @mozilla.org/messenger/mimeheaders;1 {d1258011-f391-44fd-992e-c6f4b461a42f}
contract @mozilla.org/messenger/mimeconverter;1 {93f8c049-80ed-4dda-9000-94ad8daba44c}
contract @mozilla.org/messenger/headerparser;1 {96bd8769-2d0e-4440-963d-22b97fb3ba77}
contract @mozilla.org/messenger/structuredheaders;1 {c560806a-425f-4f0f-bf69-397c58c599a7}
category custom-mime-encoder A-extra resource:///modules/extraMimeParsers.jsm

966
mailnews/mime/src/nsCMS.cpp Normal file
View file

@ -0,0 +1,966 @@
/* -*- Mode: C++; tab-width: 2; 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 "nsCMS.h"
#include "CertVerifier.h"
#include "CryptoTask.h"
#include "ScopedNSSTypes.h"
#include "cms.h"
#include "mozilla/Logging.h"
#include "mozilla/RefPtr.h"
#include "nsArrayUtils.h"
#include "nsIArray.h"
#include "nsICMSMessageErrors.h"
#include "nsICryptoHash.h"
#include "nsISupports.h"
#include "nsIX509CertDB.h"
#include "nsNSSCertificate.h"
#include "nsNSSComponent.h"
#include "nsNSSHelper.h"
#include "nsServiceManagerUtils.h"
#include "pkix/Result.h"
#include "pkix/pkixtypes.h"
#include "smime.h"
using namespace mozilla;
using namespace mozilla::psm;
using namespace mozilla::pkix;
#ifdef PR_LOGGING
extern mozilla::LazyLogModule gPIPNSSLog;
#endif
NS_IMPL_ISUPPORTS(nsCMSMessage, nsICMSMessage, nsICMSMessage2)
nsCMSMessage::nsCMSMessage()
{
m_cmsMsg = nullptr;
}
nsCMSMessage::nsCMSMessage(NSSCMSMessage *aCMSMsg)
{
m_cmsMsg = aCMSMsg;
}
nsCMSMessage::~nsCMSMessage()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return;
}
destructorSafeDestroyNSSReference();
shutdown(ShutdownCalledFrom::Object);
}
nsresult nsCMSMessage::Init()
{
nsresult rv;
nsCOMPtr<nsISupports> nssInitialized = do_GetService("@mozilla.org/psm;1", &rv);
return rv;
}
void nsCMSMessage::virtualDestroyNSSReference()
{
destructorSafeDestroyNSSReference();
}
void nsCMSMessage::destructorSafeDestroyNSSReference()
{
if (m_cmsMsg) {
NSS_CMSMessage_Destroy(m_cmsMsg);
}
}
NS_IMETHODIMP nsCMSMessage::VerifySignature()
{
return CommonVerifySignature(nullptr, 0);
}
NSSCMSSignerInfo* nsCMSMessage::GetTopLevelSignerInfo()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return nullptr;
if (!m_cmsMsg)
return nullptr;
if (!NSS_CMSMessage_IsSigned(m_cmsMsg))
return nullptr;
NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0);
if (!cinfo)
return nullptr;
NSSCMSSignedData *sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo);
if (!sigd)
return nullptr;
PR_ASSERT(NSS_CMSSignedData_SignerInfoCount(sigd) > 0);
return NSS_CMSSignedData_GetSignerInfo(sigd, 0);
}
NS_IMETHODIMP nsCMSMessage::GetSignerEmailAddress(char * * aEmail)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::GetSignerEmailAddress\n"));
NS_ENSURE_ARG(aEmail);
NSSCMSSignerInfo *si = GetTopLevelSignerInfo();
if (!si)
return NS_ERROR_FAILURE;
*aEmail = NSS_CMSSignerInfo_GetSignerEmailAddress(si);
return NS_OK;
}
NS_IMETHODIMP nsCMSMessage::GetSignerCommonName(char ** aName)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::GetSignerCommonName\n"));
NS_ENSURE_ARG(aName);
NSSCMSSignerInfo *si = GetTopLevelSignerInfo();
if (!si)
return NS_ERROR_FAILURE;
*aName = NSS_CMSSignerInfo_GetSignerCommonName(si);
return NS_OK;
}
NS_IMETHODIMP nsCMSMessage::ContentIsEncrypted(bool *isEncrypted)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::ContentIsEncrypted\n"));
NS_ENSURE_ARG(isEncrypted);
if (!m_cmsMsg)
return NS_ERROR_FAILURE;
*isEncrypted = NSS_CMSMessage_IsEncrypted(m_cmsMsg);
return NS_OK;
}
NS_IMETHODIMP nsCMSMessage::ContentIsSigned(bool *isSigned)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::ContentIsSigned\n"));
NS_ENSURE_ARG(isSigned);
if (!m_cmsMsg)
return NS_ERROR_FAILURE;
*isSigned = NSS_CMSMessage_IsSigned(m_cmsMsg);
return NS_OK;
}
NS_IMETHODIMP nsCMSMessage::GetSignerCert(nsIX509Cert **scert)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
NSSCMSSignerInfo *si = GetTopLevelSignerInfo();
if (!si)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIX509Cert> cert;
if (si->cert) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::GetSignerCert got signer cert\n"));
nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
certdb->ConstructX509(reinterpret_cast<const char *>(si->cert->derCert.data),
si->cert->derCert.len,
getter_AddRefs(cert));
}
else {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::GetSignerCert no signer cert, do we have a cert list? %s\n",
(si->certList ? "yes" : "no") ));
*scert = nullptr;
}
cert.forget(scert);
return NS_OK;
}
NS_IMETHODIMP nsCMSMessage::GetEncryptionCert(nsIX509Cert **ecert)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsCMSMessage::VerifyDetachedSignature(unsigned char* aDigestData, uint32_t aDigestDataLen)
{
if (!aDigestData || !aDigestDataLen)
return NS_ERROR_FAILURE;
return CommonVerifySignature(aDigestData, aDigestDataLen);
}
nsresult nsCMSMessage::CommonVerifySignature(unsigned char* aDigestData, uint32_t aDigestDataLen)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature, content level count %d\n", NSS_CMSMessage_ContentLevelCount(m_cmsMsg)));
NSSCMSContentInfo *cinfo = nullptr;
NSSCMSSignedData *sigd = nullptr;
NSSCMSSignerInfo *si;
int32_t nsigners;
RefPtr<SharedCertVerifier> certVerifier;
nsresult rv = NS_ERROR_FAILURE;
if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - not signed\n"));
return NS_ERROR_CMS_VERIFY_NOT_SIGNED;
}
cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0);
if (cinfo) {
// I don't like this hard cast. We should check in some way, that we really have this type.
sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo);
}
if (!sigd) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - no content info\n"));
rv = NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
goto loser;
}
if (aDigestData && aDigestDataLen)
{
SECItem digest;
digest.data = aDigestData;
digest.len = aDigestDataLen;
if (NSS_CMSSignedData_SetDigestValue(sigd, SEC_OID_SHA1, &digest)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - bad digest\n"));
rv = NS_ERROR_CMS_VERIFY_BAD_DIGEST;
goto loser;
}
}
// Import certs. Note that import failure is not a signature verification failure. //
if (NSS_CMSSignedData_ImportCerts(sigd, CERT_GetDefaultCertDB(), certUsageEmailRecipient, true) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - can not import certs\n"));
}
nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
PR_ASSERT(nsigners > 0);
NS_ENSURE_TRUE(nsigners > 0, NS_ERROR_UNEXPECTED);
si = NSS_CMSSignedData_GetSignerInfo(sigd, 0);
// See bug 324474. We want to make sure the signing cert is
// still valid at the current time.
certVerifier = GetDefaultCertVerifier();
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
{
UniqueCERTCertList builtChain;
mozilla::pkix::Result result =
certVerifier->VerifyCert(si->cert,
certificateUsageEmailSigner,
Now(),
nullptr /*XXX pinarg*/,
nullptr /*hostname*/,
builtChain);
if (result != mozilla::pkix::Success) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("nsCMSMessage::CommonVerifySignature - signing cert not trusted now\n"));
rv = NS_ERROR_CMS_VERIFY_UNTRUSTED;
goto loser;
}
}
// We verify the first signer info, only //
// XXX: NSS_CMSSignedData_VerifySignerInfo calls CERT_VerifyCert, which
// requires NSS's certificate verification configuration to be done in
// order to work well (e.g. honoring OCSP preferences and proxy settings
// for OCSP requests), but Gecko stopped doing that configuration. Something
// similar to what was done for Gecko bug 1028643 needs to be done here too.
if (NSS_CMSSignedData_VerifySignerInfo(sigd, 0, CERT_GetDefaultCertDB(), certUsageEmailSigner) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - unable to verify signature\n"));
if (NSSCMSVS_SigningCertNotFound == si->verificationStatus) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - signing cert not found\n"));
rv = NS_ERROR_CMS_VERIFY_NOCERT;
}
else if(NSSCMSVS_SigningCertNotTrusted == si->verificationStatus) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - signing cert not trusted at signing time\n"));
rv = NS_ERROR_CMS_VERIFY_UNTRUSTED;
}
else if(NSSCMSVS_Unverified == si->verificationStatus) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - can not verify\n"));
rv = NS_ERROR_CMS_VERIFY_ERROR_UNVERIFIED;
}
else if(NSSCMSVS_ProcessingError == si->verificationStatus) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - processing error\n"));
rv = NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
else if(NSSCMSVS_BadSignature == si->verificationStatus) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - bad signature\n"));
rv = NS_ERROR_CMS_VERIFY_BAD_SIGNATURE;
}
else if(NSSCMSVS_DigestMismatch == si->verificationStatus) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - digest mismatch\n"));
rv = NS_ERROR_CMS_VERIFY_DIGEST_MISMATCH;
}
else if(NSSCMSVS_SignatureAlgorithmUnknown == si->verificationStatus) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - algo unknown\n"));
rv = NS_ERROR_CMS_VERIFY_UNKNOWN_ALGO;
}
else if(NSSCMSVS_SignatureAlgorithmUnsupported == si->verificationStatus) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - algo not supported\n"));
rv = NS_ERROR_CMS_VERIFY_UNSUPPORTED_ALGO;
}
else if(NSSCMSVS_MalformedSignature == si->verificationStatus) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - malformed signature\n"));
rv = NS_ERROR_CMS_VERIFY_MALFORMED_SIGNATURE;
}
goto loser;
}
// Save the profile. Note that save import failure is not a signature verification failure. //
if (NSS_SMIMESignerInfo_SaveSMIMEProfile(si) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CommonVerifySignature - unable to save smime profile\n"));
}
rv = NS_OK;
loser:
return rv;
}
NS_IMETHODIMP nsCMSMessage::AsyncVerifySignature(
nsISMimeVerificationListener *aListener)
{
return CommonAsyncVerifySignature(aListener, nullptr, 0);
}
NS_IMETHODIMP nsCMSMessage::AsyncVerifyDetachedSignature(
nsISMimeVerificationListener *aListener,
unsigned char* aDigestData, uint32_t aDigestDataLen)
{
if (!aDigestData || !aDigestDataLen)
return NS_ERROR_FAILURE;
return CommonAsyncVerifySignature(aListener, aDigestData, aDigestDataLen);
}
class SMimeVerificationTask final : public CryptoTask
{
public:
SMimeVerificationTask(nsICMSMessage *aMessage,
nsISMimeVerificationListener *aListener,
unsigned char *aDigestData, uint32_t aDigestDataLen)
{
MOZ_ASSERT(NS_IsMainThread());
mMessage = aMessage;
mListener = aListener;
mDigestData.Assign(reinterpret_cast<char *>(aDigestData), aDigestDataLen);
}
private:
virtual void ReleaseNSSResources() override {}
virtual nsresult CalculateResult() override
{
MOZ_ASSERT(!NS_IsMainThread());
nsresult rv;
if (!mDigestData.IsEmpty()) {
rv = mMessage->VerifyDetachedSignature(
reinterpret_cast<uint8_t*>(const_cast<char *>(mDigestData.get())),
mDigestData.Length());
} else {
rv = mMessage->VerifySignature();
}
return rv;
}
virtual void CallCallback(nsresult rv) override
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsICMSMessage2> m2 = do_QueryInterface(mMessage);
mListener->Notify(m2, rv);
}
nsCOMPtr<nsICMSMessage> mMessage;
nsCOMPtr<nsISMimeVerificationListener> mListener;
nsCString mDigestData;
};
nsresult nsCMSMessage::CommonAsyncVerifySignature(nsISMimeVerificationListener *aListener,
unsigned char* aDigestData, uint32_t aDigestDataLen)
{
RefPtr<CryptoTask> task = new SMimeVerificationTask(this, aListener, aDigestData, aDigestDataLen);
return task->Dispatch("SMimeVerify");
}
class nsZeroTerminatedCertArray : public nsNSSShutDownObject
{
public:
nsZeroTerminatedCertArray()
:mCerts(nullptr), mPoolp(nullptr), mSize(0)
{
}
~nsZeroTerminatedCertArray()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return;
}
destructorSafeDestroyNSSReference();
shutdown(ShutdownCalledFrom::Object);
}
void virtualDestroyNSSReference()
{
destructorSafeDestroyNSSReference();
}
void destructorSafeDestroyNSSReference()
{
if (mCerts)
{
for (uint32_t i=0; i < mSize; i++) {
if (mCerts[i]) {
CERT_DestroyCertificate(mCerts[i]);
}
}
}
if (mPoolp)
PORT_FreeArena(mPoolp, false);
}
bool allocate(uint32_t count)
{
// only allow allocation once
if (mPoolp)
return false;
mSize = count;
if (!mSize)
return false;
mPoolp = PORT_NewArena(1024);
if (!mPoolp)
return false;
mCerts = (CERTCertificate**)PORT_ArenaZAlloc(
mPoolp, (count+1)*sizeof(CERTCertificate*));
if (!mCerts)
return false;
// null array, including zero termination
for (uint32_t i = 0; i < count+1; i++) {
mCerts[i] = nullptr;
}
return true;
}
void set(uint32_t i, CERTCertificate *c)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return;
if (i >= mSize)
return;
if (mCerts[i]) {
CERT_DestroyCertificate(mCerts[i]);
}
mCerts[i] = CERT_DupCertificate(c);
}
CERTCertificate *get(uint32_t i)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return nullptr;
if (i >= mSize)
return nullptr;
return CERT_DupCertificate(mCerts[i]);
}
CERTCertificate **getRawArray()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return nullptr;
return mCerts;
}
private:
CERTCertificate **mCerts;
PLArenaPool *mPoolp;
uint32_t mSize;
};
NS_IMETHODIMP nsCMSMessage::CreateEncrypted(nsIArray * aRecipientCerts)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateEncrypted\n"));
NSSCMSContentInfo *cinfo;
NSSCMSEnvelopedData *envd;
NSSCMSRecipientInfo *recipientInfo;
nsZeroTerminatedCertArray recipientCerts;
SECOidTag bulkAlgTag;
int keySize;
uint32_t i;
nsresult rv = NS_ERROR_FAILURE;
// Check the recipient certificates //
uint32_t recipientCertCount;
aRecipientCerts->GetLength(&recipientCertCount);
PR_ASSERT(recipientCertCount > 0);
if (!recipientCerts.allocate(recipientCertCount)) {
goto loser;
}
for (i=0; i<recipientCertCount; i++) {
nsCOMPtr<nsIX509Cert> x509cert = do_QueryElementAt(aRecipientCerts, i);
if (!x509cert)
return NS_ERROR_FAILURE;
UniqueCERTCertificate c(x509cert->GetCert());
recipientCerts.set(i, c.get());
}
// Find a bulk key algorithm //
if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientCerts.getRawArray(), &bulkAlgTag,
&keySize) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateEncrypted - can't find bulk alg for recipients\n"));
rv = NS_ERROR_CMS_ENCRYPT_NO_BULK_ALG;
goto loser;
}
m_cmsMsg = NSS_CMSMessage_Create(nullptr);
if (!m_cmsMsg) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateEncrypted - can't create new cms message\n"));
rv = NS_ERROR_OUT_OF_MEMORY;
goto loser;
}
if ((envd = NSS_CMSEnvelopedData_Create(m_cmsMsg, bulkAlgTag, keySize)) == nullptr) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateEncrypted - can't create enveloped data\n"));
goto loser;
}
cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg);
if (NSS_CMSContentInfo_SetContent_EnvelopedData(m_cmsMsg, cinfo, envd) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateEncrypted - can't create content enveloped data\n"));
goto loser;
}
cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd);
if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nullptr, false) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateEncrypted - can't set content data\n"));
goto loser;
}
// Create and attach recipient information //
for (i=0; i < recipientCertCount; i++) {
UniqueCERTCertificate rc(recipientCerts.get(i));
if ((recipientInfo = NSS_CMSRecipientInfo_Create(m_cmsMsg, rc.get())) == nullptr) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateEncrypted - can't create recipient info\n"));
goto loser;
}
if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientInfo) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateEncrypted - can't add recipient info\n"));
goto loser;
}
}
return NS_OK;
loser:
if (m_cmsMsg) {
NSS_CMSMessage_Destroy(m_cmsMsg);
m_cmsMsg = nullptr;
}
return rv;
}
NS_IMETHODIMP
nsCMSMessage::CreateSigned(nsIX509Cert* aSigningCert, nsIX509Cert* aEncryptCert,
unsigned char* aDigestData, uint32_t aDigestDataLen,
int16_t aDigestType)
{
NS_ENSURE_ARG(aSigningCert);
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned\n"));
NSSCMSContentInfo *cinfo;
NSSCMSSignedData *sigd;
NSSCMSSignerInfo *signerinfo;
UniqueCERTCertificate scert(aSigningCert->GetCert());
UniqueCERTCertificate ecert;
nsresult rv = NS_ERROR_FAILURE;
if (!scert) {
return NS_ERROR_FAILURE;
}
if (aEncryptCert) {
ecert = UniqueCERTCertificate(aEncryptCert->GetCert());
}
SECOidTag digestType;
switch (aDigestType) {
case nsICryptoHash::SHA1:
digestType = SEC_OID_SHA1;
break;
case nsICryptoHash::SHA256:
digestType = SEC_OID_SHA256;
break;
case nsICryptoHash::SHA384:
digestType = SEC_OID_SHA384;
break;
case nsICryptoHash::SHA512:
digestType = SEC_OID_SHA512;
break;
default:
return NS_ERROR_INVALID_ARG;
}
/*
* create the message object
*/
m_cmsMsg = NSS_CMSMessage_Create(nullptr); /* create a message on its own pool */
if (!m_cmsMsg) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't create new message\n"));
rv = NS_ERROR_OUT_OF_MEMORY;
goto loser;
}
/*
* build chain of objects: message->signedData->data
*/
if ((sigd = NSS_CMSSignedData_Create(m_cmsMsg)) == nullptr) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't create signed data\n"));
goto loser;
}
cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg);
if (NSS_CMSContentInfo_SetContent_SignedData(m_cmsMsg, cinfo, sigd)
!= SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't set content signed data\n"));
goto loser;
}
cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
/* we're always passing data in and detaching optionally */
if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nullptr, true)
!= SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't set content data\n"));
goto loser;
}
/*
* create & attach signer information
*/
signerinfo = NSS_CMSSignerInfo_Create(m_cmsMsg, scert.get(), digestType);
if (!signerinfo) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't create signer info\n"));
goto loser;
}
/* we want the cert chain included for this one */
if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain,
certUsageEmailSigner)
!= SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't include signer cert chain\n"));
goto loser;
}
if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now())
!= SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't add signing time\n"));
goto loser;
}
if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't add smime caps\n"));
goto loser;
}
if (ecert) {
if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ecert.get(),
CERT_GetDefaultCertDB())
!= SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't add smime enc key prefs\n"));
goto loser;
}
if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ecert.get(),
CERT_GetDefaultCertDB())
!= SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't add MS smime enc key prefs\n"));
goto loser;
}
// If signing and encryption cert are identical, don't add it twice.
bool addEncryptionCert =
(ecert && (!scert || !CERT_CompareCerts(ecert.get(), scert.get())));
if (addEncryptionCert &&
NSS_CMSSignedData_AddCertificate(sigd, ecert.get()) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't add own encryption certificate\n"));
goto loser;
}
}
if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't add signer info\n"));
goto loser;
}
// Finally, add the pre-computed digest if passed in
if (aDigestData) {
SECItem digest;
digest.data = aDigestData;
digest.len = aDigestDataLen;
if (NSS_CMSSignedData_SetDigestValue(sigd, digestType, &digest) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned - can't set digest value\n"));
goto loser;
}
}
return NS_OK;
loser:
if (m_cmsMsg) {
NSS_CMSMessage_Destroy(m_cmsMsg);
m_cmsMsg = nullptr;
}
return rv;
}
NS_IMPL_ISUPPORTS(nsCMSDecoder, nsICMSDecoder)
nsCMSDecoder::nsCMSDecoder()
: m_dcx(nullptr)
{
}
nsCMSDecoder::~nsCMSDecoder()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return;
}
destructorSafeDestroyNSSReference();
shutdown(ShutdownCalledFrom::Object);
}
nsresult nsCMSDecoder::Init()
{
nsresult rv;
nsCOMPtr<nsISupports> nssInitialized = do_GetService("@mozilla.org/psm;1", &rv);
return rv;
}
void nsCMSDecoder::virtualDestroyNSSReference()
{
destructorSafeDestroyNSSReference();
}
void nsCMSDecoder::destructorSafeDestroyNSSReference()
{
if (m_dcx) {
NSS_CMSDecoder_Cancel(m_dcx);
m_dcx = nullptr;
}
}
/* void start (in NSSCMSContentCallback cb, in voidPtr arg); */
NS_IMETHODIMP nsCMSDecoder::Start(NSSCMSContentCallback cb, void * arg)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSDecoder::Start\n"));
m_ctx = new PipUIContext();
m_dcx = NSS_CMSDecoder_Start(0, cb, arg, 0, m_ctx, 0, 0);
if (!m_dcx) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSDecoder::Start - can't start decoder\n"));
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/* void update (in string bug, in long len); */
NS_IMETHODIMP nsCMSDecoder::Update(const char *buf, int32_t len)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSDecoder::Update\n"));
NSS_CMSDecoder_Update(m_dcx, (char *)buf, len);
return NS_OK;
}
/* void finish (); */
NS_IMETHODIMP nsCMSDecoder::Finish(nsICMSMessage ** aCMSMsg)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSDecoder::Finish\n"));
NSSCMSMessage *cmsMsg;
cmsMsg = NSS_CMSDecoder_Finish(m_dcx);
m_dcx = nullptr;
if (cmsMsg) {
nsCMSMessage *obj = new nsCMSMessage(cmsMsg);
// The NSS object cmsMsg still carries a reference to the context
// we gave it on construction.
// Make sure the context will live long enough.
obj->referenceContext(m_ctx);
*aCMSMsg = obj;
NS_ADDREF(*aCMSMsg);
}
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsCMSEncoder, nsICMSEncoder)
nsCMSEncoder::nsCMSEncoder()
: m_ecx(nullptr)
{
}
nsCMSEncoder::~nsCMSEncoder()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return;
}
destructorSafeDestroyNSSReference();
shutdown(ShutdownCalledFrom::Object);
}
nsresult nsCMSEncoder::Init()
{
nsresult rv;
nsCOMPtr<nsISupports> nssInitialized = do_GetService("@mozilla.org/psm;1", &rv);
return rv;
}
void nsCMSEncoder::virtualDestroyNSSReference()
{
destructorSafeDestroyNSSReference();
}
void nsCMSEncoder::destructorSafeDestroyNSSReference()
{
if (m_ecx)
NSS_CMSEncoder_Cancel(m_ecx);
}
/* void start (); */
NS_IMETHODIMP nsCMSEncoder::Start(nsICMSMessage *aMsg, NSSCMSContentCallback cb, void * arg)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSEncoder::Start\n"));
nsCMSMessage *cmsMsg = static_cast<nsCMSMessage*>(aMsg);
m_ctx = new PipUIContext();
m_ecx = NSS_CMSEncoder_Start(cmsMsg->getCMS(), cb, arg, 0, 0, 0, m_ctx, 0, 0, 0, 0);
if (!m_ecx) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSEncoder::Start - can't start encoder\n"));
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/* void update (in string aBuf, in long aLen); */
NS_IMETHODIMP nsCMSEncoder::Update(const char *aBuf, int32_t aLen)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSEncoder::Update\n"));
if (!m_ecx || NSS_CMSEncoder_Update(m_ecx, aBuf, aLen) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSEncoder::Update - can't update encoder\n"));
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/* void finish (); */
NS_IMETHODIMP nsCMSEncoder::Finish()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
nsresult rv = NS_OK;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSEncoder::Finish\n"));
if (!m_ecx || NSS_CMSEncoder_Finish(m_ecx) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSEncoder::Finish - can't finish encoder\n"));
rv = NS_ERROR_FAILURE;
}
m_ecx = nullptr;
return rv;
}
/* void encode (in nsICMSMessage aMsg); */
NS_IMETHODIMP nsCMSEncoder::Encode(nsICMSMessage *aMsg)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSEncoder::Encode\n"));
return NS_ERROR_NOT_IMPLEMENTED;
}

104
mailnews/mime/src/nsCMS.h Normal file
View file

@ -0,0 +1,104 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef __NS_CMS_H__
#define __NS_CMS_H__
#include "nsISupports.h"
#include "nsCOMPtr.h"
#include "nsXPIDLString.h"
#include "nsIInterfaceRequestor.h"
#include "nsICMSMessage.h"
#include "nsICMSMessage2.h"
#include "nsIX509Cert.h"
#include "nsICMSEncoder.h"
#include "nsICMSDecoder.h"
#include "sechash.h"
#include "cms.h"
#include "nsNSSShutDown.h"
#define NS_CMSMESSAGE_CID \
{ 0xa4557478, 0xae16, 0x11d5, { 0xba,0x4b,0x00,0x10,0x83,0x03,0xb1,0x17 } }
class nsCMSMessage : public nsICMSMessage,
public nsICMSMessage2,
public nsNSSShutDownObject
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICMSMESSAGE
NS_DECL_NSICMSMESSAGE2
nsCMSMessage();
nsCMSMessage(NSSCMSMessage* aCMSMsg);
nsresult Init();
void referenceContext(nsIInterfaceRequestor* aContext) {m_ctx = aContext;}
NSSCMSMessage* getCMS() {return m_cmsMsg;}
private:
virtual ~nsCMSMessage();
nsCOMPtr<nsIInterfaceRequestor> m_ctx;
NSSCMSMessage * m_cmsMsg;
NSSCMSSignerInfo* GetTopLevelSignerInfo();
nsresult CommonVerifySignature(unsigned char* aDigestData, uint32_t aDigestDataLen);
nsresult CommonAsyncVerifySignature(nsISMimeVerificationListener *aListener,
unsigned char* aDigestData, uint32_t aDigestDataLen);
virtual void virtualDestroyNSSReference() override;
void destructorSafeDestroyNSSReference();
};
// ===============================================
// nsCMSDecoder - implementation of nsICMSDecoder
// ===============================================
#define NS_CMSDECODER_CID \
{ 0x9dcef3a4, 0xa3bc, 0x11d5, { 0xba, 0x47, 0x00, 0x10, 0x83, 0x03, 0xb1, 0x17 } }
class nsCMSDecoder : public nsICMSDecoder,
public nsNSSShutDownObject
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICMSDECODER
nsCMSDecoder();
nsresult Init();
private:
virtual ~nsCMSDecoder();
nsCOMPtr<nsIInterfaceRequestor> m_ctx;
NSSCMSDecoderContext *m_dcx;
virtual void virtualDestroyNSSReference() override;
void destructorSafeDestroyNSSReference();
};
// ===============================================
// nsCMSEncoder - implementation of nsICMSEncoder
// ===============================================
#define NS_CMSENCODER_CID \
{ 0xa15789aa, 0x8903, 0x462b, { 0x81, 0xe9, 0x4a, 0xa2, 0xcf, 0xf4, 0xd5, 0xcb } }
class nsCMSEncoder : public nsICMSEncoder,
public nsNSSShutDownObject
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICMSENCODER
nsCMSEncoder();
nsresult Init();
private:
virtual ~nsCMSEncoder();
nsCOMPtr<nsIInterfaceRequestor> m_ctx;
NSSCMSEncoderContext *m_ecx;
virtual void virtualDestroyNSSReference() override;
void destructorSafeDestroyNSSReference();
};
#endif

View file

@ -0,0 +1,363 @@
/* -*- Mode: C++; tab-width: 2; 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 "nsXPIDLString.h"
#include "nsCOMPtr.h"
#include "nsISupports.h"
#include "nsIInterfaceRequestor.h"
#include "nsCRT.h"
#include "nsIX509CertDB.h"
#include "nsICMSSecureMessage.h"
#include "nsCMSSecureMessage.h"
#include "nsIX509Cert.h"
#include "nsNSSHelper.h"
#include "nsNSSCertificate.h"
#include "nsNSSShutDown.h"
#include <string.h>
#include "plbase64.h"
#include "cert.h"
#include "cms.h"
#include "nsIServiceManager.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "mozilla/Logging.h"
#ifdef PR_LOGGING
extern mozilla::LazyLogModule gPIPNSSLog;
#endif
using namespace mozilla;
// Standard ISupports implementation
// NOTE: Should these be the thread-safe versions?
/*****
* nsCMSSecureMessage
*****/
// Standard ISupports implementation
NS_IMPL_ISUPPORTS(nsCMSSecureMessage, nsICMSSecureMessage)
// nsCMSSecureMessage constructor
nsCMSSecureMessage::nsCMSSecureMessage()
{
// initialize superclass
}
// nsCMSMessage destructor
nsCMSSecureMessage::~nsCMSSecureMessage()
{
}
nsresult nsCMSSecureMessage::Init()
{
nsresult rv;
nsCOMPtr<nsISupports> nssInitialized = do_GetService("@mozilla.org/psm;1", &rv);
return rv;
}
/* string getCertByPrefID (in string certID); */
NS_IMETHODIMP nsCMSSecureMessage::
GetCertByPrefID(const char *certID, char **_retval)
{
nsNSSShutDownPreventionLock locker;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::GetCertByPrefID\n"));
nsresult rv = NS_OK;
CERTCertificate *cert = 0;
nsXPIDLCString nickname;
nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
*_retval = 0;
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
goto done;
}
rv = prefs->GetCharPref(certID,
getter_Copies(nickname));
if (NS_FAILED(rv)) goto done;
/* Find a good cert in the user's database */
cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), const_cast<char*>(nickname.get()),
certUsageEmailRecipient, true, ctx);
if (!cert) {
/* Success, but no value */
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::GetCertByPrefID - can't find user cert\n"));
goto done;
}
/* Convert the DER to a BASE64 String */
encode(cert->derCert.data, cert->derCert.len, _retval);
done:
if (cert) CERT_DestroyCertificate(cert);
return rv;
}
// nsCMSSecureMessage::DecodeCert
nsresult nsCMSSecureMessage::
DecodeCert(const char *value, nsIX509Cert ** _retval)
{
nsNSSShutDownPreventionLock locker;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::DecodeCert\n"));
nsresult rv = NS_OK;
int32_t length;
unsigned char *data = 0;
*_retval = 0;
if (!value) { return NS_ERROR_FAILURE; }
rv = decode(value, &data, &length);
if (NS_FAILED(rv)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::DecodeCert - can't decode cert\n"));
return rv;
}
nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
if (!certdb) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIX509Cert> cert;
certdb->ConstructX509(reinterpret_cast<char *>(data), length, getter_AddRefs(cert));
if (cert) {
*_retval = cert;
NS_ADDREF(*_retval);
}
else {
rv = NS_ERROR_FAILURE;
}
free((char*)data);
return rv;
}
// nsCMSSecureMessage::SendMessage
nsresult nsCMSSecureMessage::
SendMessage(const char *msg, const char *base64Cert, char ** _retval)
{
nsNSSShutDownPreventionLock locker;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage\n"));
nsresult rv = NS_OK;
CERTCertificate *cert = 0;
NSSCMSMessage *cmsMsg = 0;
unsigned char *certDER = 0;
int32_t derLen;
NSSCMSEnvelopedData *env;
NSSCMSContentInfo *cinfo;
NSSCMSRecipientInfo *rcpt;
SECItem output;
PLArenaPool *arena = PORT_NewArena(1024);
SECStatus s;
nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
/* Step 0. Create a CMS Message */
cmsMsg = NSS_CMSMessage_Create(nullptr);
if (!cmsMsg) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't create NSSCMSMessage\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
/* Step 1. Import the certificate into NSS */
rv = decode(base64Cert, &certDER, &derLen);
if (NS_FAILED(rv)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't decode / import cert into NSS\n"));
goto done;
}
cert = CERT_DecodeCertFromPackage((char *)certDER, derLen);
if (!cert) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't decode cert from package\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
/* Step 2. Get a signature cert */
/* Step 3. Build inner (signature) content */
/* Step 4. Build outer (enveloped) content */
env = NSS_CMSEnvelopedData_Create(cmsMsg, SEC_OID_DES_EDE3_CBC, 0);
if (!env) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't create envelope data\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
cinfo = NSS_CMSEnvelopedData_GetContentInfo(env);
s = NSS_CMSContentInfo_SetContent_Data(cmsMsg, cinfo, 0, false);
if (s != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't set content data\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
rcpt = NSS_CMSRecipientInfo_Create(cmsMsg, cert);
if (!rcpt) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't create recipient info\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
s = NSS_CMSEnvelopedData_AddRecipient(env, rcpt);
if (s != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't add recipient\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
/* Step 5. Add content to message */
cinfo = NSS_CMSMessage_GetContentInfo(cmsMsg);
s = NSS_CMSContentInfo_SetContent_EnvelopedData(cmsMsg, cinfo, env);
if (s != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't set content enveloped data\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
/* Step 6. Encode */
NSSCMSEncoderContext *ecx;
output.data = 0; output.len = 0;
ecx = NSS_CMSEncoder_Start(cmsMsg, 0, 0, &output, arena,
0, ctx, 0, 0, 0, 0);
if (!ecx) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't start cms encoder\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
s = NSS_CMSEncoder_Update(ecx, msg, strlen(msg));
if (s != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't update encoder\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
s = NSS_CMSEncoder_Finish(ecx);
if (s != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't finish encoder\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
/* Step 7. Base64 encode and return the result */
rv = encode(output.data, output.len, _retval);
done:
if (certDER) free((char *)certDER);
if (cert) CERT_DestroyCertificate(cert);
if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg);
if (arena) PORT_FreeArena(arena, false); /* false? */
return rv;
}
/*
* nsCMSSecureMessage::ReceiveMessage
*/
nsresult nsCMSSecureMessage::
ReceiveMessage(const char *msg, char **_retval)
{
nsNSSShutDownPreventionLock locker;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::ReceiveMessage\n"));
nsresult rv = NS_OK;
NSSCMSDecoderContext *dcx;
unsigned char *der = 0;
int32_t derLen;
NSSCMSMessage *cmsMsg = 0;
SECItem *content;
nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
/* Step 1. Decode the base64 wrapper */
rv = decode(msg, &der, &derLen);
if (NS_FAILED(rv)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::ReceiveMessage - can't base64 decode\n"));
goto done;
}
dcx = NSS_CMSDecoder_Start(0, 0, 0, /* pw */ 0, ctx, /* key */ 0, 0);
if (!dcx) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::ReceiveMessage - can't start decoder\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
(void)NSS_CMSDecoder_Update(dcx, (char *)der, derLen);
cmsMsg = NSS_CMSDecoder_Finish(dcx);
if (!cmsMsg) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::ReceiveMessage - can't finish decoder\n"));
rv = NS_ERROR_FAILURE;
/* Memory leak on dcx?? */
goto done;
}
content = NSS_CMSMessage_GetContent(cmsMsg);
if (!content) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::ReceiveMessage - can't get content\n"));
rv = NS_ERROR_FAILURE;
goto done;
}
/* Copy the data */
*_retval = (char*)malloc(content->len+1);
memcpy(*_retval, content->data, content->len);
(*_retval)[content->len] = 0;
done:
if (der) free(der);
if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg);
return rv;
}
nsresult nsCMSSecureMessage::
encode(const unsigned char *data, int32_t dataLen, char **_retval)
{
nsresult rv = NS_OK;
*_retval = PL_Base64Encode((const char *)data, dataLen, nullptr);
if (!*_retval) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; }
loser:
return rv;
}
nsresult nsCMSSecureMessage::
decode(const char *data, unsigned char **result, int32_t * _retval)
{
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::decode\n"));
nsresult rv = NS_OK;
uint32_t len = strlen(data);
int adjust = 0;
/* Compute length adjustment */
if (data[len-1] == '=') {
adjust++;
if (data[len-2] == '=') adjust++;
}
*result = (unsigned char *)PL_Base64Decode(data, len, nullptr);
if (!*result) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::decode - error decoding base64\n"));
rv = NS_ERROR_ILLEGAL_VALUE;
goto loser;
}
*_retval = (len*3)/4 - adjust;
loser:
return rv;
}

View file

@ -0,0 +1,37 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef _NSCMSSECUREMESSAGE_H_
#define _NSCMSSECUREMESSAGE_H_
#include "nsICMSSecureMessage.h"
#include "cms.h"
// ===============================================
// nsCMSManager - implementation of nsICMSManager
// ===============================================
#define NS_CMSSECUREMESSAGE_CID \
{ 0x5fb907e0, 0x1dd2, 0x11b2, { 0xa7, 0xc0, 0xf1, 0x4c, 0x41, 0x6a, 0x62, 0xa1 } }
class nsCMSSecureMessage
: public nsICMSSecureMessage
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICMSSECUREMESSAGE
nsCMSSecureMessage();
nsresult Init();
private:
virtual ~nsCMSSecureMessage();
NS_METHOD encode(const unsigned char *data, int32_t dataLen, char **_retval);
NS_METHOD decode(const char *data, unsigned char **result, int32_t * _retval);
};
#endif /* _NSCMSMESSAGE_H_ */

View file

@ -0,0 +1,97 @@
/* -*- Mode: C++; tab-width: 2; 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 "mimecom.h"
#include "nscore.h"
#include "nsMimeObjectClassAccess.h"
/*
* The following macros actually implement addref, release and
* query interface for our component.
*/
NS_IMPL_ISUPPORTS(nsMimeObjectClassAccess, nsIMimeObjectClassAccess)
/*
* nsMimeObjectClassAccess definitions....
*/
/*
* Inherited methods for nsMimeObjectClassAccess
*/
nsMimeObjectClassAccess::nsMimeObjectClassAccess()
{
}
nsMimeObjectClassAccess::~nsMimeObjectClassAccess()
{
}
nsresult
nsMimeObjectClassAccess::MimeObjectWrite(void *mimeObject,
char *data,
int32_t length,
bool user_visible_p)
{
int rc = XPCOM_MimeObject_write(mimeObject, data, length, user_visible_p);
NS_ENSURE_FALSE(rc < 0, NS_ERROR_FAILURE);
return NS_OK;
}
nsresult
nsMimeObjectClassAccess::GetmimeInlineTextClass(void **ptr)
{
*ptr = XPCOM_GetmimeInlineTextClass();
return NS_OK;
}
nsresult
nsMimeObjectClassAccess::GetmimeLeafClass(void **ptr)
{
*ptr = XPCOM_GetmimeLeafClass();
return NS_OK;
}
nsresult
nsMimeObjectClassAccess::GetmimeObjectClass(void **ptr)
{
*ptr = XPCOM_GetmimeObjectClass();
return NS_OK;
}
nsresult
nsMimeObjectClassAccess::GetmimeContainerClass(void **ptr)
{
*ptr = XPCOM_GetmimeContainerClass();
return NS_OK;
}
nsresult
nsMimeObjectClassAccess::GetmimeMultipartClass(void **ptr)
{
*ptr = XPCOM_GetmimeMultipartClass();
return NS_OK;
}
nsresult
nsMimeObjectClassAccess::GetmimeMultipartSignedClass(void **ptr)
{
*ptr = XPCOM_GetmimeMultipartSignedClass();
return NS_OK;
}
nsresult
nsMimeObjectClassAccess::GetmimeEncryptedClass(void **ptr)
{
*ptr = XPCOM_GetmimeEncryptedClass();
return NS_OK;
}
nsresult
nsMimeObjectClassAccess::MimeCreate(char * content_type, void * hdrs, void * opts, void **ptr)
{
*ptr = XPCOM_Mime_create(content_type, hdrs, opts);
return NS_OK;
}

View file

@ -0,0 +1,52 @@
/* -*- 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/. */
/*
* This interface is implemented by libmime. This interface is used by
* a Content-Type handler "Plug In" (i.e. vCard) for accessing various
* internal information about the object class system of libmime. When
* libmime progresses to a C++ object class, this would probably change.
*/
#ifndef nsMimeObjectClassAccess_h_
#define nsMimeObjectClassAccess_h_
#include "mozilla/Attributes.h"
#include "nsISupports.h"
#include "nsIMimeObjectClassAccess.h"
class nsMimeObjectClassAccess : public nsIMimeObjectClassAccess {
public:
nsMimeObjectClassAccess();
/* this macro defines QueryInterface, AddRef and Release for this class */
NS_DECL_ISUPPORTS
// These methods are all implemented by libmime to be used by
// content type handler plugins for processing stream data.
// This is the write call for outputting processed stream data.
NS_IMETHOD MimeObjectWrite(void *mimeObject,
char *data,
int32_t length,
bool user_visible_p) override;
// The following group of calls expose the pointers for the object
// system within libmime.
NS_IMETHOD GetmimeInlineTextClass(void **ptr) override;
NS_IMETHOD GetmimeLeafClass(void **ptr) override;
NS_IMETHOD GetmimeObjectClass(void **ptr) override;
NS_IMETHOD GetmimeContainerClass(void **ptr) override;
NS_IMETHOD GetmimeMultipartClass(void **ptr) override;
NS_IMETHOD GetmimeMultipartSignedClass(void **ptr) override;
NS_IMETHOD GetmimeEncryptedClass(void **ptr) override;
NS_IMETHOD MimeCreate(char *content_type, void * hdrs,
void * opts, void**ptr) override;
private:
virtual ~nsMimeObjectClassAccess();
};
#endif /* nsMimeObjectClassAccess_h_ */

View file

@ -0,0 +1,40 @@
/* 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/. */
#ifndef _NAME_OF_THIS_HEADER_FILE__
#define _NAME_OF_THIS_HEADER_FILE__
/* Note that the negative values are not actually strings: they are error
* codes masquerading as strings. Do not pass them to MimeGetStringByID()
* expecting to get anything back for your trouble.
*/
#define MIME_OUT_OF_MEMORY -1000
#define MIME_UNABLE_TO_OPEN_TMP_FILE -1001
#define MIME_ERROR_WRITING_FILE -1002
#define MIME_MHTML_SUBJECT 1000
#define MIME_MHTML_RESENT_COMMENTS 1001
#define MIME_MHTML_RESENT_DATE 1002
#define MIME_MHTML_RESENT_SENDER 1003
#define MIME_MHTML_RESENT_FROM 1004
#define MIME_MHTML_RESENT_TO 1005
#define MIME_MHTML_RESENT_CC 1006
#define MIME_MHTML_DATE 1007
#define MIME_MHTML_SENDER 1008
#define MIME_MHTML_FROM 1009
#define MIME_MHTML_REPLY_TO 1010
#define MIME_MHTML_ORGANIZATION 1011
#define MIME_MHTML_TO 1012
#define MIME_MHTML_CC 1013
#define MIME_MHTML_NEWSGROUPS 1014
#define MIME_MHTML_FOLLOWUP_TO 1015
#define MIME_MHTML_REFERENCES 1016
#define MIME_MHTML_MESSAGE_ID 1021
#define MIME_MHTML_BCC 1023
#define MIME_MSG_LINK_TO_DOCUMENT 1026
#define MIME_MSG_DOCUMENT_INFO 1027
#define MIME_MSG_ATTACHMENT 1028
#define MIME_MSG_DEFAULT_ATTACHMENT_NAME 1040
#define MIME_FORWARDED_MESSAGE_HTML_USER_WROTE 1041
#endif /* _NAME_OF_THIS_HEADER_FILE__ */

View file

@ -0,0 +1,209 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "mimecth.h"
#include "mimeobj.h"
#include "mimetext.h"
#include "mimemoz2.h"
#include "mimecom.h"
#include "nsStringGlue.h"
#include "nsComponentManagerUtils.h"
#include "nsICategoryManager.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsISimpleMimeConverter.h"
#include "nsServiceManagerUtils.h"
#include "nsSimpleMimeConverterStub.h"
typedef struct MimeSimpleStub MimeSimpleStub;
typedef struct MimeSimpleStubClass MimeSimpleStubClass;
struct MimeSimpleStubClass {
MimeInlineTextClass text;
};
struct MimeSimpleStub {
MimeInlineText text;
nsCString *buffer;
nsCOMPtr<nsISimpleMimeConverter> innerScriptable;
};
#define MimeSimpleStubClassInitializer(ITYPE,CSUPER) \
{ MimeInlineTextClassInitializer(ITYPE,CSUPER) }
MimeDefClass(MimeSimpleStub, MimeSimpleStubClass, mimeSimpleStubClass, NULL);
static int
BeginGather(MimeObject *obj)
{
MimeSimpleStub *ssobj = (MimeSimpleStub *)obj;
int status = ((MimeObjectClass *)XPCOM_GetmimeLeafClass())->parse_begin(obj);
if (status < 0)
return status;
if (!obj->output_p ||
!obj->options ||
!obj->options->write_html_p) {
return 0;
}
ssobj->buffer->Truncate();
return 0;
}
static int
GatherLine(const char *line, int32_t length, MimeObject *obj)
{
MimeSimpleStub *ssobj = (MimeSimpleStub *)obj;
if (!obj->output_p ||
!obj->options ||
!obj->options->output_fn) {
return 0;
}
if (!obj->options->write_html_p)
return MimeObject_write(obj, line, length, true);
ssobj->buffer->Append(line);
return 0;
}
static int
EndGather(MimeObject *obj, bool abort_p)
{
MimeSimpleStub *ssobj = (MimeSimpleStub *)obj;
if (obj->closed_p)
return 0;
int status = ((MimeObjectClass *)MIME_GetmimeInlineTextClass())->parse_eof(obj, abort_p);
if (status < 0)
return status;
if (ssobj->buffer->IsEmpty())
return 0;
mime_stream_data *msd = (mime_stream_data *) (obj->options->stream_closure);
nsIChannel *channel = msd->channel; // note the lack of ref counting...
if (channel)
{
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
ssobj->innerScriptable->SetUri(uri);
}
nsCString asHTML;
nsresult rv = ssobj->innerScriptable->ConvertToHTML(nsDependentCString(obj->content_type),
*ssobj->buffer,
asHTML);
if (NS_FAILED(rv)) {
NS_ASSERTION(NS_SUCCEEDED(rv), "converter failure");
return -1;
}
// MimeObject_write wants a non-const string for some reason, but it doesn't mutate it
status = MimeObject_write(obj, asHTML.get(),
asHTML.Length(), true);
if (status < 0)
return status;
return 0;
}
static int
Initialize(MimeObject *obj)
{
MimeSimpleStub *ssobj = (MimeSimpleStub *)obj;
nsresult rv;
nsCOMPtr<nsICategoryManager> catman =
do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
if (NS_FAILED(rv))
return -1;
nsAutoCString contentType; // lowercase
ToLowerCase(nsDependentCString(obj->content_type), contentType);
nsCString value;
rv = catman->GetCategoryEntry(NS_SIMPLEMIMECONVERTERS_CATEGORY,
contentType.get(), getter_Copies(value));
if (NS_FAILED(rv) || value.IsEmpty())
return -1;
ssobj->innerScriptable = do_CreateInstance(value.get(), &rv);
if (NS_FAILED(rv) || !ssobj->innerScriptable)
return -1;
ssobj->buffer = new nsCString();
((MimeObjectClass *)XPCOM_GetmimeLeafClass())->initialize(obj);
return 0;
}
static void
Finalize(MimeObject *obj)
{
MimeSimpleStub *ssobj = (MimeSimpleStub *)obj;
ssobj->innerScriptable = nullptr;
delete ssobj->buffer;
}
static int
MimeSimpleStubClassInitialize(MimeSimpleStubClass *clazz)
{
MimeObjectClass *oclass = (MimeObjectClass *)clazz;
oclass->parse_begin = BeginGather;
oclass->parse_line = GatherLine;
oclass->parse_eof = EndGather;
oclass->initialize = Initialize;
oclass->finalize = Finalize;
return 0;
}
class nsSimpleMimeConverterStub : public nsIMimeContentTypeHandler
{
public:
nsSimpleMimeConverterStub(const char *aContentType) : mContentType(aContentType) { }
NS_DECL_ISUPPORTS
NS_IMETHOD GetContentType(char **contentType) override
{
*contentType = ToNewCString(mContentType);
return *contentType ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHOD CreateContentTypeHandlerClass(const char *contentType,
contentTypeHandlerInitStruct *initString,
MimeObjectClass **objClass) override;
private:
virtual ~nsSimpleMimeConverterStub() { }
nsCString mContentType;
};
NS_IMPL_ISUPPORTS(nsSimpleMimeConverterStub, nsIMimeContentTypeHandler)
NS_IMETHODIMP
nsSimpleMimeConverterStub::CreateContentTypeHandlerClass(const char *contentType,
contentTypeHandlerInitStruct *initStruct,
MimeObjectClass **objClass)
{
NS_ENSURE_ARG_POINTER(objClass);
*objClass = (MimeObjectClass *)&mimeSimpleStubClass;
(*objClass)->superclass = (MimeObjectClass *)XPCOM_GetmimeInlineTextClass();
NS_ENSURE_TRUE((*objClass)->superclass, NS_ERROR_UNEXPECTED);
initStruct->force_inline_display = true;
return NS_OK;;
}
nsresult
MIME_NewSimpleMimeConverterStub(const char *aContentType,
nsIMimeContentTypeHandler **aResult)
{
RefPtr<nsSimpleMimeConverterStub> inst = new nsSimpleMimeConverterStub(aContentType);
NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY);
return CallQueryInterface(inst.get(), aResult);
}

View file

@ -0,0 +1,13 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
#ifndef NS_SIMPLE_MIME_CONVERTER_STUB_H_
#define NS_SIMPLE_MIME_CONVERTER_STUB_H_
nsresult
MIME_NewSimpleMimeConverterStub(const char *aContentType,
nsIMimeContentTypeHandler **aResult);
#endif /* NS_SIMPLE_MIME_CONVERTER_STUB_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,88 @@
/* -*- 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/. */
#ifndef nsStreamConverter_h_
#define nsStreamConverter_h_
#include "nsIStreamConverter.h"
#include "nsIMimeStreamConverter.h"
#include "nsIMimeEmitter.h"
#include "nsIURI.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsIChannel.h"
#include "nsStringGlue.h"
#include "nsCOMPtr.h"
#define MIME_FORWARD_HTML_PREFIX "<HTML><BODY><BR><BR>"
class nsStreamConverter : public nsIStreamConverter, public nsIMimeStreamConverter {
public:
nsStreamConverter();
NS_DECL_THREADSAFE_ISUPPORTS
// nsIMimeStreamConverter support
NS_DECL_NSIMIMESTREAMCONVERTER
// nsIStreamConverter methods
NS_DECL_NSISTREAMCONVERTER
// nsIStreamListener methods
NS_DECL_NSISTREAMLISTENER
// nsIRequestObserver methods
NS_DECL_NSIREQUESTOBSERVER
////////////////////////////////////////////////////////////////////////////
// nsStreamConverter specific methods:
////////////////////////////////////////////////////////////////////////////
NS_IMETHOD Init(nsIURI *aURI, nsIStreamListener * aOutListener, nsIChannel *aChannel);
NS_IMETHOD GetContentType(char **aOutputContentType);
NS_IMETHOD InternalCleanup(void);
NS_IMETHOD DetermineOutputFormat(const char *url, nsMimeOutputType *newType);
NS_IMETHOD FirePendingStartRequest(void);
private:
virtual ~nsStreamConverter();
nsresult Close();
// the input and output streams form a pipe...they need to be passed around together..
nsCOMPtr<nsIAsyncOutputStream> mOutputStream; // output stream
nsCOMPtr<nsIAsyncInputStream> mInputStream;
nsCOMPtr<nsIStreamListener> mOutListener; // output stream listener
nsCOMPtr<nsIChannel> mOutgoingChannel;
nsCOMPtr<nsIMimeEmitter> mEmitter; // emitter being used...
nsCOMPtr<nsIURI> mURI; // URI being processed
nsMimeOutputType mOutputType; // the output type we should use for the operation
bool mAlreadyKnowOutputType;
void *mBridgeStream; // internal libmime data stream
// Type of output, entire message, header only, body only
nsCString mOutputFormat;
nsCString mRealContentType; // if we know the content type for real, this will be set (used by attachments)
nsCString mOverrideFormat; // this is a possible override for emitter creation
bool mWrapperOutput; // Should we output the frame split message display
nsCOMPtr<nsIMimeStreamConverterListener> mMimeStreamConverterListener;
bool mForwardInline;
bool mForwardInlineFilter;
bool mOverrideComposeFormat;
nsString mForwardToAddress;
nsCOMPtr<nsIMsgIdentity> mIdentity;
nsCString mOriginalMsgURI;
nsCOMPtr<nsIMsgDBHdr> mOrigMsgHdr;
nsCString mFromType;
nsCString mToType;
#ifdef DEBUG_mscott
PRTime mConvertContentTime;
#endif
nsIRequest * mPendingRequest; // used when we need to delay to fire onStartRequest
nsISupports * mPendingContext; // used when we need to delay to fire onStartRequest
};
#endif /* nsStreamConverter_h_ */