bug 167265 : add to necko Content-Disposition header handling per RFC 2231 (with

fallbacks to RFC 2047 and raw 8bit chars in |aOriginCharset| ) necessary for
i18nized filename support (when downloading files via http) :
r=cbiesinger, sr=alecf
This commit is contained in:
jshin%mailaps.org 2003-06-12 21:57:49 +00:00
Родитель fccaeaf76c
Коммит ecc35790fe
23 изменённых файлов: 1524 добавлений и 847 удалений

Просмотреть файл

@ -34,6 +34,7 @@ XPIDLSRCS = \
nsICurrentCharsetListener.idl \
nsICharsetConverterManager.idl \
nsIScriptableUConv.idl \
nsIUTF8ConverterService.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -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 <jshin@mailaps.org>.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s:
* Naoki Hotta <nhotta@netscape.com>
*
* 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.
*
* <p>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 <strong>blindly</strong>
* 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.
*
* <p>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);
};

Просмотреть файл

@ -40,6 +40,7 @@ EXPORTS = \
nsIConverterInputStream.h \
uconvutil.h \
nsEncoderDecoderUtils.h \
nsUConvCID.h \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -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 <jshin@mailaps.org>.
* 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"

Просмотреть файл

@ -61,6 +61,7 @@ CPPSRCS = \
nsTextToSubURI.cpp \
nsURLProperties.cpp \
nsCharsetConverterManager.cpp \
nsUTF8ConverterService.cpp \
$(NULL)
ifndef MOZ_USE_NATIVE_UCONV

Просмотреть файл

@ -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,

Просмотреть файл

@ -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 <nhotta@netscape.com> and Jungshik Shin <jshin@mailaps.org>.
* 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<nsICharsetConverterManager> ccm;
ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUnicodeDecoder> 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<PRUnichar> 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);
}

Просмотреть файл

@ -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 <jshin@mailaps.org>.
* 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__

Просмотреть файл

@ -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<nsICharsetConverterManager> charsetConverterManager;
charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUnicodeDecoder> 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<nsIUTF8ConverterService>
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);

Просмотреть файл

@ -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 <nsIMIMEHeaderParam> 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)

Просмотреть файл

@ -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
<token> [ ';' <token> '=' <token-or-quoted-string> ]*
*/
nsresult rv;
nsCOMPtr <nsIMIMEHeaderParam> 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 <rhp@netscape.com> 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 <rhp@netscape.com> 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 <rhp@netscape.com> 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 <nsIMIMEHeaderParam> 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;

Просмотреть файл

@ -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)
{

Просмотреть файл

@ -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,

Просмотреть файл

@ -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__

Просмотреть файл

@ -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,

Просмотреть файл

@ -32,6 +32,7 @@ XPIDL_MODULE = mimetype
XPIDLSRCS = \
nsIMIMEService.idl \
nsIMIMEInfo.idl \
nsIMIMEHeaderParam.idl \
$(NULL)
EXPORTS = \

Просмотреть файл

@ -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 <jshin@mailaps.org>
* 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,
* <code>aFallbackCharset</code> and the locale charset as the last
* resort if <code>TryLocaleCharset</code> is set.
*
* <p>
* This method internally invokes <code>getParameterInternal</code>,
* However, it does not stop at decoding RFC 2231 (the task for
* <code>getParameterInternal</code> but tries to cope
* with several non-standard-compliant cases mentioned below.
*
* <p>
* 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 <code>aParamName</code> 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.
* <p>
* For <strong>internal use only</strong>. 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 <code>aParamName</code> 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
* <p>
* For internal use only. The only other place where this needs to be
* invoked is <code>MIME_DecodeMimeHeader</code> 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 <code>aOverrideCharset</code> is set.
* @param aOverrideCharset When set, overrides MIME charset specified
* in RFC 2047 style encoding with <code>aDefaultCharset</code>
* @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.
* <p>
* For internal use only. The only other place where this needs to be
* invoked is <code>mime_decode_filename</code> 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
* <code>aParamValue</code> 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 <code>aOverrideCharset</code> is set.
* @param aOverrideCharset When set, overrides MIME charset specified
* in RFC 2047 style encoding with
* <code>aDefaultCharset</code>
* @return decoded parameter
*/
[noscript]
ACString decodeParameter(in ACString aParamValue,
in string aCharset,
in string aDefaultCharset,
in boolean aOverrideCharset);
};

Просмотреть файл

@ -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

Просмотреть файл

@ -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 <jshin@mailaps.org>
* John G Myers <jgmyers@netscape.com>
* Takayuki Tei <taka@netscape.com>
*
* 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 <string.h>
#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<nsIUTF8ConverterService>
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
// <token> [ ';' <token> '=' <token-or-quoted-string> ]*
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<nsIUTF8ConverterService> 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<nsIUTF8ConverterService>
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<nsIUTF8ConverterService>
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;
}

Просмотреть файл

@ -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 <jshin@mailaps.org>
* 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

Просмотреть файл

@ -45,6 +45,7 @@ REQUIRES = xpcom \
intl \
webshell \
xpconnect \
mimetype \
$(NULL)
CPPSRCS = \

Просмотреть файл

@ -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<nsIHttpChannel> 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<nsIMIMEHeaderParam> 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;
}
}
}

Просмотреть файл

@ -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<nsIMIMEHeaderParam> 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<nsIURI> srcUri;
GetSource(getter_AddRefs(srcUri));
nsCOMPtr<nsIURL> 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
}