diff --git a/mozilla/intl/uconv/idl/Makefile.in b/mozilla/intl/uconv/idl/Makefile.in index b6e037bbef1..b0c2846f6d2 100644 --- a/mozilla/intl/uconv/idl/Makefile.in +++ b/mozilla/intl/uconv/idl/Makefile.in @@ -34,6 +34,7 @@ XPIDLSRCS = \ nsICurrentCharsetListener.idl \ nsICharsetConverterManager.idl \ nsIScriptableUConv.idl \ + nsIUTF8ConverterService.idl \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/mozilla/intl/uconv/idl/nsIUTF8ConverterService.idl b/mozilla/intl/uconv/idl/nsIUTF8ConverterService.idl new file mode 100644 index 00000000000..f757bf6a609 --- /dev/null +++ b/mozilla/intl/uconv/idl/nsIUTF8ConverterService.idl @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Jungshik Shin . + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s: + * Naoki Hotta + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +[scriptable, uuid(249f52a3-2599-4b00-ba40-0481364831a2)] +interface nsIUTF8ConverterService : nsISupports +{ + /** + * Ensure that |aString| is encoded in UTF-8. If not, + * convert to UTF-8 assuming it's encoded in |aCharset| + * and return the converted string in UTF-8. + * + * @param aString a string to ensure its UTF8ness + * @param aCharset the charset to convert from if |aString| is not in UTF-8 + * @param aSkipCheck determines whether or not to skip 'ASCIIness' and + * 'UTF8ness' check. Set this to PR_TRUE only if you suspect that + * aString can be mistaken for ASCII / UTF-8 but is actually NOT + * in ASCII / UTF-8 so that aString has to go through the conversion. + * skipping ASCIIness/UTF8ness check. + * The most common case is the input is in 7bit non-ASCII charsets + * like ISO-2022-JP, HZ or UTF-7 (in its original form or + * a modified form used in IMAP folder names). + * @return the converted string in UTF-8. + * @throws NS_ERROR_UCONV_NOCONV when there is no decoder for aCharset + * or error code of nsIUnicodeDecoder in case of conversion failure + */ + + AUTF8String convertStringToUTF8(in ACString aString, + in string aCharset, + in boolean aSkipCheck); + +/* XXX : To-be-added. convertStringFromUTF8 */ + + /** + * Ensure that |aSpec| (after URL-unescaping it) is encoded in UTF-8. + * If not, convert it to UTF-8, assuming it's encoded in |aCharset|, + * and return the result. + * + *

Make sure that all characters outside US-ASCII in your input spec + * are url-escaped if your spec is not in UTF-8 (before url-escaping) + * because the presence of non-ASCII characters is blindly + * regarded as an indication that your input spec is in unescaped UTF-8 + * and it will be returned without further processing. No valid spec + * going around in Mozilla code would break this assumption. + * + *

XXX The above may change in the future depending on the usage pattern. + * + * @param aSpec an url-escaped URI spec to ensure its UTF8ness + * @param aCharset the charset to convert from if |aSpec| is not in UTF-8 + * @return the converted spec in UTF-8. + * @throws NS_ERROR_UCONV_NOCONV when there is no decoder for aCharset + * or error code of nsIUnicodeDecoder in case of conversion failure + */ + + AUTF8String convertURISpecToUTF8(in ACString aSpec, + in string aCharset); +}; + diff --git a/mozilla/intl/uconv/public/Makefile.in b/mozilla/intl/uconv/public/Makefile.in index a0f9971bbe4..62e626528b9 100644 --- a/mozilla/intl/uconv/public/Makefile.in +++ b/mozilla/intl/uconv/public/Makefile.in @@ -40,6 +40,7 @@ EXPORTS = \ nsIConverterInputStream.h \ uconvutil.h \ nsEncoderDecoderUtils.h \ + nsUConvCID.h \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/mozilla/intl/uconv/public/nsUConvCID.h b/mozilla/intl/uconv/public/nsUConvCID.h new file mode 100644 index 00000000000..b5e95f29f29 --- /dev/null +++ b/mozilla/intl/uconv/public/nsUConvCID.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + **/ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Jungshik Shin . + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// I am taking the first step in the hope that this file will become 'the' +// repository of #defines for CID's and CONTRACTID's of implementations of +// all interfaces in nsUConvModule and be included by any file that wants +// to refers to one or more of implementations by CID/CONTRACTID. +// see bug 162765 comment #33. + +// {2b026890-5a2e-4981-ada2-a600358947b4} +#define NS_UTF8CONVERTERSERVICE_CID { 0x2b026890, 0x5a2e, 0x4981, \ + { 0xad, 0xa2, 0xa6, 0x00, 0x35, 0x89, 0x47, 0xb4 } } +#define NS_UTF8CONVERTERSERVICE_CONTRACTID "@mozilla.org/intl/utf8converterservice;1" + diff --git a/mozilla/intl/uconv/src/Makefile.in b/mozilla/intl/uconv/src/Makefile.in index e049f4f3a4c..aeb673b4b0d 100644 --- a/mozilla/intl/uconv/src/Makefile.in +++ b/mozilla/intl/uconv/src/Makefile.in @@ -61,6 +61,7 @@ CPPSRCS = \ nsTextToSubURI.cpp \ nsURLProperties.cpp \ nsCharsetConverterManager.cpp \ + nsUTF8ConverterService.cpp \ $(NULL) ifndef MOZ_USE_NATIVE_UCONV diff --git a/mozilla/intl/uconv/src/nsUConvModule.cpp b/mozilla/intl/uconv/src/nsUConvModule.cpp index ae6ce8af8b5..d0ac14fa049 100644 --- a/mozilla/intl/uconv/src/nsUConvModule.cpp +++ b/mozilla/intl/uconv/src/nsUConvModule.cpp @@ -50,9 +50,11 @@ #include "nsIServiceManager.h" +#include "nsUConvCID.h" #include "nsCharsetConverterManager.h" #include "nsCharsetAlias.h" #include "nsTextToSubURI.h" +#include "nsUTF8ConverterService.h" #include "nsConverterInputStream.h" #include "nsPlatformCharset.h" @@ -677,6 +679,7 @@ nsConverterManagerDataRegister(nsIComponentManager* aCompMgr, NS_GENERIC_FACTORY_CONSTRUCTOR(nsCharsetConverterManager) NS_GENERIC_FACTORY_CONSTRUCTOR(nsTextToSubURI) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF8ConverterService) NS_GENERIC_FACTORY_CONSTRUCTOR(nsCharsetAlias2) NS_GENERIC_FACTORY_CONSTRUCTOR(nsConverterInputStream) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPlatformCharset, Init) @@ -726,6 +729,11 @@ static const nsModuleComponentInfo components[] = NS_UNICODEENCODEHELPER_CONTRACTID, nsUnicodeEncodeHelperConstructor }, + { + "Converter to/from UTF8 with charset", NS_UTF8CONVERTERSERVICE_CID, + NS_UTF8CONVERTERSERVICE_CONTRACTID, + nsUTF8ConverterServiceConstructor + }, { "Unicode Encoder / Decoder for Script", NS_ISCRIPTABLEUNICODECONVERTER_CID, NS_ISCRIPTABLEUNICODECONVERTER_CONTRACTID, diff --git a/mozilla/intl/uconv/src/nsUTF8ConverterService.cpp b/mozilla/intl/uconv/src/nsUTF8ConverterService.cpp new file mode 100644 index 00000000000..5df103c390d --- /dev/null +++ b/mozilla/intl/uconv/src/nsUTF8ConverterService.cpp @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=4: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developers of the Original Code are + * Naoki Hotta and Jungshik Shin . + * Portions created by the Initial Developer are Copyright (C) 2002, 2003 + * the Initial Developers. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "nsString.h" +#include "nsIUnicodeEncoder.h" +#include "nsICharsetConverterManager.h" +#include "nsReadableUtils.h" +#include "nsIServiceManager.h" +#include "nsUConvDll.h" +#include "prmem.h" +#include "nsUTF8ConverterService.h" +#include "nsEscape.h" +#include "nsAutoPtr.h" + +NS_IMPL_ISUPPORTS1(nsUTF8ConverterService, nsIUTF8ConverterService) + +static nsresult +ToUTF8(const nsACString &aString, const char *aCharset, nsACString &aResult) +{ + nsresult rv; + if (!aCharset || !*aCharset) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr ccm; + + ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr unicodeDecoder; + rv = ccm->GetUnicodeDecoder(aCharset, + getter_AddRefs(unicodeDecoder)); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 srcLen = aString.Length(); + PRInt32 dstLen; + const nsAFlatCString& inStr = PromiseFlatCString(aString); + rv = unicodeDecoder->GetMaxLength(inStr.get(), srcLen, &dstLen); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoPtr ustr(new PRUnichar[dstLen]); + NS_ENSURE_TRUE(ustr, NS_ERROR_OUT_OF_MEMORY); + + rv = unicodeDecoder->Convert(inStr.get(), &srcLen, ustr, &dstLen); + if (NS_SUCCEEDED(rv)) + CopyUTF16toUTF8(nsDependentString(ustr, dstLen), aResult); + return rv; +} + +NS_IMETHODIMP +nsUTF8ConverterService::ConvertStringToUTF8(const nsACString &aString, + const char *aCharset, + PRBool aSkipCheck, + nsACString &aUTF8String) +{ + aUTF8String.Truncate(); + + // return if ASCII only or valid UTF-8 providing that the ASCII/UTF-8 + // check is requested. It may not be asked for if a caller suspects + // that the input is in non-ASCII 7bit charset (ISO-2022-xx, HZ) or + // it's in a charset other than UTF-8 that can be mistaken for UTF-8. + if (!aSkipCheck && (IsASCII(aString) || IsUTF8(aString))) { + aUTF8String = aString; + return NS_OK; + } + + nsresult rv = ToUTF8(aString, aCharset, aUTF8String); + + // additional protection for cases where check is skipped and the input + // is actually in UTF-8 as opposed to aCharset. (i.e. caller's hunch + // was wrong.) We don't check ASCIIness assuming there's no charset + // incompatible with ASCII (we don't support EBCDIC). + if (aSkipCheck && NS_FAILED(rv) && IsUTF8(aString)) { + aUTF8String = aString; + return NS_OK; + } + + return rv; +} + +NS_IMETHODIMP +nsUTF8ConverterService::ConvertURISpecToUTF8(const nsACString &aSpec, + const char *aCharset, + nsACString &aUTF8Spec) +{ + aUTF8Spec.Truncate(); + + // assume UTF-8 if the spec contains unescaped non-ASCII characters. + // No valid spec in Mozilla would break this assumption. + if (!IsASCII(aSpec)) { + aUTF8Spec = aSpec; + return NS_OK; + } + + nsCAutoString unescapedSpec; + NS_UnescapeURL(PromiseFlatCString(aSpec).get(), aSpec.Length(), + esc_OnlyNonASCII, unescapedSpec); + + // return if ASCII only or escaped UTF-8 + if (IsASCII(unescapedSpec) || IsUTF8(unescapedSpec)) { + aUTF8Spec = unescapedSpec; + return NS_OK; + } + + return ToUTF8(unescapedSpec, aCharset, aUTF8Spec); +} + diff --git a/mozilla/intl/uconv/src/nsUTF8ConverterService.h b/mozilla/intl/uconv/src/nsUTF8ConverterService.h new file mode 100644 index 00000000000..d1e91d14416 --- /dev/null +++ b/mozilla/intl/uconv/src/nsUTF8ConverterService.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Jungshik Shin . + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#ifndef nsUTF8ConverterService_h__ +#define nsUTF8ConverterService_h__ + +#include "nsIUTF8ConverterService.h" + +//============================================================== + +class nsUTF8ConverterService: public nsIUTF8ConverterService { + NS_DECL_ISUPPORTS + NS_DECL_NSIUTF8CONVERTERSERVICE + +public: + nsUTF8ConverterService() {}; + virtual ~nsUTF8ConverterService() {}; +}; + +#endif // nsUTF8ConverterService_h__ + diff --git a/mozilla/mailnews/compose/src/nsSmtpService.cpp b/mozilla/mailnews/compose/src/nsSmtpService.cpp index 3860f4f4cfc..281f01a4f5c 100644 --- a/mozilla/mailnews/compose/src/nsSmtpService.cpp +++ b/mozilla/mailnews/compose/src/nsSmtpService.cpp @@ -60,7 +60,8 @@ #include "nsIPrompt.h" #include "nsIWindowWatcher.h" #include "nsMsgSimulateError.h" -#include "nsICharsetConverterManager.h" +#include "nsIUTF8ConverterService.h" +#include "nsUConvCID.h" #define SERVER_DELIMITER "," #define APPEND_SERVERS_VERSION_PREF_NAME "append_preconfig_smtpservers.version" @@ -83,60 +84,6 @@ static NS_DEFINE_CID(kCSmtpUrlCID, NS_SMTPURL_CID); static NS_DEFINE_CID(kCMailtoUrlCID, NS_MAILTOURL_CID); static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); - -// function to ensure the spec is encoded in UTF-8 -// if not then use Unicode converter and apply conversions -// output string is empty if no conversion is necessary -static nsresult -EnsureUTF8Spec(const nsACString &aSpec, const char *aCharset, - nsACString &aUTF8Spec) -{ - aUTF8Spec.Truncate(0); - - // assume UTF-8 if the spec contains unescaped non ASCII - if (!nsCRT::IsAscii(PromiseFlatCString(aSpec).get())) - return NS_OK; - - nsCAutoString unescapedSpec; - NS_UnescapeURL(PromiseFlatCString(aSpec).get(), aSpec.Length(), - esc_OnlyNonASCII, unescapedSpec); - - // return if ASCII only or escaped UTF-8 - if (IsASCII(unescapedSpec) || - unescapedSpec.Equals(NS_ConvertUCS2toUTF8(NS_ConvertUTF8toUCS2(unescapedSpec)))) - return NS_OK; - - - nsresult rv; - nsCOMPtr charsetConverterManager; - - charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr unicodeDecoder; - rv = charsetConverterManager->GetUnicodeDecoder(aCharset, - getter_AddRefs(unicodeDecoder)); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt32 srcLen = unescapedSpec.Length(); - PRInt32 dstLen; - rv = unicodeDecoder->GetMaxLength(unescapedSpec.get(), srcLen, &dstLen); - NS_ENSURE_SUCCESS(rv, rv); - - PRUnichar *ustr = (PRUnichar *) nsMemory::Alloc(dstLen * sizeof(PRUnichar)); - NS_ENSURE_TRUE(ustr, NS_ERROR_OUT_OF_MEMORY); - - rv = unicodeDecoder->Convert(unescapedSpec.get(), &srcLen, ustr, &dstLen); - if (NS_SUCCEEDED(rv)) - { - NS_ConvertUCS2toUTF8 rawUTF8Spec(ustr, dstLen); - NS_EscapeURL(rawUTF8Spec, esc_AlwaysCopy | esc_OnlyNonASCII, aUTF8Spec); - } - nsMemory::Free(ustr); - - return rv; -} - // foward declarations... nsresult NS_MsgBuildSmtpUrl(nsIFileSpec * aFilePath, @@ -370,9 +317,14 @@ NS_IMETHODIMP nsSmtpService::NewURI(const nsACString &aSpec, { nsCAutoString utf8Spec; if (aOriginCharset) - rv = EnsureUTF8Spec(aSpec, aOriginCharset, utf8Spec); + { + nsCOMPtr + utf8Converter(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) + rv = utf8Converter->ConvertURISpecToUTF8(aSpec, aOriginCharset, utf8Spec); + } - if (NS_SUCCEEDED(rv) && !utf8Spec.IsEmpty()) + if (NS_SUCCEEDED(rv)) mailtoUrl->SetSpec(utf8Spec); else mailtoUrl->SetSpec(aSpec); diff --git a/mozilla/mailnews/mime/src/comi18n.cpp b/mozilla/mailnews/mime/src/comi18n.cpp index 8c4ea3b8383..a5a4cc3a8c9 100644 --- a/mozilla/mailnews/mime/src/comi18n.cpp +++ b/mozilla/mailnews/mime/src/comi18n.cpp @@ -62,6 +62,8 @@ #include "nsHankakuToZenkakuCID.h" #include "nsReadableUtils.h" #include "mimehdrs.h" +#include "nsIMIMEHeaderParam.h" +#include "nsNetCID.h" //////////////////////////////////////////////////////////////////////////////// @@ -69,30 +71,6 @@ static char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -#define XX 127 -/* - * Table for decoding base64 - */ -static char index_64[256] = { - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63, - 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX, - XX, 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,XX, XX,XX,XX,XX, - XX,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,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, -}; -#define CHAR64(c) (index_64[(unsigned char)(c)]) - #define NO_Q_ENCODING_NEEDED(ch) \ (((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \ ((ch) >= '0' && (ch) <= '9') || (ch) == '!' || (ch) == '*' || \ @@ -178,72 +156,6 @@ intlmime_encode_b(const unsigned char* input, PRInt32 inlen, char* output) return (output - head); } -static char *intlmime_decode_b (const char *in, unsigned length) -{ - char *out, *dest = 0; - PRInt32 c1, c2, c3, c4; - - out = dest = (char *)PR_Malloc(length+1); - if (dest == NULL) - return NULL; - - while (length > 0) { - while (length > 0 && CHAR64(*in) == XX) { - if (*in == '=') goto badsyntax; - in++; - length--; - } - if (length == 0) break; - c1 = *in++; - length--; - - while (length > 0 && CHAR64(*in) == XX) { - if (*in == '=') goto badsyntax; - in++; - length--; - } - if (length == 0) goto badsyntax; - c2 = *in++; - length--; - - while (length > 0 && *in != '=' && CHAR64(*in) == XX) { - in++; - length--; - } - if (length == 0) goto badsyntax; - c3 = *in++; - length--; - - while (length > 0 && *in != '=' && CHAR64(*in) == XX) { - in++; - length--; - } - if (length == 0) goto badsyntax; - c4 = *in++; - length--; - - c1 = CHAR64(c1); - c2 = CHAR64(c2); - *out++ = ((c1<<2) | ((c2&0x30)>>4)); - if (c3 == '=') { - if (c4 != '=') goto badsyntax; - break; /* End of data */ - } - c3 = CHAR64(c3); - *out++ = (((c2&0x0F) << 4) | ((c3&0x3C) >> 2)); - if (c4 == '=') { - break; /* End of data */ - } - c4 = CHAR64(c4); - *out++ = (((c3&0x03) << 6) | c4); - } - *out++ = '\0'; - return dest; - -badsyntax: - PR_Free(dest); - return NULL; -} /* some utility function used by this file */ static PRBool intlmime_only_ascii_str(const char *s) @@ -803,329 +715,6 @@ char * apply_rfc2047_encoding(const char *_src, PRBool structured, const char *c return output; } -/* - * Table for decoding hexadecimal in quoted-printable - */ -static char index_hex[256] = { - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX, - XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, - XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, -}; -#define HEXCHAR(c) (index_hex[(unsigned char)(c)]) - -static void convert_htab(char *s) -{ - for (; *s; ++s) { - if (*s == TAB) - *s = ' '; - } -} - -static char *intlmime_decode_q(const char *in, unsigned length) -{ - char *out, *dest = 0; - - out = dest = (char *)PR_Malloc(length+1); - if (dest == NULL) - return NULL; - memset(out, 0, length+1); - while (length > 0) { - switch (*in) { - case '=': - if (length < 3 || HEXCHAR(in[1]) == XX || - HEXCHAR(in[2]) == XX) goto badsyntax; - *out++ = (HEXCHAR(in[1]) << 4) + HEXCHAR(in[2]); - in += 3; - length -= 3; - break; - - case '_': - *out++ = ' '; - in++; - length--; - continue; - - default: - if (*in & 0x80) goto badsyntax; - *out++ = *in++; - length--; - } - } - *out++ = '\0'; - convert_htab(dest); - return dest; - - badsyntax: - PR_Free(dest); - return NULL; -} - -static PRBool intl_is_utf8(const char *input, unsigned len) -{ - PRInt32 c; - /* - * Input which contains legal HZ sequences should not be detected - * as UTF-8. - */ - enum { hz_initial, /* No HZ seen yet */ - hz_escaped, /* Inside an HZ ~{ escape sequence */ - hz_seen, /* Have seen at least one complete HZ sequence */ - hz_notpresent /* Have seen something that is not legal HZ */ - } hz_state; - - hz_state = hz_initial; - while (len) { - c = (unsigned char)*input++; - len--; - if (c == 0x1B) return PR_FALSE; - if (c == '~') { - switch (hz_state) { - case hz_initial: - case hz_seen: - if (*input == '{') { - hz_state = hz_escaped; - } else if (*input == '~') { - /* ~~ is the HZ encoding of ~. Skip over second ~ as well */ - hz_state = hz_seen; - input++; - len--; - } else { - hz_state = hz_notpresent; - } - break; - - case hz_escaped: - if (*input == '}') hz_state = hz_seen; - break; - } - continue; - } - if ((c & 0x80) == 0) continue; - hz_state = hz_notpresent; - if ((c & 0xE0) == 0xC0) { - if (len < 1 || (*input & 0xC0) != 0x80 || - ((c & 0x1F)<<6) + (*input & 0x3f) < 0x80) { - return PR_FALSE; - } - input++; - len--; - } else if ((c & 0xF0) == 0xE0) { - if (len < 2 || (input[0] & 0xC0) != 0x80 || - (input[1] & 0xC0) != 0x80) { - return PR_FALSE; - } - input += 2; - len -= 2; - } else if ((c & 0xF8) == 0xF0) { - if (len < 3 || (input[0] & 0xC0) != 0x80 || - (input[1] & 0xC0) != 0x80 || (input[2] & 0xC0) != 0x80) { - return PR_FALSE; - } - input += 2; - len -= 2; - } else { - return PR_FALSE; - } - } - if (hz_state == hz_seen) return PR_FALSE; - return PR_TRUE; -} - -static void intl_copy_uncoded_header(char **output, const char *input, - unsigned len, const char *default_charset) -{ - PRInt32 c; - char *dest = *output; - - if (!default_charset) { - memcpy(dest, input, len); - *output = dest + len; - return; - } - - // Copy as long as it's US-ASCII. An ESC may indicate ISO 2022 - // A ~ may indicate it is HZ - while (len && (c = (unsigned char)*input++) != 0x1B && c != '~' && !(c & 0x80)) { - *dest++ = c; - len--; - } - if (!len) { - *output = dest; - return; - } - input--; - - // If not UTF-8, treat as default charset - nsAutoString tempUnicodeString; - if (!intl_is_utf8(input, len)) { - if (NS_FAILED(ConvertToUnicode(default_charset, nsCAutoString(input, len).get(), tempUnicodeString))) { - // Failed to convert. Populate the outString with Unicode Replacement Char - tempUnicodeString.Truncate(); - for (unsigned i = 0; i < len; i++) { - tempUnicodeString.Append((PRUnichar)0xFFFD); - } - } - NS_ConvertUCS2toUTF8 utf8_text(tempUnicodeString); - PRInt32 output_len = utf8_text.Length(); - memcpy(dest, utf8_text.get(), output_len); - *output = dest + output_len; - } else { - memcpy(dest, input, len); - *output = dest + len; - } -} - -static const char especials[] = "()<>@,;:\\\"/[]?.="; - -static -char *intl_decode_mime_part2_str(const char *header, - const char *default_charset, PRBool override_charset) -{ - char *output_p = NULL; - char *retbuff = NULL; - const char *p, *q, *r; - char *decoded_text; - const char *begin; /* tracking pointer for where we are in the input buffer */ - PRInt32 last_saw_encoded_word = 0; - const char *charset_start, *charset_end; - char charset[80]; - nsAutoString tempUnicodeString; - - // initialize charset name to an empty string - charset[0] = '\0'; - - /* Assume no more than 3X expansion due to UTF-8 conversion */ - retbuff = (char *)PR_Malloc(3*strlen(header)+1); - - if (retbuff == NULL) - return NULL; - - output_p = retbuff; - begin = header; - - while ((p = PL_strstr(begin, "=?")) != 0) { - if (last_saw_encoded_word) { - /* See if it's all whitespace. */ - for (q = begin; q < p; q++) { - if (!PL_strchr(" \t\r\n", *q)) break; - } - } - - if (!last_saw_encoded_word || q < p) { - /* copy the part before the encoded-word */ - intl_copy_uncoded_header(&output_p, begin, p - begin, default_charset); - begin = p; - } - - p += 2; - - /* Get charset info */ - charset_start = p; - charset_end = 0; - for (q = p; *q != '?'; q++) { - if (*q <= ' ' || PL_strchr(especials, *q)) { - goto badsyntax; - } - - /* RFC 2231 section 5 */ - if (!charset_end && *q == '*') { - charset_end = q; - } - } - if (!charset_end) { - charset_end = q; - } - - /* Check for too-long charset name */ - if ((unsigned)(charset_end - charset_start) >= sizeof(charset)) goto badsyntax; - - memcpy(charset, charset_start, charset_end - charset_start); - charset[charset_end - charset_start] = 0; - - q++; - if (*q != 'Q' && *q != 'q' && *q != 'B' && *q != 'b') - goto badsyntax; - - if (q[1] != '?') - goto badsyntax; - - r = q; - for (r = q + 2; *r != '?'; r++) { - if (*r < ' ') goto badsyntax; - } - if (r[1] != '=') - goto badsyntax; - else if (r == q + 2) { - // it's empty, skip - begin = r + 2; - last_saw_encoded_word = 1; - continue; - } - - - if(*q == 'Q' || *q == 'q') - decoded_text = intlmime_decode_q(q+2, r - (q+2)); - else - decoded_text = intlmime_decode_b(q+2, r - (q+2)); - - if (decoded_text == NULL) - goto badsyntax; - - // Override charset if requested. Never override labeled UTF-8. - // Use default charset instead of UNKNOWN-8BIT - if ((override_charset && 0 != nsCRT::strcasecmp(charset, "UTF-8")) || - (default_charset && 0 == nsCRT::strcasecmp(charset, "UNKNOWN-8BIT"))) { - PL_strncpy(charset, default_charset, sizeof(charset)-1); - charset[sizeof(charset)-1] = '\0'; - } - - if (NS_SUCCEEDED(ConvertToUnicode(charset, decoded_text, tempUnicodeString))) { - NS_ConvertUCS2toUTF8 utf8_text(tempUnicodeString.get()); - PRInt32 utf8_len = utf8_text.Length(); - - memcpy(output_p, utf8_text.get(), utf8_len); - output_p += utf8_len; - } else { - PL_strcpy(output_p, "\347\277\275"); /* UTF-8 encoding of U+FFFD */ - output_p += 3; - } - - PR_Free(decoded_text); - - begin = r + 2; - last_saw_encoded_word = 1; - continue; - - badsyntax: - /* copy the part before the encoded-word */ - PL_strncpy(output_p, begin, p - begin); - output_p += p - begin; - begin = p; - last_saw_encoded_word = 0; - } - - /* put the tail back */ - intl_copy_uncoded_header(&output_p, begin, strlen(begin), default_charset); - *output_p = '\0'; - convert_htab(retbuff); - - return retbuff; -} - - //////////////////////////////////////////////////////////////////////////////// // BEGIN PUBLIC INTERFACE extern "C" { @@ -1137,25 +726,16 @@ extern "C" char *MIME_DecodeMimeHeader(const char *header, PRBool override_charset, PRBool eatContinuations) { - char *result = nsnull; - - if (header == 0) + nsresult rv; + nsCOMPtr mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv); + if (NS_FAILED(rv)) return nsnull; - - // If no MIME encoded then do nothing otherwise decode the input. - if (PL_strstr(header, "=?") || - (default_charset && !intl_is_utf8(header, strlen(header)))) { - result = intl_decode_mime_part2_str(header, default_charset, override_charset); - } else if (eatContinuations && - (PL_strchr(header, '\n') || PL_strchr(header, '\r'))) { - result = nsCRT::strdup(header); - } else { - eatContinuations = PR_FALSE; - } - if (eatContinuations) - result = MIME_StripContinuations(result); - - return result; + nsCAutoString result; + rv = mimehdrpar->DecodeRFC2047Header(header, default_charset, override_charset, + eatContinuations, result); + if (NS_SUCCEEDED(rv)) + return nsCRT::strdup(result.get()); + return nsnull; } char *MIME_EncodeMimePartIIStr(const char* header, PRBool structured, const char* mailCharset, const PRInt32 fieldNameLen, const PRInt32 encodedWordSize) diff --git a/mozilla/mailnews/mime/src/mimehdrs.cpp b/mozilla/mailnews/mime/src/mimehdrs.cpp index 31dffddff41..f0255851d2c 100644 --- a/mozilla/mailnews/mime/src/mimehdrs.cpp +++ b/mozilla/mailnews/mime/src/mimehdrs.cpp @@ -55,6 +55,8 @@ #include "mimemoz2.h" #include "nsMsgI18N.h" #include "mimehdrs.h" +#include "nsIMIMEHeaderParam.h" +#include "nsNetCID.h" // Forward declares... PRInt32 MimeHeaders_build_heads_list(MimeHeaders *hdrs); @@ -481,190 +483,22 @@ MimeHeaders_get (MimeHeaders *hdrs, const char *header_name, char * MimeHeaders_get_parameter (const char *header_value, const char *parm_name, - char **charset, char **language) + char **charset, char **language) { - const char *str; - char *s = NULL; /* parm value to be returned */ - PRInt32 parm_len; if (!header_value || !parm_name || !*header_value || !*parm_name) - return 0; + return nsnull; - /* The format of these header lines is - [ ';' '=' ]* - */ + nsresult rv; + nsCOMPtr mimehdrpar = + do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv); - if (charset) *charset = NULL; - if (language) *language = NULL; + if (NS_FAILED(rv)) + return nsnull; - str = header_value; - parm_len = strlen(parm_name); - - /* Skip forward to first ';' */ - for (; *str && *str != ';' && *str != ','; str++) - ; - if (*str) - str++; - /* Skip over following whitespace */ - for (; *str && nsCRT::IsAsciiSpace(*str); str++) - ; - if (!*str) - return 0; - - while (*str) - { - const char *token_start = str; - const char *token_end = 0; - const char *value_start = str; - const char *value_end = 0; - - NS_ASSERTION(!nsCRT::IsAsciiSpace(*str), "1.1 19 Mar 1999 12:00"); /* should be after whitespace already */ - - /* Skip forward to the end of this token. */ - for (; *str && !nsCRT::IsAsciiSpace(*str) && *str != '=' && *str != ';'; str++) - ; - token_end = str; - - /* Skip over whitespace, '=', and whitespace */ - while (nsCRT::IsAsciiSpace (*str)) str++; - if (*str == '=') str++; - while (nsCRT::IsAsciiSpace (*str)) str++; - - if (*str != '"') - { - /* The value is a token, not a quoted string. */ - value_start = str; - for (value_end = str; - *value_end && !nsCRT::IsAsciiSpace (*value_end) && *value_end != ';'; - value_end++) - ; - str = value_end; - } - else - { - /* The value is a quoted string. */ - str++; - value_start = str; - for (value_end = str; *value_end; value_end++) - { - if (*value_end == '\\') - value_end++; - else if (*value_end == '"') - break; - } - str = value_end+1; - } - - /* See if this is the parameter we're looking for. - If so, copy it and return. - */ - if (token_end - token_start == parm_len && - !nsCRT::strncasecmp(token_start, parm_name, parm_len)) - { - s = (char *) PR_MALLOC ((value_end - value_start) + 1); - if (! s) return 0; /* MIME_OUT_OF_MEMORY */ - memcpy (s, value_start, value_end - value_start); - s [value_end - value_start] = 0; - /* if the parameter spans across multiple lines we have to strip out the - line continuatio -- jht 4/29/98 */ - MIME_StripContinuations(s); - return s; - } - else if (token_end - token_start > parm_len && - !nsCRT::strncasecmp(token_start, parm_name, parm_len) && - *(token_start+parm_len) == '*') - { - /* RFC2231 - The legitimate parm format can be: - title*=us-ascii'en-us'This%20is%20weired. - or - title*0*=us-ascii'en'This%20is%20weired.%20We - title*1*=have%20to%20support%20this. - title*3="Else..." - or - title*0="Hey, what you think you are doing?" - title*1="There is no charset and language info." - */ - const char *cp = token_start+parm_len+1; /* 1st char pass '*' */ - PRBool needUnescape = *(token_end-1) == '*'; - if ((*cp == '0' && needUnescape) || (token_end-token_start == parm_len+1)) - { - const char *s_quote1 = PL_strchr(value_start, 0x27); - const char *s_quote2 = (char *) (s_quote1 ? PL_strchr(s_quote1+1, 0x27) : NULL); - NS_ASSERTION(s_quote1 && s_quote2, "1.1 19 Mar 1999 12:00"); - if (charset && s_quote1 > value_start && s_quote1 < value_end) - { - *charset = (char *) PR_MALLOC(s_quote1-value_start+1); - if (*charset) - { - memcpy(*charset, value_start, s_quote1-value_start); - *(*charset+(s_quote1-value_start)) = 0; - } - } - if (language && s_quote1 && s_quote2 && s_quote2 > s_quote1+1 && - s_quote2 < value_end) - { - *language = (char *) PR_MALLOC(s_quote2-(s_quote1+1)+1); - if (*language) - { - memcpy(*language, s_quote1+1, s_quote2-(s_quote1+1)); - *(*language+(s_quote2-(s_quote1+1))) = 0; - } - } - if (s_quote2 && s_quote2+1 < value_end) - { - NS_ASSERTION(!s, "1.1 19 Mar 1999 12:00"); - s = (char *) PR_MALLOC(value_end-(s_quote2+1)+1); - if (s) - { - memcpy(s, s_quote2+1, value_end-(s_quote2+1)); - *(s+(value_end-(s_quote2+1))) = 0; - if (needUnescape) - { - nsUnescape(s); - if (token_end-token_start == parm_len+1) - return s; /* we done; this is the simple case of - encoding charset and language info - */ - } - } - } - } - else if (IS_DIGIT(*cp)) - { - PRInt32 len = 0; - char *ns = NULL; - if (s) - { - len = strlen(s); - ns = (char *) PR_Realloc(s, len+(value_end-value_start)+1); - if (!ns) - { - PR_FREEIF(s); - } - else if (ns != s) - s = ns; - } - else if (*cp == '0') /* must be; otherwise something is wrong */ - { - s = (char *) PR_MALLOC(value_end-value_start+1); - } - /* else {} something is really wrong; out of memory */ - if (s) - { - memcpy(s+len, value_start, value_end-value_start); - *(s+len+(value_end-value_start)) = 0; - if (needUnescape) - nsUnescape(s+len); - } - } - } - - /* str now points after the end of the value. - skip over whitespace, ';', whitespace. */ - while (nsCRT::IsAsciiSpace (*str)) str++; - if (*str == ';') str++; - while (nsCRT::IsAsciiSpace (*str)) str++; - } - return s; + nsXPIDLCString result; + rv = mimehdrpar->GetParameterInternal(header_value, parm_name, charset, + language, getter_Copies(result)); + return NS_SUCCEEDED(rv) ? PL_strdup(result.get()) : nsnull; } #define MimeHeaders_write(OPT,DATA,LENGTH) \ @@ -848,40 +682,17 @@ char * mime_decode_filename(char *name, const char *charset, MimeDisplayOptions *opt) { - char *s = name, *d = name; - char *cvt, *returnVal = NULL; - - // If charset parameter is used, this is RFC2231 encoding. - if (charset) - { - nsAutoString tempUnicodeString; - if (NS_SUCCEEDED(ConvertToUnicode(charset, name, tempUnicodeString))) - { - if (returnVal = nsCRT::strdup(NS_ConvertUCS2toUTF8(tempUnicodeString.get()).get())) - return returnVal; - } - } + nsresult rv; + nsCOMPtr mimehdrpar = + do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv); - while (*s) - { - /* Remove backslashes when they are used to escape special characters. */ - if ((*s == '\\') && - ((*(s+1) == nsCRT::CR) || (*(s+1) == nsCRT::LF) || (*(s+1) == '"') || (*(s+1) == '\\'))) - s++; /* take whatever char follows the backslash */ - if (*s) - *d++ = *s++; - } - *d = 0; - returnVal = name; - - cvt = MIME_DecodeMimeHeader(returnVal, opt->default_charset, - opt->override_charset, PR_TRUE); - - if (cvt && cvt != returnVal) { - returnVal = cvt; - } - - return returnVal; + if (NS_FAILED(rv)) + return nsnull; + nsCAutoString result; + rv = mimehdrpar->DecodeParameter(nsDependentCString(name), charset, + opt->default_charset, + opt->override_charset, result); + return NS_SUCCEEDED(rv) ? PL_strdup(result.get()) : nsnull; } /* Pull the name out of some header or another. Order is: @@ -898,50 +709,50 @@ MimeHeaders_get_name(MimeHeaders *hdrs, MimeDisplayOptions *opt) s = MimeHeaders_get(hdrs, HEADER_CONTENT_DISPOSITION, PR_FALSE, PR_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, PR_FALSE, PR_FALSE); - if (s) - { - PR_FREEIF(charset); - - name = MimeHeaders_get_parameter(s, HEADER_PARM_NAME, &charset, NULL); - PR_Free(s); - } + name = MimeHeaders_get_parameter(s, HEADER_PARM_FILENAME, &charset, NULL); + PR_Free(s); } if (! name) - name = MimeHeaders_get (hdrs, HEADER_CONTENT_NAME, PR_FALSE, PR_FALSE); + { + s = MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE); + if (s) + { + nsMemory::Free(charset); + + name = MimeHeaders_get_parameter(s, HEADER_PARM_NAME, &charset, NULL); + PR_Free(s); + } + } + + if (! name) + name = MimeHeaders_get (hdrs, HEADER_CONTENT_NAME, PR_FALSE, PR_FALSE); if (! name) - name = MimeHeaders_get (hdrs, HEADER_X_SUN_DATA_NAME, PR_FALSE, PR_FALSE); + name = MimeHeaders_get (hdrs, HEADER_X_SUN_DATA_NAME, PR_FALSE, PR_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); + /* 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); + /* 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); - PR_FREEIF(charset); + nsMemory::Free(charset); - if (cvt && cvt != name) - { - PR_Free(name); - name = cvt; - } + if (cvt && cvt != name) + { + PR_Free(name); + name = cvt; + } } return name; diff --git a/mozilla/mailnews/mime/src/mimemoz2.cpp b/mozilla/mailnews/mime/src/mimemoz2.cpp index 16156f0a6ac..51a9ed57cd2 100644 --- a/mozilla/mailnews/mime/src/mimemoz2.cpp +++ b/mozilla/mailnews/mime/src/mimemoz2.cpp @@ -156,7 +156,7 @@ ProcessBodyAsAttachment(MimeObject *obj, nsMsgAttachmentData **data) { char *fname = NULL; fname = mime_decode_filename(tmp->real_name, charset, obj->options); - PR_FREEIF(charset); + nsMemory::Free(charset); if (fname && fname != tmp->real_name) { PR_Free(tmp->real_name); @@ -367,7 +367,7 @@ GenerateAttachmentData(MimeObject *object, const char *aMessageURL, MimeDisplayO for (i = 0; i < 2 && !tmp->real_name; i ++) { PR_FREEIF(disp); - PR_FREEIF(charset); + nsMemory::Free(charset); disp = MimeHeaders_get(((MimeContainer *)object)->children[i]->headers, HEADER_CONTENT_DISPOSITION, PR_FALSE, PR_FALSE); tmp->real_name = MimeHeaders_get_parameter(disp, "filename", &charset, nsnull); } @@ -382,7 +382,7 @@ GenerateAttachmentData(MimeObject *object, const char *aMessageURL, MimeDisplayO char *fname = nsnull; fname = mime_decode_filename(tmp->real_name, charset, options); - PR_FREEIF(charset); + nsMemory::Free(charset); if (fname && fname != tmp->real_name) { @@ -408,7 +408,7 @@ GenerateAttachmentData(MimeObject *object, const char *aMessageURL, MimeDisplayO for (i = 0; i < 2 && !tmp->real_name; i ++) { PR_FREEIF(disp); - PR_FREEIF(charset); + nsMemory::Free(charset); disp = MimeHeaders_get(((MimeContainer *)object)->children[i]->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE); tmp->real_name = MimeHeaders_get_parameter(disp, "name", &charset, nsnull); } @@ -423,7 +423,7 @@ GenerateAttachmentData(MimeObject *object, const char *aMessageURL, MimeDisplayO char *fname = nsnull; fname = mime_decode_filename(tmp->real_name, charset, options); - PR_FREEIF(charset); + nsMemory::Free(charset); if (fname && fname != tmp->real_name) { diff --git a/mozilla/mailnews/mime/src/modlmime.h b/mozilla/mailnews/mime/src/modlmime.h index 275652aaebc..667402ad92e 100644 --- a/mozilla/mailnews/mime/src/modlmime.h +++ b/mozilla/mailnews/mime/src/modlmime.h @@ -118,15 +118,18 @@ extern char *MimeHeaders_get(MimeHeaders *hdrs, 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 - 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. - 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" + 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 nsMemory::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, diff --git a/mozilla/netwerk/build/nsNetCID.h b/mozilla/netwerk/build/nsNetCID.h index 7be6b1e59cd..2111b2aec60 100644 --- a/mozilla/netwerk/build/nsNetCID.h +++ b/mozilla/netwerk/build/nsNetCID.h @@ -570,4 +570,20 @@ {0x8c, 0x31, 0x28, 0x65, 0xfb, 0xb6, 0x8c, 0x91} \ } +/****************************************************************************** + * netwerk/mime classes + */ + +#define NS_MIMEHEADERPARAM_CLASSNAME \ + "nsMIMEHeaderParamImpl" +// {1F4DBCF7-245C-4c8c-943D-8A1DA0495E8A} +#define NS_MIMEHEADERPARAM_CID \ +{ 0x1f4dbcf7, \ + 0x245c, \ + 0x4c8c, \ + { 0x94, 0x3d, 0x8a, 0x1d, 0xa0, 0x49, 0x5e, 0x8a } \ +} + +#define NS_MIMEHEADERPARAM_CONTRACTID "@mozilla.org/network/mime-hdrparam;1" + #endif // nsNetCID_h__ diff --git a/mozilla/netwerk/build/nsNetModule.cpp b/mozilla/netwerk/build/nsNetModule.cpp index 3a4ec157335..feed1bf9082 100644 --- a/mozilla/netwerk/build/nsNetModule.cpp +++ b/mozilla/netwerk/build/nsNetModule.cpp @@ -112,6 +112,11 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsMIMEInfoImpl) /////////////////////////////////////////////////////////////////////////////// +#include "nsMIMEHeaderParamImpl.h" + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsMIMEHeaderParamImpl) +/////////////////////////////////////////////////////////////////////////////// + #include "nsRequestObserverProxy.h" #include "nsSimpleStreamListener.h" #include "nsDirIndexParser.h" @@ -811,6 +816,12 @@ static const nsModuleComponentInfo gNetModuleInfo[] = { nsMIMEInfoImplConstructor }, + { "mime header param", + NS_MIMEHEADERPARAM_CID, + NS_MIMEHEADERPARAM_CONTRACTID, + nsMIMEHeaderParamImplConstructor + }, + #ifdef NECKO_PROTOCOL_file // from netwerk/protocol/file: { NS_FILEPROTOCOLHANDLER_CLASSNAME, diff --git a/mozilla/netwerk/mime/public/Makefile.in b/mozilla/netwerk/mime/public/Makefile.in index 750e9dd894e..be9f8782dcb 100644 --- a/mozilla/netwerk/mime/public/Makefile.in +++ b/mozilla/netwerk/mime/public/Makefile.in @@ -32,6 +32,7 @@ XPIDL_MODULE = mimetype XPIDLSRCS = \ nsIMIMEService.idl \ nsIMIMEInfo.idl \ + nsIMIMEHeaderParam.idl \ $(NULL) EXPORTS = \ diff --git a/mozilla/netwerk/mime/public/nsIMIMEHeaderParam.idl b/mozilla/netwerk/mime/public/nsIMIMEHeaderParam.idl new file mode 100644 index 00000000000..eb6a5fe1b9d --- /dev/null +++ b/mozilla/netwerk/mime/public/nsIMIMEHeaderParam.idl @@ -0,0 +1,201 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Jungshik Shin + * Portions created by the Initial Developer are Copyright (C) 2003. + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * This interface allows any module to access the routine + * for MIME header parameter parsing (RFC 2231) + */ + +#include "nsISupports.idl" + +[scriptable, uuid(ddbbdfb8-a1c0-4dd5-a31b-5d2a7a3bb6ec)] +interface nsIMIMEHeaderParam : nsISupports { + + /** + * Given the value of a single header field (such as + * Content-Disposition and Content-Type) and the name of a parameter + * (e.g. filename, name, charset), returns the value of the parameter. + * The value is obtained by decoding RFC 2231-style encoding, + * RFC 2047-style encoding, and converting to UniChar(UTF-16) + * from charset specified in RFC 2231/2047 encoding, UTF-8, + * aFallbackCharset and the locale charset as the last + * resort if TryLocaleCharset is set. + * + *

+ * This method internally invokes getParameterInternal, + * However, it does not stop at decoding RFC 2231 (the task for + * getParameterInternal but tries to cope + * with several non-standard-compliant cases mentioned below. + * + *

+ * Note that a lot of MUAs and HTTP servers put RFC 2047-encoded parameters + * in mail headers and HTTP headers. Unfortunately, this includes Mozilla + * as of 2003-05-30. Even more standard-ignorant MUAs, web servers and + * application servers put 'raw 8bit characters'. This will try to cope + * with all these cases as gracefully as possible. Additionally, it + * returns the language tag if the parameter is encoded per RFC 2231 and + * includes lang. + * + * + * + * @param aHeaderVal a header string to get the value of a parameter + * from. + * @param aParamName the name of a MIME header parameter (e.g. + * filename, name, charset). If empty, returns + * the first (possibly) _unnamed_ 'parameter'. + * @param aFallbackCharset fallback charset to try if the string after + * RFC 2231/2047 decoding or the raw 8bit + * string is not UTF-8 + * @param aTryLocaleCharset If set, makes yet another attempt + * with the locale charset. + * @param aLang If non-null, assigns it to a pointer + * to a string containing the value of language + * obtained from RFC 2231 parsing. Caller has to + * nsMemory::Free it. + * @return the value of aParamName in Unichar(UTF-16). + */ + + AString getParameter(in ACString aHeaderVal, + in string aParamName, + in ACString aFallbackCharset, + in boolean aTryLocaleCharset, + out string aLang); + /** + * Given the value of a single header field (such as + * Content-Disposition and Content-Type) and the name of a parameter + * (e.g. filename, name, charset), returns the value of the parameter + * after decoding RFC 2231-style encoding. + *

+ * For internal use only. The only other place where + * this needs to be invoked is |MimeHeaders_get_parameter| in + * mailnews/mime/src/mimehdrs.cpp defined as + * char * MimeHeaders_get_parameter (const char *header_value, + * const char *parm_name, + * char **charset, char **language) + * + * Otherwise, this method would have been made static. + * + * @param aHeaderVal a header string to get the value of a parameter from. + * @param aParamName the name of a MIME header parameter (e.g. + * filename, name, charset). If empty, returns + * the first (possibly) _unnamed_ 'parameter'. + * @param aCharset If non-null, it gets assigned a new pointer + * to a string containing the value of charset obtained + * from RFC 2231 parsing. Caller has to nsMemory::Free it. + * @param aLang If non-null, it gets assigned a new pointer + * to a string containing the value of language obtained + * from RFC 2231 parsing. Caller has to nsMemory::Free it. + * @return the value of aParamName after + * RFC 2231 decoding but without charset conversion. + */ + + [noscript] + string getParameterInternal(in string aHeaderVal, + in string aParamName, + out string aCharset, + out string aLang); + + + /** + * Given a header value, decodes RFC 2047-style encoding and + * returns the decoded header value in UTF-8 if either it's + * RFC-2047-encoded or aDefaultCharset is given. Otherwise, + * returns the input header value (in whatever encoding) + * as it is except that RFC 822 (using backslash) quotation and + * CRLF (if aEatContinuation is set) are stripped away + *

+ * For internal use only. The only other place where this needs to be + * invoked is MIME_DecodeMimeHeader in + * mailnews/mime/src/mimehdrs.cpp defined as + * char * Mime_DecodeMimeHeader(char *header_val, const char *charset, + * PRBool override, PRBool eatcontinuation) + * + * @param aHeaderVal a header value to decode + * @param aDefaultCharset MIME charset to use in place of MIME charset + * specified in RFC 2047 style encoding + * when aOverrideCharset is set. + * @param aOverrideCharset When set, overrides MIME charset specified + * in RFC 2047 style encoding with aDefaultCharset + * @param aEatContinuation When set, removes CR/LF + * @return decoded header value + */ + [noscript] + ACString decodeRFC2047Header(in string aHeaderVal, + in string aDefaultCharset, + in boolean aOverrideCharset, + in boolean aEatContinuation); + + + /** + * Given a header parameter, decodes RFC 2047 style encoding (if it's + * not obtained from RFC 2231 encoding), converts it to + * UTF-8 and returns the result in UTF-8 if an attempt to extract + * charset info. from a few different sources succeeds. + * Otherwise, returns the input header value (in whatever encoding) + * as it is except that RFC 822 (using backslash) quotation is + * stripped off. + *

+ * For internal use only. The only other place where this needs to be + * invoked is mime_decode_filename in + * mailnews/mime/src/mimehdrs.cpp defined as + * char * mime_decode_filename(char *name, const char *charset, + * MimeDisplayOptions *opt) + * + * @param aParamValue the value of a parameter to decode and convert + * @param aCharset charset obtained from RFC 2231 decoding in which + * aParamValue is encoded. If null, + * indicates that it needs to try RFC 2047, instead. + * @param aDefaultCharset MIME charset to use when aCharset is null and + * cannot be obtained per RFC 2047 (most likely + * because 'bare' string is used.) Besides, it + * overrides aCharset/MIME charset obtained from + * RFC 2047 if aOverrideCharset is set. + * @param aOverrideCharset When set, overrides MIME charset specified + * in RFC 2047 style encoding with + * aDefaultCharset + * @return decoded parameter + */ + + [noscript] + ACString decodeParameter(in ACString aParamValue, + in string aCharset, + in string aDefaultCharset, + in boolean aOverrideCharset); +}; + diff --git a/mozilla/netwerk/mime/src/Makefile.in b/mozilla/netwerk/mime/src/Makefile.in index d2a729920ce..89a105c184d 100644 --- a/mozilla/netwerk/mime/src/Makefile.in +++ b/mozilla/netwerk/mime/src/Makefile.in @@ -31,11 +31,13 @@ LIBRARY_NAME = nkmime_s REQUIRES = xpcom \ string \ necko \ + uconv \ pref \ $(NULL) CPPSRCS = \ nsMIMEInfoImpl.cpp \ + nsMIMEHeaderParamImpl.cpp \ $(NULL) # we don't want the shared lib, but we want to force the creation of a diff --git a/mozilla/netwerk/mime/src/nsMIMEHeaderParamImpl.cpp b/mozilla/netwerk/mime/src/nsMIMEHeaderParamImpl.cpp new file mode 100644 index 00000000000..093dc567584 --- /dev/null +++ b/mozilla/netwerk/mime/src/nsMIMEHeaderParamImpl.cpp @@ -0,0 +1,745 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=4: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998, 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * rhp@netscape.com + * Jungshik Shin + * John G Myers + * Takayuki Tei + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include "prtypes.h" +#include "prmem.h" +#include "prprf.h" +#include "plstr.h" +#include "plbase64.h" +#include "nsCRT.h" +#include "nsMemory.h" +#include "nsCOMPtr.h" +#include "nsEscape.h" +#include "nsIUTF8ConverterService.h" +#include "nsUConvCID.h" +#include "nsIServiceManager.h" +#include "nsMIMEHeaderParamImpl.h" +#include "nsReadableUtils.h" + +// static functions declared below are moved from mailnews/mime/src/comi18n.cpp + +static char *DecodeQ(const char *, PRUint32); +static PRBool Is7bitNonAsciiString(const char *, PRUint32); +static void CopyRawHeader(const char *, PRUint32, const char *, nsACString &); +static nsresult DecodeRFC2047Str(const char *, const char *, PRBool, nsACString&); + +// XXX The chance of UTF-7 being used in the message header is really +// low, but in theory it's possible. +#define IS_7BIT_NON_ASCII_CHARSET(cset) \ + (!nsCRT::strncasecmp((cset), "ISO-2022", 8) || \ + !nsCRT::strncasecmp((cset), "HZ-GB", 5) || \ + !nsCRT::strncasecmp((cset), "UTF-7", 5)) + +NS_IMPL_ISUPPORTS1(nsMIMEHeaderParamImpl, nsIMIMEHeaderParam) + +// XXX : aTryLocaleCharset is not yet effective. +NS_IMETHODIMP +nsMIMEHeaderParamImpl::GetParameter(const nsACString& aHeaderVal, + const char *aParamName, + const nsACString& aFallbackCharset, + PRBool aTryLocaleCharset, + char **aLang, nsAString& aResult) +{ + aResult.Truncate(); + nsresult rv; + + // get parameter (decode RFC 2231 if it's RFC 2231-encoded and + // return charset.) + nsXPIDLCString med; + nsXPIDLCString charset; + rv = GetParameterInternal(PromiseFlatCString(aHeaderVal).get(), aParamName, + getter_Copies(charset), aLang, getter_Copies(med)); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(!aTryLocaleCharset, "aTryLocaleCharset not yet supported !"); + + // convert to UTF-8 after charset conversion and RFC 2047 decoding + // if necessary. + + nsCAutoString str1; + rv = DecodeParameter(med, charset.get(), nsnull, PR_FALSE, str1); + NS_ENSURE_SUCCESS(rv, rv); + + if (!aFallbackCharset.IsEmpty()) + { + nsCAutoString str2; + nsCOMPtr + cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv) && + NS_SUCCEEDED(cvtUTF8->ConvertStringToUTF8(str1, + PromiseFlatCString(aFallbackCharset).get(), PR_FALSE, str2))) + CopyUTF8toUTF16(str2, aResult); + else + CopyUTF8toUTF16(str1, aResult); + } + else + CopyUTF8toUTF16(str1, aResult); + return NS_OK; +} + +// moved almost verbatim from mimehdrs.cpp +// char * +// MimeHeaders_get_parameter (const char *header_value, const char *parm_name, +// char **charset, char **language) +// +// The format of these header lines is +// [ ';' '=' ]* +NS_IMETHODIMP +nsMIMEHeaderParamImpl::GetParameterInternal(const char *aHeaderValue, + const char *aParamName, + char **aCharset, + char **aLang, + char **aResult) +{ + if (!aHeaderValue || !*aHeaderValue || !aResult) + return NS_ERROR_INVALID_ARG; + + *aResult = nsnull; + + if (aCharset) *aCharset = nsnull; + if (aLang) *aLang = nsnull; + + const char *str = aHeaderValue; + + // skip leading white space. + for (; *str && nsCRT::IsAsciiSpace(*str); ++str) + ; + const char *start = str; + + // aParamName is empty. return the first (possibly) _unnamed_ 'parameter' + // For instance, return 'inline' in the following case: + // Content-Disposition: inline; filename=..... + if (!aParamName || !*aParamName) + { + for (; *str && *str != ';' && !nsCRT::IsAsciiSpace(*str); ++str) + ; + if (str == start) + return NS_ERROR_UNEXPECTED; + *aResult = (char *) nsMemory::Clone(start, (str - start) + 1); + NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY); + return NS_OK; + } + + /* Skip forward to first ';' */ + for (; *str && *str != ';' && *str != ','; ++str) + ; + if (*str) + str++; + /* Skip over following whitespace */ + for (; *str && nsCRT::IsAsciiSpace(*str); ++str) + ; + + // Some broken http servers just specify parameters + // like 'filename' without sepcifying disposition + // method. Rewind to the first non-white-space + // character. + + if (!*str) + str = start; + + // RFC2231 - The legitimate parm format can be: + // A. title=ThisIsTitle + // B. title*=us-ascii'en-us'This%20is%20wierd. + // C. title*0*=us-ascii'en'This%20is%20wierd.%20We + // title*1*=have%20to%20support%20this. + // title*2="Else..." + // D. title*0="Hey, what you think you are doing?" + // title*1="There is no charset and lang info." + + PRInt32 paramLen = strlen(aParamName); + + while (*str) { + const char *tokenStart = str; + const char *tokenEnd = 0; + const char *valueStart = str; + const char *valueEnd = 0; + + NS_ASSERTION(!nsCRT::IsAsciiSpace(*str), "should be after whitespace."); + + // Skip forward to the end of this token. + for (; *str && !nsCRT::IsAsciiSpace(*str) && *str != '=' && *str != ';'; str++) + ; + tokenEnd = str; + + // Skip over whitespace, '=', and whitespace + while (nsCRT::IsAsciiSpace(*str)) ++str; + if (*str == '=') ++str; + while (nsCRT::IsAsciiSpace(*str)) ++str; + + if (*str != '"') + { + // The value is a token, not a quoted string. + valueStart = str; + for (valueEnd = str; + *valueEnd && !nsCRT::IsAsciiSpace (*valueEnd) && *valueEnd != ';'; + valueEnd++) + ; + str = valueEnd; + } + else + { + // The value is a quoted string. + ++str; + valueStart = str; + for (valueEnd = str; *valueEnd; ++valueEnd) + { + if (*valueEnd == '\\') + ++valueEnd; + else if (*valueEnd == '"') + break; + } + str = valueEnd + 1; + } + + // See if this is the simplest case (case A above), + // a 'single' line value with no charset and lang. + // If so, copy it and return. + if (tokenEnd - tokenStart == paramLen && + !nsCRT::strncasecmp(tokenStart, aParamName, paramLen)) + { + // if the parameter spans across multiple lines we have to strip out the + // line continuation -- jht 4/29/98 + nsCAutoString tempStr(valueStart, valueEnd - valueStart); + tempStr.StripChars("\r\n"); + *aResult = ToNewCString(tempStr); + NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY); + return NS_OK; + } + // case B, C, and D + else if (tokenEnd - tokenStart > paramLen && + !nsCRT::strncasecmp(tokenStart, aParamName, paramLen) && + *(tokenStart + paramLen) == '*') + { + const char *cp = tokenStart + paramLen + 1; // 1st char pass '*' + PRBool needUnescape = *(tokenEnd - 1) == '*'; + // the 1st line of a multi-line parameter or a single line that needs + // unescaping. ( title*0*= or title*= ) + if ((*cp == '0' && needUnescape) || (tokenEnd - tokenStart == paramLen + 1)) + { + // look for single quotation mark(') + const char *sQuote1 = PL_strchr(valueStart, 0x27); + const char *sQuote2 = (char *) (sQuote1 ? PL_strchr(sQuote1 + 1, 0x27) : nsnull); + + // Two single quotation marks must be present even in + // absence of charset and lang. + if (!sQuote1 || !sQuote2) + NS_WARNING("Mandatory two single quotes are missing in header parameter\n"); + if (aCharset && sQuote1 > valueStart && sQuote1 < valueEnd) + { + *aCharset = (char *) nsMemory::Clone(valueStart, sQuote1 - valueStart + 1); + if (*aCharset) + *(*aCharset + (sQuote1 - valueStart)) = 0; + } + if (aLang && sQuote1 && sQuote2 && sQuote2 > sQuote1 + 1 && + sQuote2 < valueEnd) + { + *aLang = (char *) nsMemory::Clone(sQuote1 + 1, sQuote2 - (sQuote1 + 1) + 1); + if (*aLang) + *(*aLang + (sQuote2 - (sQuote1 + 1))) = 0; + } + + // Be generous and handle gracefully when required + // single quotes are absent. + if (sQuote1) + { + if(!sQuote2) + sQuote2 = sQuote1; + } + else + sQuote2 = valueStart - 1; + + if (sQuote2 && sQuote2 + 1 < valueEnd) + { + NS_ASSERTION(!*aResult, "This is the 1st line. result buffer should be null."); + *aResult = (char *) nsMemory::Alloc(valueEnd - (sQuote2 + 1) + 1); + if (*aResult) + { + memcpy(*aResult, sQuote2 + 1, valueEnd - (sQuote2 + 1)); + *(*aResult + (valueEnd - (sQuote2 + 1))) = 0; + if (needUnescape) + { + nsUnescape(*aResult); + if (tokenEnd - tokenStart == paramLen + 1) + // we're done; this is case B + return NS_OK; + } + } + } + } // end of if-block : title*0*= or title*= + // a line of multiline param with no need for unescaping : title*[0-9]= + // or 2nd or later lines of a multiline param : title*[1-9]*= + else if (nsCRT::IsAsciiDigit(PRUnichar(*cp))) + { + PRInt32 len = 0; + if (*aResult) // 2nd or later lines of multiline parameter + { + len = strlen(*aResult); + char *ns = (char *) nsMemory::Realloc(*aResult, len + (valueEnd - valueStart) + 1); + if (!ns) + { + nsMemory::Free(*aResult); + } + *aResult = ns; + } + else if (*cp == '0') // must be; 1st line : title*0= + { + *aResult = (char *) nsMemory::Alloc(valueEnd - valueStart + 1); + } + // else {} something is really wrong; out of memory + if (*aResult) + { + // append a partial value + memcpy(*aResult + len, valueStart, valueEnd - valueStart); + *(*aResult + len + (valueEnd - valueStart)) = 0; + if (needUnescape) + nsUnescape(*aResult + len); + } + else + return NS_ERROR_OUT_OF_MEMORY; + } // end of if-block : title*[0-9]= or title*[1-9]*= + } + + // str now points after the end of the value. + // skip over whitespace, ';', whitespace. + + while (nsCRT::IsAsciiSpace(*str)) ++str; + if (*str == ';') ++str; + while (nsCRT::IsAsciiSpace(*str)) ++str; + } + + if (*aResult) + return NS_OK; + else + return NS_ERROR_INVALID_ARG; // aParameter not found !! +} + + +NS_IMETHODIMP +nsMIMEHeaderParamImpl::DecodeRFC2047Header(const char* aHeaderVal, + const char* aDefaultCharset, + PRBool aOverrideCharset, + PRBool aEatContinuations, + nsACString& aResult) +{ + aResult.Truncate(); + if (!aHeaderVal) + return NS_ERROR_INVALID_ARG; + if (!*aHeaderVal) + return NS_OK; + + + // If aHeaderVal is RFC 2047 encoded or is not a UTF-8 string but + // aDefaultCharset is specified, decodes RFC 2047 encoding and converts + // to UTF-8. Otherwise, just strips away CRLF. + if (PL_strstr(aHeaderVal, "=?") || + aDefaultCharset && (!IsUTF8(nsDependentCString(aHeaderVal)) || + Is7bitNonAsciiString(aHeaderVal, PL_strlen(aHeaderVal)))) { + DecodeRFC2047Str(aHeaderVal, aDefaultCharset, aOverrideCharset, aResult); + } else if (aEatContinuations && + (PL_strchr(aHeaderVal, '\n') || PL_strchr(aHeaderVal, '\r'))) { + aResult = aHeaderVal; + } else { + aEatContinuations = PR_FALSE; + aResult = aHeaderVal; + } + + if (aEatContinuations) { + nsCAutoString temp(aResult); + temp.StripChars("\r\n"); + aResult = temp; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsMIMEHeaderParamImpl::DecodeParameter(const nsACString& aParamValue, + const char* aCharset, + const char* aDefaultCharset, + PRBool aOverrideCharset, + nsACString& aResult) +{ + aResult.Truncate(); + nsresult rv; + // If aCharset is given, aParamValue was obtained from RFC2231 + // encoding and we're pretty sure that it's in aCharset. + if (aCharset && *aCharset) + { + nsCOMPtr cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) + // skip ASCIIness/UTF8ness test if aCharset is 7bit non-ascii charset. + return cvtUTF8->ConvertStringToUTF8(aParamValue, aCharset, + IS_7BIT_NON_ASCII_CHARSET(aCharset), aResult); + } + + const nsAFlatCString& param = PromiseFlatCString(aParamValue); + nsCAutoString unQuoted; + nsACString::const_iterator s, e; + param.BeginReading(s); + param.EndReading(e); + + // strip '\' when used to quote CR, LF, '"' and '\' + for ( ; s != e; ++s) { + if ((*s == '\\')) { + if (++s == e) { + --s; // '\' is at the end. move back and append '\'. + } + else if (*s != nsCRT::CR && *s != nsCRT::LF && *s != '"' && *s != '\\') { + --s; // '\' is not foll. by CR,LF,'"','\'. move back and append '\' + } + // else : skip '\' and append the quoted character. + } + unQuoted.Append(*s); + } + + aResult = unQuoted; + + nsCAutoString decoded; + + // Try RFC 2047 encoding, instead. + rv = DecodeRFC2047Header(unQuoted.get(), aDefaultCharset, + aOverrideCharset, PR_TRUE, decoded); + + if (NS_SUCCEEDED(rv) && !decoded.IsEmpty()) + aResult = decoded; + + return rv; +} + +#define ISHEXCHAR(c) \ + (0x30 <= PRUint8(c) && PRUint8(c) <= 0x39 || \ + 0x41 <= PRUint8(c) && PRUint8(c) <= 0x46 || \ + 0x61 <= PRUint8(c) && PRUint8(c) <= 0x66) + +// Decode Q encoding (RFC 2047). +// static +char *DecodeQ(const char *in, PRUint32 length) +{ + char *out, *dest = 0; + + out = dest = (char *)PR_Calloc(length + 1, sizeof(char)); + if (dest == nsnull) + return nsnull; + while (length > 0) { + switch (*in) { + case '=': + // check if |in| in the form of '=hh' where h is [0-9a-fA-F]. + if (length < 3 || !ISHEXCHAR(in[1]) || !ISHEXCHAR(in[2])) + goto badsyntax; + PR_sscanf(in + 1, "%2X", out); + ++out; + in += 3; + length -= 3; + break; + + case '_': + *out++ = ' '; + in++; + length--; + break; + + default: + if (*in & 0x80) goto badsyntax; + *out++ = *in++; + length--; + } + } + *out++ = '\0'; + + for (out = dest; *out ; ++out) { + if (*out == '\t') + *out = ' '; + } + + return dest; + + badsyntax: + PR_Free(dest); + return nsnull; +} + +// check if input is HZ (a 7bit encoding for simplified Chinese : RFC 1842)) +// or has ESC which may be an indication that it's in one of many ISO +// 2022 7bit encodings (e.g. ISO-2022-JP(-2)/CN : see RFC 1468, 1922, 1554). +// static +PRBool Is7bitNonAsciiString(const char *input, PRUint32 len) +{ + PRInt32 c; + + enum { hz_initial, // No HZ seen yet + hz_escaped, // Inside an HZ ~{ escape sequence + hz_seen, // Have seen at least one complete HZ sequence + hz_notpresent // Have seen something that is not legal HZ + } hz_state; + + hz_state = hz_initial; + while (len) { + c = PRUint8(*input++); + len--; + if (c & 0x80) return PR_FALSE; + if (c == 0x1B) return PR_TRUE; + if (c == '~') { + switch (hz_state) { + case hz_initial: + case hz_seen: + if (*input == '{') { + hz_state = hz_escaped; + } else if (*input == '~') { + // ~~ is the HZ encoding of ~. Skip over second ~ as well + hz_state = hz_seen; + input++; + len--; + } else { + hz_state = hz_notpresent; + } + break; + + case hz_escaped: + if (*input == '}') hz_state = hz_seen; + break; + default: + break; + } + } + } + return hz_state == hz_seen; +} + +#define REPLACEMENT_CHAR "\357\277\275" // EF BF BD (UTF-8 encoding of U+FFFD) + +// copy 'raw' sequences of octets in aInput to aOutput. +// If aDefaultCharset is specified, the input is assumed to be in the +// charset and converted to UTF-8. Otherwise, a blind copy is made. +// If aDefaultCharset is specified, but the conversion to UTF-8 +// is not successful, each octet is replaced by Unicode replacement +// chars. *aOutput is advanced by the number of output octets. +// static +void CopyRawHeader(const char *aInput, PRUint32 aLen, + const char *aDefaultCharset, nsACString &aOutput) +{ + PRInt32 c; + + // If aDefaultCharset is not specified, make a blind copy. + if (!aDefaultCharset || !*aDefaultCharset) { + aOutput.Append(aInput, aLen); + return; + } + + // Copy as long as it's US-ASCII. An ESC may indicate ISO 2022 + // A ~ may indicate it is HZ + while (aLen && (c = PRUint8(*aInput++)) != 0x1B && c != '~' && !(c & 0x80)) { + aOutput.Append(char(c)); + aLen--; + } + if (!aLen) { + return; + } + aInput--; + + // skip ASCIIness/UTF8ness test if aInput is supected to be a 7bit non-ascii + // string and aDefaultCharset is a 7bit non-ascii charset. + PRBool skipCheck = (c == 0x1B || c == '~') && + IS_7BIT_NON_ASCII_CHARSET(aDefaultCharset); + + nsresult rv; + // If not UTF-8, treat as default charset + nsCOMPtr + cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID, &rv)); + nsCAutoString utf8Text; + if (NS_SUCCEEDED(rv) && + NS_SUCCEEDED( + cvtUTF8->ConvertStringToUTF8(nsDependentCString(aInput, aLen), + aDefaultCharset, skipCheck, utf8Text))) { + aOutput.Append(utf8Text); + } else { // replace each octet with Unicode replacement char in UTF-8. + for (PRUint32 i = 0; i < aLen; i++) { + aOutput.Append(REPLACEMENT_CHAR); + } + } +} + +static const char especials[] = "()<>@,;:\\\"/[]?.="; + +// |decode_mime_part2_str| taken from comi18n.c +// Decode RFC2047-encoded words in the input and convert the result to UTF-8. +// If aOverrideCharset is true, charset in RFC2047-encoded words is +// ignored and aDefaultCharset is assumed, instead. aDefaultCharset +// is also used to convert raw octets (without RFC 2047 encoding) to UTF-8. +//static +nsresult DecodeRFC2047Str(const char *aHeader, const char *aDefaultCharset, + PRBool aOverrideCharset, nsACString &aResult) +{ + const char *p, *q, *r; + char *decodedText; + const char *begin; // tracking pointer for where we are in the input buffer + PRInt32 isLastEncodedWord = 0; + const char *charsetStart, *charsetEnd; + char charset[80]; + + // initialize charset name to an empty string + charset[0] = '\0'; + + begin = aHeader; + + // To avoid buffer realloc, if possible, set capacity in advance. No + // matter what, more than 3x expansion can never happen for all charsets + // supported by Mozilla. SCSU/BCSU with the sliding window set to a + // non-BMP block may be exceptions, but Mozilla does not support them. + // Neither any known mail/news program use them. Even if there's, we're + // safe because we don't use a raw *char any more. + aResult.SetCapacity(3 * strlen(aHeader)); + + while ((p = PL_strstr(begin, "=?")) != 0) { + if (isLastEncodedWord) { + // See if it's all whitespace. + for (q = begin; q < p; ++q) { + if (!PL_strchr(" \t\r\n", *q)) break; + } + } + + if (!isLastEncodedWord || q < p) { + // copy the part before the encoded-word + CopyRawHeader(begin, p - begin, aDefaultCharset, aResult); + begin = p; + } + + p += 2; + + // Get charset info + charsetStart = p; + charsetEnd = 0; + for (q = p; *q != '?'; q++) { + if (*q <= ' ' || PL_strchr(especials, *q)) { + goto badsyntax; + } + + // RFC 2231 section 5 + if (!charsetEnd && *q == '*') { + charsetEnd = q; + } + } + if (!charsetEnd) { + charsetEnd = q; + } + + // Check for too-long charset name + if (PRUint32(charsetEnd - charsetStart) >= sizeof(charset)) + goto badsyntax; + + memcpy(charset, charsetStart, charsetEnd - charsetStart); + charset[charsetEnd - charsetStart] = 0; + + q++; + if (*q != 'Q' && *q != 'q' && *q != 'B' && *q != 'b') + goto badsyntax; + + if (q[1] != '?') + goto badsyntax; + + r = q; + for (r = q + 2; *r != '?'; r++) { + if (*r < ' ') goto badsyntax; + } + if (r[1] != '=') + goto badsyntax; + else if (r == q + 2) { + // it's empty, skip + begin = r + 2; + isLastEncodedWord = 1; + continue; + } + + if(*q == 'Q' || *q == 'q') + decodedText = DecodeQ(q + 2, r - (q + 2)); + else + decodedText = PL_Base64Decode(q + 2, r - (q + 2), nsnull); + + if (decodedText == nsnull) + goto badsyntax; + + // Override charset if requested. Never override labeled UTF-8. + // Use default charset instead of UNKNOWN-8BIT + if ((aOverrideCharset && 0 != nsCRT::strcasecmp(charset, "UTF-8")) || + (aDefaultCharset && 0 == nsCRT::strcasecmp(charset, "UNKNOWN-8BIT"))) { + PL_strncpy(charset, aDefaultCharset, sizeof(charset) - 1); + charset[sizeof(charset) - 1] = '\0'; + } + + { + nsresult rv; + nsCOMPtr + cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID, &rv)); + nsCAutoString utf8Text; + // skip ASCIIness/UTF8ness test if aCharset is 7bit non-ascii charset. + if (NS_SUCCEEDED(rv) && + NS_SUCCEEDED( + cvtUTF8->ConvertStringToUTF8(nsDependentCString(decodedText), + charset, IS_7BIT_NON_ASCII_CHARSET(charset), utf8Text))) { + aResult.Append(utf8Text); + } else { + aResult.Append(REPLACEMENT_CHAR); + } + } + PR_Free(decodedText); + begin = r + 2; + isLastEncodedWord = 1; + continue; + + badsyntax: + // copy the part before the encoded-word + aResult.Append(begin, p - begin); + begin = p; + isLastEncodedWord = 0; + } + + // put the tail back + CopyRawHeader(begin, strlen(begin), aDefaultCharset, aResult); + + nsCAutoString tempStr(aResult); + tempStr.ReplaceChar('\t', ' '); + aResult = tempStr; + + return NS_OK; +} + diff --git a/mozilla/netwerk/mime/src/nsMIMEHeaderParamImpl.h b/mozilla/netwerk/mime/src/nsMIMEHeaderParamImpl.h new file mode 100644 index 00000000000..a28cffcbdda --- /dev/null +++ b/mozilla/netwerk/mime/src/nsMIMEHeaderParamImpl.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Jungshik Shin + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIMIMEHeaderParam.h" + +#ifndef __nsmimeheaderparamimpl_h___ +#define __nsmimeheaderparamimpl_h___ +class nsMIMEHeaderParamImpl : public nsIMIMEHeaderParam +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIMIMEHEADERPARAM + + nsMIMEHeaderParamImpl() {}; + virtual ~nsMIMEHeaderParamImpl() {}; +}; + +#endif + diff --git a/mozilla/uriloader/base/Makefile.in b/mozilla/uriloader/base/Makefile.in index 65f41c6659e..98d73f675af 100644 --- a/mozilla/uriloader/base/Makefile.in +++ b/mozilla/uriloader/base/Makefile.in @@ -45,6 +45,7 @@ REQUIRES = xpcom \ intl \ webshell \ xpconnect \ + mimetype \ $(NULL) CPPSRCS = \ diff --git a/mozilla/uriloader/base/nsURILoader.cpp b/mozilla/uriloader/base/nsURILoader.cpp index 3aa2083d63b..4eabf1b33b8 100644 --- a/mozilla/uriloader/base/nsURILoader.cpp +++ b/mozilla/uriloader/base/nsURILoader.cpp @@ -71,6 +71,9 @@ #include "nsICategoryManager.h" #include "nsCExternalHandlerService.h" // contains contractids for the helper app service +#include "nsIMIMEHeaderParam.h" +#include "nsNetCID.h" + static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID); @@ -281,8 +284,6 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports * // could happen because the Content-Disposition header is set so, or, in the // future, because the user has specified external handling for the MIME // type. - // XXXbz we need a utility function in necko for parsing content-disposition - // headers, methinks. PRBool forceExternalHandling = PR_FALSE; nsCAutoString disposition; nsCOMPtr httpChannel(do_QueryInterface(request)); @@ -302,39 +303,22 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports * if (NS_SUCCEEDED(rv) && !disposition.IsEmpty()) { - nsCAutoString::const_iterator start, end; - disposition.BeginReading(start); - disposition.EndReading(end); - // skip leading whitespace - while (start != end && nsCRT::IsAsciiSpace(*start)) + nsCOMPtr mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { - ++start; - } - nsCAutoString::const_iterator iter = start; - // walk forward till we hit the next whitespace, semicolon, or - // equals sign - while (iter != end && *iter != ';' && *iter != '=' && - !nsCRT::IsAsciiSpace(*iter)) - { - ++iter; - } - - if (start != iter) - { - const nsACString & dispToken = Substring(start, iter); + nsAutoString dispToken; + // Get the disposition type + rv = mimehdrpar->GetParameter(disposition, "", NS_LITERAL_CSTRING(""), + PR_FALSE, nsnull, dispToken); // RFC 2183, section 2.8 says that an unknown disposition // value should be treated as "attachment" - if (!dispToken.Equals(NS_LITERAL_CSTRING("inline"), - nsCaseInsensitiveCStringComparator()) && + if (NS_FAILED(rv) || !dispToken.EqualsIgnoreCase("inline",6) && // Broken sites just send // Content-Disposition: filename="file" // without a disposition token... screen those out. - !dispToken.Equals(NS_LITERAL_CSTRING("filename"), - nsCaseInsensitiveCStringComparator())) - { + !dispToken.EqualsIgnoreCase("filename", 8)) // We have a content-disposition of "attachment" or unknown forceExternalHandling = PR_TRUE; - } } } diff --git a/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp b/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp index 832000b2dcd..ae5813de950 100644 --- a/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -1,5 +1,5 @@ /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * + * vim:expandtab:shiftwidth=2:tabstop=3: * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of @@ -80,6 +80,7 @@ #include "nsIDOMWindow.h" #include "nsITextToSubURI.h" +#include "nsIMIMEHeaderParam.h" #include "nsIPrefService.h" @@ -991,90 +992,54 @@ void nsExternalAppHandler::ExtractSuggestedFileNameFromChannel(nsIChannel* aChan // disposition-type < ; name=value >* < ; filename=value > < ; name=value >* if ( NS_SUCCEEDED( rv ) && !disp.IsEmpty() ) { - nsCAutoString::const_iterator start, end; - disp.BeginReading(start); - disp.EndReading(end); - // skip leading whitespace - while (start != end && nsCRT::IsAsciiSpace(*start)) { - ++start; - } - nsCAutoString::const_iterator iter = start; - // walk forward till we hit the next whitespace, semicolon, or - // equals sign - while (iter != end && *iter != ';' && *iter != '=' && - !nsCRT::IsAsciiSpace(*iter)) { - ++iter; - } - - if (start != iter) { - const nsACString & dispToken = Substring(start, iter); - // RFC 2183, section 2.8 says that an unknown disposition - // value should be treated as "attachment" - if (!dispToken.Equals(NS_LITERAL_CSTRING("inline"), - nsCaseInsensitiveCStringComparator()) && - // Broken sites just send - // Content-Disposition: filename="file" - // without a disposition token... screen those out. - !dispToken.Equals(NS_LITERAL_CSTRING("filename"), - nsCaseInsensitiveCStringComparator())) { - // We have a content-disposition of "attachment" or unknown - mHandlingAttachment = PR_TRUE; - } + nsCOMPtr mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return; + nsAutoString dispToken; + // Get the disposition type + rv = mimehdrpar->GetParameter(disp, "", NS_LITERAL_CSTRING(""), PR_FALSE, + nsnull, dispToken); + // RFC 2183, section 2.8 says that an unknown disposition + // value should be treated as "attachment" + if (NS_FAILED(rv) || !dispToken.EqualsIgnoreCase("inline", 6) && + // Broken sites just send + // Content-Disposition: filename="file" + // without a disposition token... screen those out. + !dispToken.EqualsIgnoreCase("filename", 8)) + { + // We have a content-disposition of "attachment" or unknown + mHandlingAttachment = PR_TRUE; } // We may not have a disposition type listed; some servers suck. // But they could have listed a filename anyway. - disp.BeginReading(start); - iter = end; + nsCOMPtr srcUri; + GetSource(getter_AddRefs(srcUri)); + nsCOMPtr url = do_QueryInterface(srcUri); - if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("filename="), - start, - iter)) - { - // The value is either a string with no whitespace or a string - // in double quotes. See RFC 2183 and bug 66181. + nsCAutoString fallbackCharset; + nsAutoString fileName; + if (url) + url->GetOriginCharset(fallbackCharset); + // Get the value of 'filename' parameter + rv = mimehdrpar->GetParameter(disp, "filename", fallbackCharset, + PR_TRUE, nsnull, fileName); + if (NS_FAILED(rv) || fileName.IsEmpty()) + // Try 'name' parameter, instead. + rv = mimehdrpar->GetParameter(disp, "name",fallbackCharset, PR_TRUE, + nsnull, fileName); + if (NS_FAILED(rv) || fileName.IsEmpty()) + return; - // Search for the ';' if it's not in double quotes, then walk - // back past any whitespace - if (iter != end) { // otherwise our filename is empty... - char endChar = ';'; - if (*iter == '"') { - endChar = '"'; - ++iter; // since we had iter < end, this is not running us past the end of the string - } - start = iter; - FindCharInReadable(endChar, iter, end); - // Now start points at the beginning of the filename. iter - // points to just past its end if the name was quoted. If we - // looked for a semicolon, we need to step back past - // whitespace. - if (endChar == ';' && iter != start) { - --iter; - while (iter != start && nsCRT::IsAsciiSpace(*iter)) { - --iter; - } - ++iter; - } - - if (iter != start) { // not empty - // ONLY if we got here, will we remember the suggested file name... - // The filename must be ASCII, see RFC 2231 - // We ignore the filename in the header if the filename contains raw 8bit. - // (and keep the URI filename instead). - const nsACString& newFileName = Substring(start, iter); - if (IsASCII(newFileName)) - CopyASCIItoUCS2(newFileName, mSuggestedFileName); + mSuggestedFileName = fileName; #ifdef XP_WIN - // Make sure extension is still correct. - EnsureSuggestedFileName(); + // Make sure extension is still correct. + EnsureSuggestedFileName(); #endif - // replace platform specific path separator and illegal characters to avoid any confusion - mSuggestedFileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '-'); - } - } - } // if we found a file name in the header disposition field + // replace platform specific path separator and illegal characters to avoid any confusion + mSuggestedFileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '-'); } // we had a disp header }