mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-08 16:27:31 +09:00
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
This commit is contained in:
parent
23e0d82436
commit
e400f4130a
1564 changed files with 510348 additions and 0 deletions
229
mailnews/mime/src/MimeHeaderParser.cpp
Normal file
229
mailnews/mime/src/MimeHeaderParser.cpp
Normal 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
|
||||
108
mailnews/mime/src/comi18n.cpp
Normal file
108
mailnews/mime/src/comi18n.cpp
Normal 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
|
||||
|
||||
42
mailnews/mime/src/comi18n.h
Normal file
42
mailnews/mime/src/comi18n.h
Normal 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_
|
||||
|
||||
29
mailnews/mime/src/extraMimeParsers.jsm
Normal file
29
mailnews/mime/src/extraMimeParsers.jsm
Normal 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);
|
||||
90
mailnews/mime/src/jsmime.jsm
Normal file
90
mailnews/mime/src/jsmime.jsm
Normal 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");
|
||||
}
|
||||
7
mailnews/mime/src/mime.def
Normal file
7
mailnews/mime/src/mime.def
Normal 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
|
||||
512
mailnews/mime/src/mimeJSComponents.js
Normal file
512
mailnews/mime/src/mimeJSComponents.js
Normal 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);
|
||||
258
mailnews/mime/src/mimeParser.jsm
Normal file
258
mailnews/mime/src/mimeParser.jsm
Normal 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";
|
||||
}
|
||||
},
|
||||
};
|
||||
150
mailnews/mime/src/mimeTextHTMLParsed.cpp
Normal file
150
mailnews/mime/src/mimeTextHTMLParsed.cpp
Normal 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;
|
||||
}
|
||||
28
mailnews/mime/src/mimeTextHTMLParsed.h
Normal file
28
mailnews/mime/src/mimeTextHTMLParsed.h
Normal 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_ */
|
||||
249
mailnews/mime/src/mimebuf.cpp
Normal file
249
mailnews/mime/src/mimebuf.cpp
Normal 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;
|
||||
}
|
||||
39
mailnews/mime/src/mimebuf.h
Normal file
39
mailnews/mime/src/mimebuf.h
Normal 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_ */
|
||||
716
mailnews/mime/src/mimecms.cpp
Normal file
716
mailnews/mime/src/mimecms.cpp
Normal 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;
|
||||
}
|
||||
|
||||
36
mailnews/mime/src/mimecms.h
Normal file
36
mailnews/mime/src/mimecms.h
Normal 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_ */
|
||||
74
mailnews/mime/src/mimecom.cpp
Normal file
74
mailnews/mime/src/mimecom.cpp
Normal 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);
|
||||
}
|
||||
38
mailnews/mime/src/mimecom.h
Normal file
38
mailnews/mime/src/mimecom.h
Normal 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_ */
|
||||
218
mailnews/mime/src/mimecont.cpp
Normal file
218
mailnews/mime/src/mimecont.cpp
Normal 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
|
||||
43
mailnews/mime/src/mimecont.h
Normal file
43
mailnews/mime/src/mimecont.h
Normal 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_ */
|
||||
571
mailnews/mime/src/mimecryp.cpp
Normal file
571
mailnews/mime/src/mimecryp.cpp
Normal 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;
|
||||
}
|
||||
140
mailnews/mime/src/mimecryp.h
Normal file
140
mailnews/mime/src/mimecryp.h
Normal 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_ */
|
||||
51
mailnews/mime/src/mimecth.cpp
Normal file
51
mailnews/mime/src/mimecth.cpp
Normal 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
135
mailnews/mime/src/mimecth.h
Normal 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_ */
|
||||
2084
mailnews/mime/src/mimedrft.cpp
Normal file
2084
mailnews/mime/src/mimedrft.cpp
Normal file
File diff suppressed because it is too large
Load diff
509
mailnews/mime/src/mimeebod.cpp
Normal file
509
mailnews/mime/src/mimeebod.cpp
Normal 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;
|
||||
}
|
||||
37
mailnews/mime/src/mimeebod.h
Normal file
37
mailnews/mime/src/mimeebod.h
Normal 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_ */
|
||||
1107
mailnews/mime/src/mimeenc.cpp
Normal file
1107
mailnews/mime/src/mimeenc.cpp
Normal file
File diff suppressed because it is too large
Load diff
236
mailnews/mime/src/mimeeobj.cpp
Normal file
236
mailnews/mime/src/mimeeobj.cpp
Normal 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;
|
||||
}
|
||||
34
mailnews/mime/src/mimeeobj.h
Normal file
34
mailnews/mime/src/mimeeobj.h
Normal 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_ */
|
||||
399
mailnews/mime/src/mimefilt.cpp
Normal file
399
mailnews/mime/src/mimefilt.cpp
Normal 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);
|
||||
}
|
||||
888
mailnews/mime/src/mimehdrs.cpp
Normal file
888
mailnews/mime/src/mimehdrs.cpp
Normal 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;
|
||||
}
|
||||
88
mailnews/mime/src/mimehdrs.h
Normal file
88
mailnews/mime/src/mimehdrs.h
Normal 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
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
422
mailnews/mime/src/mimei.h
Normal 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_ */
|
||||
249
mailnews/mime/src/mimeiimg.cpp
Normal file
249
mailnews/mime/src/mimeiimg.cpp
Normal 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;
|
||||
}
|
||||
35
mailnews/mime/src/mimeiimg.h
Normal file
35
mailnews/mime/src/mimeiimg.h
Normal 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_ */
|
||||
221
mailnews/mime/src/mimeleaf.cpp
Normal file
221
mailnews/mime/src/mimeleaf.cpp
Normal 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;
|
||||
}
|
||||
59
mailnews/mime/src/mimeleaf.h
Normal file
59
mailnews/mime/src/mimeleaf.h
Normal 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_ */
|
||||
580
mailnews/mime/src/mimemalt.cpp
Normal file
580
mailnews/mime/src/mimemalt.cpp
Normal 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;
|
||||
}
|
||||
48
mailnews/mime/src/mimemalt.h
Normal file
48
mailnews/mime/src/mimemalt.h
Normal 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_ */
|
||||
189
mailnews/mime/src/mimemapl.cpp
Normal file
189
mailnews/mime/src/mimemapl.cpp
Normal 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;
|
||||
}
|
||||
32
mailnews/mime/src/mimemapl.h
Normal file
32
mailnews/mime/src/mimemapl.h
Normal 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_ */
|
||||
470
mailnews/mime/src/mimemcms.cpp
Normal file
470
mailnews/mime/src/mimemcms.cpp
Normal 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;
|
||||
}
|
||||
35
mailnews/mime/src/mimemcms.h
Normal file
35
mailnews/mime/src/mimemcms.h
Normal 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_ */
|
||||
24
mailnews/mime/src/mimemdig.cpp
Normal file
24
mailnews/mime/src/mimemdig.cpp
Normal 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;
|
||||
}
|
||||
33
mailnews/mime/src/mimemdig.h
Normal file
33
mailnews/mime/src/mimemdig.h
Normal 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_ */
|
||||
21
mailnews/mime/src/mimemmix.cpp
Normal file
21
mailnews/mime/src/mimemmix.cpp
Normal 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;
|
||||
}
|
||||
32
mailnews/mime/src/mimemmix.h
Normal file
32
mailnews/mime/src/mimemmix.h
Normal 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_ */
|
||||
2211
mailnews/mime/src/mimemoz2.cpp
Normal file
2211
mailnews/mime/src/mimemoz2.cpp
Normal file
File diff suppressed because it is too large
Load diff
196
mailnews/mime/src/mimemoz2.h
Normal file
196
mailnews/mime/src/mimemoz2.h
Normal 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_ */
|
||||
|
||||
21
mailnews/mime/src/mimempar.cpp
Normal file
21
mailnews/mime/src/mimempar.cpp
Normal 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;
|
||||
}
|
||||
32
mailnews/mime/src/mimempar.h
Normal file
32
mailnews/mime/src/mimempar.h
Normal 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_ */
|
||||
1199
mailnews/mime/src/mimemrel.cpp
Normal file
1199
mailnews/mime/src/mimemrel.cpp
Normal file
File diff suppressed because it is too large
Load diff
66
mailnews/mime/src/mimemrel.h
Normal file
66
mailnews/mime/src/mimemrel.h
Normal 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_ */
|
||||
977
mailnews/mime/src/mimemsg.cpp
Normal file
977
mailnews/mime/src/mimemsg.cpp
Normal 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
|
||||
43
mailnews/mime/src/mimemsg.h
Normal file
43
mailnews/mime/src/mimemsg.h
Normal 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_ */
|
||||
775
mailnews/mime/src/mimemsig.cpp
Normal file
775
mailnews/mime/src/mimemsig.cpp
Normal 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;
|
||||
}
|
||||
134
mailnews/mime/src/mimemsig.h
Normal file
134
mailnews/mime/src/mimemsig.h
Normal 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_ */
|
||||
748
mailnews/mime/src/mimemult.cpp
Normal file
748
mailnews/mime/src/mimemult.cpp
Normal 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
|
||||
102
mailnews/mime/src/mimemult.h
Normal file
102
mailnews/mime/src/mimemult.h
Normal 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_ */
|
||||
327
mailnews/mime/src/mimeobj.cpp
Normal file
327
mailnews/mime/src/mimeobj.cpp
Normal 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
186
mailnews/mime/src/mimeobj.h
Normal 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_ */
|
||||
296
mailnews/mime/src/mimepbuf.cpp
Normal file
296
mailnews/mime/src/mimepbuf.cpp
Normal 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;
|
||||
}
|
||||
|
||||
64
mailnews/mime/src/mimepbuf.h
Normal file
64
mailnews/mime/src/mimepbuf.h
Normal 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_ */
|
||||
342
mailnews/mime/src/mimesun.cpp
Normal file
342
mailnews/mime/src/mimesun.cpp
Normal 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);
|
||||
}
|
||||
59
mailnews/mime/src/mimesun.h
Normal file
59
mailnews/mime/src/mimesun.h
Normal 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_ */
|
||||
28
mailnews/mime/src/mimetenr.cpp
Normal file
28
mailnews/mime/src/mimetenr.cpp
Normal 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;
|
||||
}
|
||||
32
mailnews/mime/src/mimetenr.h
Normal file
32
mailnews/mime/src/mimetenr.h
Normal 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_ */
|
||||
544
mailnews/mime/src/mimetext.cpp
Normal file
544
mailnews/mime/src/mimetext.cpp
Normal 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;
|
||||
}
|
||||
82
mailnews/mime/src/mimetext.h
Normal file
82
mailnews/mime/src/mimetext.h
Normal 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 "<", 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_ */
|
||||
165
mailnews/mime/src/mimethpl.cpp
Normal file
165
mailnews/mime/src/mimethpl.cpp
Normal 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;
|
||||
}
|
||||
35
mailnews/mime/src/mimethpl.h
Normal file
35
mailnews/mime/src/mimethpl.h
Normal 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_ */
|
||||
143
mailnews/mime/src/mimethsa.cpp
Normal file
143
mailnews/mime/src/mimethsa.cpp
Normal 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;
|
||||
}
|
||||
28
mailnews/mime/src/mimethsa.h
Normal file
28
mailnews/mime/src/mimethsa.h
Normal 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_ */
|
||||
254
mailnews/mime/src/mimethtm.cpp
Normal file
254
mailnews/mime/src/mimethtm.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
mailnews/mime/src/mimethtm.h
Normal file
35
mailnews/mime/src/mimethtm.h
Normal 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_ */
|
||||
630
mailnews/mime/src/mimetpfl.cpp
Normal file
630
mailnews/mime/src/mimetpfl.cpp
Normal 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 += "-- <br>";
|
||||
} else {
|
||||
exdata->isSig = true;
|
||||
preface += "<div class=\"moz-txt-sig\"><span class=\"moz-txt-tag\">"
|
||||
"-- <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 <
|
||||
if ('<' == a_current_char) {
|
||||
*a_in_tag = true;
|
||||
*a_in_quote_in_tag = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts whitespace to | |, 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 .
|
||||
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(" ");
|
||||
}
|
||||
|
||||
while(number_of_space--) {
|
||||
// a_out_string += ' '; gives error
|
||||
a_out_string.AppendLiteral(" ");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes over the line and converts whitespace to | |, 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, "e_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;
|
||||
}
|
||||
52
mailnews/mime/src/mimetpfl.h
Normal file
52
mailnews/mime/src/mimetpfl.h
Normal 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_ */
|
||||
451
mailnews/mime/src/mimetpla.cpp
Normal file
451
mailnews/mime/src/mimetpla.cpp
Normal 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;
|
||||
}
|
||||
|
||||
39
mailnews/mime/src/mimetpla.h
Normal file
39
mailnews/mime/src/mimetpla.h
Normal 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_ */
|
||||
353
mailnews/mime/src/mimetric.cpp
Normal file
353
mailnews/mime/src/mimetric.cpp
Normal 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, "&", outlen);
|
||||
addedlen = strlen(out);
|
||||
outlen -= addedlen;
|
||||
out += addedlen;
|
||||
}
|
||||
else if (*this_start == '>')
|
||||
{
|
||||
PL_strncpyz (out, ">", 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, "<", 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 = "<", 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;
|
||||
}
|
||||
33
mailnews/mime/src/mimetric.h
Normal file
33
mailnews/mime/src/mimetric.h
Normal 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_ */
|
||||
588
mailnews/mime/src/mimeunty.cpp
Normal file
588
mailnews/mime/src/mimeunty.cpp
Normal 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;
|
||||
}
|
||||
70
mailnews/mime/src/mimeunty.h
Normal file
70
mailnews/mime/src/mimeunty.h
Normal 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_ */
|
||||
398
mailnews/mime/src/modlmime.h
Normal file
398
mailnews/mime/src/modlmime.h
Normal 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 "<", 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_ */
|
||||
56
mailnews/mime/src/modmimee.h
Normal file
56
mailnews/mime/src/modmimee.h
Normal 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_ */
|
||||
92
mailnews/mime/src/moz.build
Normal file
92
mailnews/mime/src/moz.build
Normal 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
|
||||
9
mailnews/mime/src/msgMime.manifest
Normal file
9
mailnews/mime/src/msgMime.manifest
Normal 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
966
mailnews/mime/src/nsCMS.cpp
Normal 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
104
mailnews/mime/src/nsCMS.h
Normal 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
|
||||
363
mailnews/mime/src/nsCMSSecureMessage.cpp
Normal file
363
mailnews/mime/src/nsCMSSecureMessage.cpp
Normal 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;
|
||||
}
|
||||
37
mailnews/mime/src/nsCMSSecureMessage.h
Normal file
37
mailnews/mime/src/nsCMSSecureMessage.h
Normal 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_ */
|
||||
97
mailnews/mime/src/nsMimeObjectClassAccess.cpp
Normal file
97
mailnews/mime/src/nsMimeObjectClassAccess.cpp
Normal 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;
|
||||
}
|
||||
52
mailnews/mime/src/nsMimeObjectClassAccess.h
Normal file
52
mailnews/mime/src/nsMimeObjectClassAccess.h
Normal 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_ */
|
||||
40
mailnews/mime/src/nsMimeStringResources.h
Normal file
40
mailnews/mime/src/nsMimeStringResources.h
Normal 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__ */
|
||||
209
mailnews/mime/src/nsSimpleMimeConverterStub.cpp
Normal file
209
mailnews/mime/src/nsSimpleMimeConverterStub.cpp
Normal 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);
|
||||
}
|
||||
13
mailnews/mime/src/nsSimpleMimeConverterStub.h
Normal file
13
mailnews/mime/src/nsSimpleMimeConverterStub.h
Normal 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_ */
|
||||
1157
mailnews/mime/src/nsStreamConverter.cpp
Normal file
1157
mailnews/mime/src/nsStreamConverter.cpp
Normal file
File diff suppressed because it is too large
Load diff
88
mailnews/mime/src/nsStreamConverter.h
Normal file
88
mailnews/mime/src/nsStreamConverter.h
Normal 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_ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue