mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
758f79ff72
This fixes many crashes caused by illegal uses of the nsStdURL. This also allows a plugable protocol to provide their own url parser.
533 lines
18 KiB
C++
533 lines
18 KiB
C++
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* 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 Netscape are
|
|
* Copyright (C) 1999 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
#include "nsIServiceManager.h"
|
|
#ifndef XPCOM_STANDALONE
|
|
#include "nsIPlatformCharset.h"
|
|
#include "nsICharsetConverterManager.h"
|
|
#include "nsIUnicodeEncoder.h"
|
|
#include "nsIUnicodeDecoder.h"
|
|
#include "nsIUnicodeEncoder.h"
|
|
#include "nsIUnicodeDecoder.h"
|
|
#include "nsIURLParser.h"
|
|
#endif /* XPCOM_STANDALONE */
|
|
|
|
#include "nsFileSpec.h" // evil ftang hack
|
|
|
|
#include "nsLocalFile.h" // includes platform-specific headers
|
|
|
|
#include "nsString.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsXPIDLString.h"
|
|
|
|
#ifndef XPCOM_STANDALONE
|
|
static NS_DEFINE_CID(kStdURLParserCID, NS_STANDARDURLPARSER_CID);
|
|
#endif
|
|
|
|
|
|
class nsFSStringConversion {
|
|
public:
|
|
static void CleanUp();
|
|
static nsresult UCSToNewFS( const PRUnichar* aIn, char** aOut);
|
|
static nsresult FSToNewUCS( const char* aIn, PRUnichar** aOut);
|
|
|
|
private:
|
|
static nsresult PrepareFSCharset();
|
|
static nsresult PrepareEncoder();
|
|
static nsresult PrepareDecoder();
|
|
static nsString* mFSCharset;
|
|
#ifndef XPCOM_STANDALONE
|
|
static nsIUnicodeEncoder* mEncoder;
|
|
static nsIUnicodeDecoder* mDecoder;
|
|
#endif /* XPCOM_STANDALONE */
|
|
};
|
|
|
|
nsString* nsFSStringConversion::mFSCharset = nsnull;
|
|
#ifndef XPCOM_STANDALONE
|
|
nsIUnicodeEncoder* nsFSStringConversion::mEncoder = nsnull;
|
|
nsIUnicodeDecoder* nsFSStringConversion::mDecoder = nsnull;
|
|
#endif /* XPCOM_STANDALONE */
|
|
|
|
#define GET_UCS( func , arg) \
|
|
{ \
|
|
char* tmp; \
|
|
nsresult res; \
|
|
if(NS_SUCCEEDED(res = (func)(&tmp))) { \
|
|
if(NS_SUCCEEDED(res = nsFSStringConversion::FSToNewUCS(tmp, (arg)))){ \
|
|
nsMemory::Free(tmp); \
|
|
} \
|
|
} \
|
|
return res; \
|
|
}
|
|
#define VOID_SET_UCS( func , arg, assertion_msg) \
|
|
{ \
|
|
char* tmp; \
|
|
nsresult res; \
|
|
if(NS_SUCCEEDED(res = nsFSStringConversion::UCSToNewFS((arg), &tmp))) { \
|
|
(func)(tmp); \
|
|
nsMemory::Free(tmp); \
|
|
} \
|
|
NS_ASSERTION(NS_SUCCEEDED(res), assertion_msg); \
|
|
}
|
|
#define SET_UCS( func , arg) \
|
|
{ \
|
|
char* tmp; \
|
|
nsresult res; \
|
|
if(NS_SUCCEEDED(res = nsFSStringConversion::UCSToNewFS((arg), &tmp))) { \
|
|
res = (func)(tmp); \
|
|
nsMemory::Free(tmp); \
|
|
} \
|
|
return res; \
|
|
}
|
|
#define SET_UCS_2ARGS_2( func , arg1, arg2) \
|
|
{ \
|
|
char* tmp; \
|
|
nsresult res; \
|
|
if(NS_SUCCEEDED(res = nsFSStringConversion::UCSToNewFS((arg2), &tmp))){ \
|
|
res = (func)(arg1, tmp); \
|
|
nsMemory::Free(tmp); \
|
|
} \
|
|
return res; \
|
|
}
|
|
#define SET_UCS_2ARGS_1( func , path, followLinks, result) \
|
|
{ \
|
|
char* tmp; \
|
|
nsresult res; \
|
|
if(NS_SUCCEEDED(res = nsFSStringConversion::UCSToNewFS((path), &tmp))){ \
|
|
res = (func)(tmp, followLinks, result); \
|
|
nsMemory::Free(tmp); \
|
|
} \
|
|
return res; \
|
|
}
|
|
|
|
void
|
|
nsFSStringConversion::CleanUp()
|
|
{
|
|
#ifndef XPCOM_STANDALONE
|
|
NS_IF_RELEASE(mEncoder);
|
|
NS_IF_RELEASE(mDecoder);
|
|
#endif /* XPCOM_STANDALONE */
|
|
}
|
|
|
|
/* static */ nsresult
|
|
nsFSStringConversion::PrepareFSCharset()
|
|
{
|
|
nsresult res = NS_ERROR_NOT_IMPLEMENTED;
|
|
#ifndef XPCOM_STANDALONE
|
|
res = NS_OK;
|
|
if(!mFSCharset)
|
|
{
|
|
// lazy eval of the file system charset
|
|
NS_WITH_SERVICE(nsIPlatformCharset, pcharset, NS_PLATFORMCHARSET_CONTRACTID, &res);
|
|
if (NS_SUCCEEDED(res) && !pcharset) res = NS_ERROR_NULL_POINTER;
|
|
if (NS_SUCCEEDED(res)) {
|
|
mFSCharset = new nsString();
|
|
if (!mFSCharset)
|
|
res = NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
if(NS_SUCCEEDED(res)) {
|
|
res = pcharset->GetCharset(kPlatformCharsetSel_FileName, *mFSCharset);
|
|
}
|
|
if (NS_FAILED(res)) {
|
|
NS_WARNING("cannot get platform charset");
|
|
}
|
|
}
|
|
#endif /* XPCOM_STANDALONE */
|
|
return res;
|
|
}
|
|
/* static */ nsresult
|
|
nsFSStringConversion::PrepareEncoder()
|
|
{
|
|
nsresult res = NS_ERROR_NOT_IMPLEMENTED;
|
|
#ifndef XPCOM_STANDALONE
|
|
res = NS_OK;
|
|
if(! mEncoder)
|
|
{
|
|
res = PrepareFSCharset();
|
|
if(NS_SUCCEEDED(res)) {
|
|
NS_WITH_SERVICE(nsICharsetConverterManager,
|
|
ucmgr, NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
|
|
NS_ASSERTION((NS_SUCCEEDED(res) && ucmgr),
|
|
"cannot get charset converter manager ");
|
|
if(NS_SUCCEEDED(res) && ucmgr)
|
|
res = ucmgr->GetUnicodeEncoder( mFSCharset, &mEncoder);
|
|
NS_ASSERTION((NS_SUCCEEDED(res) && mEncoder),
|
|
"cannot find the unicode encoder");
|
|
}
|
|
}
|
|
#endif /* XPCOM_STANDALONE */
|
|
return res;
|
|
}
|
|
/* static */ nsresult
|
|
nsFSStringConversion::PrepareDecoder()
|
|
{
|
|
nsresult res = NS_ERROR_NOT_IMPLEMENTED;
|
|
#ifndef XPCOM_STANDALONE
|
|
res = NS_OK;
|
|
if(! mDecoder)
|
|
{
|
|
res = PrepareFSCharset();
|
|
if(NS_SUCCEEDED(res)) {
|
|
NS_WITH_SERVICE(nsICharsetConverterManager,
|
|
ucmgr, NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
|
|
NS_ASSERTION((NS_SUCCEEDED(res) && ucmgr),
|
|
"cannot get charset converter manager ");
|
|
if(NS_SUCCEEDED(res) && ucmgr)
|
|
res = ucmgr->GetUnicodeDecoder( mFSCharset, &mDecoder);
|
|
NS_ASSERTION((NS_SUCCEEDED(res) && mDecoder),
|
|
"cannot find the unicode decoder");
|
|
}
|
|
}
|
|
#endif /* XPCOM_STANDALONE */
|
|
return res;
|
|
}
|
|
/* static */ nsresult
|
|
nsFSStringConversion::UCSToNewFS( const PRUnichar* aIn, char** aOut)
|
|
{
|
|
nsresult res = NS_ERROR_NOT_IMPLEMENTED;
|
|
#ifndef XPCOM_STANDALONE
|
|
res = PrepareEncoder();
|
|
if(NS_SUCCEEDED(res))
|
|
{
|
|
PRInt32 inLength = nsCRT::strlen(aIn);
|
|
PRInt32 outLength;
|
|
res= mEncoder->GetMaxLength(aIn, inLength,&outLength);
|
|
if(NS_SUCCEEDED(res)) {
|
|
*aOut = (char*)nsMemory::Alloc(outLength+1);
|
|
if(nsnull != *aOut) {
|
|
res = mEncoder->Convert(aIn, &inLength, *aOut, &outLength);
|
|
if(NS_SUCCEEDED(res)) {
|
|
(*aOut)[outLength] = '\0';
|
|
} else {
|
|
nsMemory::Free(*aOut);
|
|
*aOut = nsnull;
|
|
}
|
|
} else {
|
|
res = NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
#endif /* XPCOM_STANDALONE */
|
|
return res;
|
|
}
|
|
/* static */ nsresult
|
|
nsFSStringConversion::FSToNewUCS( const char* aIn, PRUnichar** aOut)
|
|
{
|
|
nsresult res = NS_ERROR_NOT_IMPLEMENTED;
|
|
#ifndef XPCOM_STANDALONE
|
|
res = PrepareDecoder();
|
|
if(NS_SUCCEEDED(res))
|
|
{
|
|
PRInt32 inLength = nsCRT::strlen(aIn);
|
|
PRInt32 outLength;
|
|
res= mDecoder->GetMaxLength(aIn, inLength,&outLength);
|
|
if(NS_SUCCEEDED(res)) {
|
|
*aOut = (PRUnichar*)nsMemory::Alloc(2*(outLength+1));
|
|
if(nsnull != *aOut) {
|
|
res = mDecoder->Convert(aIn, &inLength, *aOut, &outLength);
|
|
if(NS_SUCCEEDED(res)) {
|
|
(*aOut)[outLength] = '\0';
|
|
} else {
|
|
nsMemory::Free(*aOut);
|
|
*aOut = nsnull;
|
|
}
|
|
} else {
|
|
res = NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
#endif /* XPCOM_STANDALONE */
|
|
return res;
|
|
}
|
|
|
|
void NS_StartupLocalFile()
|
|
{
|
|
#ifdef XP_WIN
|
|
CoInitialize(NULL); // FIX: we should probably move somewhere higher up during startup
|
|
#endif
|
|
}
|
|
|
|
void NS_ShutdownLocalFile()
|
|
{
|
|
#ifndef XPCOM_STANDALONE
|
|
nsFSStringConversion::CleanUp();
|
|
#endif /* XPCOM_STANDALONE */
|
|
|
|
#ifdef XP_WIN
|
|
CoUninitialize();
|
|
#endif
|
|
|
|
}
|
|
|
|
// Unicode interface Wrapper
|
|
NS_IMETHODIMP
|
|
nsLocalFile::InitWithUnicodePath(const PRUnichar *filePath)
|
|
{
|
|
SET_UCS(InitWithPath, filePath);
|
|
}
|
|
NS_IMETHODIMP
|
|
nsLocalFile::AppendUnicode(const PRUnichar *node)
|
|
{
|
|
SET_UCS( Append , node);
|
|
}
|
|
NS_IMETHODIMP
|
|
nsLocalFile::AppendRelativeUnicodePath(const PRUnichar *node)
|
|
{
|
|
SET_UCS( AppendRelativePath , node);
|
|
}
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetUnicodeLeafName(PRUnichar **aLeafName)
|
|
{
|
|
GET_UCS(GetLeafName, aLeafName);
|
|
}
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetUnicodeLeafName(const PRUnichar * aLeafName)
|
|
{
|
|
SET_UCS( SetLeafName , aLeafName);
|
|
}
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetUnicodePath(PRUnichar **_retval)
|
|
{
|
|
GET_UCS(GetPath, _retval);
|
|
}
|
|
NS_IMETHODIMP
|
|
nsLocalFile::CopyToUnicode(nsIFile *newParentDir, const PRUnichar *newName)
|
|
{
|
|
SET_UCS_2ARGS_2( CopyTo , newParentDir, newName);
|
|
}
|
|
NS_IMETHODIMP
|
|
nsLocalFile::CopyToFollowingLinksUnicode(nsIFile *newParentDir, const PRUnichar *newName)
|
|
{
|
|
SET_UCS_2ARGS_2( CopyToFollowingLinks , newParentDir, newName);
|
|
}
|
|
NS_IMETHODIMP
|
|
nsLocalFile::MoveToUnicode(nsIFile *newParentDir, const PRUnichar *newName)
|
|
{
|
|
SET_UCS_2ARGS_2( MoveTo , newParentDir, newName);
|
|
}
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetUnicodeTarget(PRUnichar **_retval)
|
|
{
|
|
GET_UCS(GetTarget, _retval);
|
|
}
|
|
nsresult
|
|
NS_NewUnicodeLocalFile(const PRUnichar* path, PRBool followLinks, nsILocalFile* *result)
|
|
{
|
|
SET_UCS_2ARGS_1( NS_NewLocalFile, path, followLinks, result)
|
|
}
|
|
// should work on Macintosh, Unix, and Win32.
|
|
#define kMaxFilenameLength 31
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::CreateUnique(const char* suggestedName, PRUint32 type, PRUint32 attributes)
|
|
{
|
|
nsresult rv = Create(type, attributes);
|
|
|
|
if (NS_SUCCEEDED(rv)) return NS_OK;
|
|
if (rv != NS_ERROR_FILE_ALREADY_EXISTS) return rv;
|
|
|
|
char* leafName;
|
|
rv = GetLeafName(&leafName);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
char* lastDot = strrchr(leafName, '.');
|
|
char suffix[kMaxFilenameLength + 1] = "";
|
|
if (lastDot)
|
|
{
|
|
strncpy(suffix, lastDot, kMaxFilenameLength); // include '.'
|
|
suffix[kMaxFilenameLength] = 0; // make sure it's null terminated
|
|
*lastDot = '\0'; // strip suffix and dot.
|
|
}
|
|
|
|
// 27 should work on Macintosh, Unix, and Win32.
|
|
const int maxRootLength = 27 - nsCRT::strlen(suffix) - 1;
|
|
|
|
if ((int)nsCRT::strlen(leafName) > (int)maxRootLength)
|
|
leafName[maxRootLength] = '\0';
|
|
|
|
for (short indx = 1; indx < 10000; indx++)
|
|
{
|
|
// start with "Picture-1.jpg" after "Picture.jpg" exists
|
|
char newName[kMaxFilenameLength + 1];
|
|
sprintf(newName, "%s-%d%s", leafName, indx, suffix);
|
|
SetLeafName(newName);
|
|
|
|
rv = Create(type, attributes);
|
|
|
|
if (NS_SUCCEEDED(rv) || rv != NS_ERROR_FILE_ALREADY_EXISTS)
|
|
{
|
|
nsMemory::Free(leafName);
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
nsMemory::Free(leafName);
|
|
// The disk is full, sort of
|
|
return NS_ERROR_FILE_TOO_BIG;
|
|
}
|
|
|
|
nsresult nsLocalFile::ParseURL(const char* inURL, char **outHost, char **outDirectory,
|
|
char **outFileBaseName, char **outFileExtension)
|
|
{
|
|
nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
#ifndef XPCOM_STANDALONE
|
|
NS_ENSURE_ARG(inURL);
|
|
NS_ENSURE_ARG_POINTER(outHost);
|
|
*outHost = nsnull;
|
|
NS_ENSURE_ARG_POINTER(outDirectory);
|
|
*outDirectory = nsnull;
|
|
NS_ENSURE_ARG_POINTER(outFileBaseName);
|
|
*outFileBaseName = nsnull;
|
|
NS_ENSURE_ARG_POINTER(outFileExtension);
|
|
*outFileExtension = nsnull;
|
|
|
|
rv = NS_OK;
|
|
char* eSpec = nsnull;
|
|
eSpec = nsCRT::strdup(inURL);
|
|
if (!eSpec)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
// Skip leading spaces and control-characters
|
|
char* fwdPtr= (char*) eSpec;
|
|
while (fwdPtr && (*fwdPtr > '\0') && (*fwdPtr <= ' '))
|
|
fwdPtr++;
|
|
// Remove trailing spaces and control-characters
|
|
if (fwdPtr) {
|
|
char* bckPtr= (char*)fwdPtr + PL_strlen(fwdPtr) -1;
|
|
if (*bckPtr > '\0' && *bckPtr <= ' ') {
|
|
while ((bckPtr-fwdPtr) >= 0 && (*bckPtr <= ' ')) {
|
|
bckPtr--;
|
|
}
|
|
*(bckPtr+1) = '\0';
|
|
}
|
|
}
|
|
|
|
NS_WITH_SERVICE(nsIURLParser, parser, kStdURLParserCID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsXPIDLCString ePath;
|
|
nsXPIDLCString scheme, username, password, host;
|
|
PRInt32 mPort;
|
|
|
|
// Parse the spec
|
|
rv = parser->ParseAtScheme(eSpec, getter_Copies(scheme), getter_Copies(username),
|
|
getter_Copies(password), outHost, &mPort,
|
|
getter_Copies(ePath));
|
|
|
|
// if this isn't a file: URL, then we can't deal
|
|
if (NS_FAILED(rv) || nsCRT::strcasecmp(scheme, "file") != 0) {
|
|
CRTFREEIF(*outHost);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsXPIDLCString param, query, ref;
|
|
|
|
// Now parse the path
|
|
rv = parser->ParseAtDirectory(ePath, outDirectory, outFileBaseName, outFileExtension,
|
|
getter_Copies(param), getter_Copies(query), getter_Copies(ref));
|
|
if (NS_FAILED(rv)) {
|
|
CRTFREEIF(*outDirectory);
|
|
CRTFREEIF(*outFileBaseName);
|
|
CRTFREEIF(*outFileExtension);
|
|
return rv;
|
|
}
|
|
|
|
// If any of the components are non-NULL but empty, free them
|
|
if (*outHost && !nsCRT::strlen(*outHost))
|
|
CRTFREEIF(*outHost);
|
|
if (*outDirectory && !nsCRT::strlen(*outDirectory))
|
|
CRTFREEIF(*outDirectory);
|
|
if (*outFileBaseName && !nsCRT::strlen(*outFileBaseName))
|
|
CRTFREEIF(*outFileBaseName);
|
|
if (*outFileExtension && !nsCRT::strlen(*outFileExtension))
|
|
CRTFREEIF(*outFileExtension);
|
|
#endif /* XPCOM_STANDALONE */
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
// E_V_I_L Below! E_V_I_L Below! E_V_I_L Below! E_V_I_L Below!
|
|
|
|
|
|
// FTANG need to move this crap out of here. Mixing nsFileSpec here is
|
|
// E_V_I_L
|
|
|
|
// ==================================================================
|
|
// nsFileSpec stuff . put here untill nsFileSpec get obsoleted
|
|
// ==================================================================
|
|
|
|
void nsFileSpec::operator = (const nsString& inNativePath)
|
|
{
|
|
char* tmp;
|
|
nsresult res;
|
|
if(NS_SUCCEEDED(res = nsFSStringConversion::UCSToNewFS((inNativePath.GetUnicode()), &tmp))) {
|
|
*this = tmp;
|
|
nsMemory::Free(tmp);
|
|
}
|
|
mError = res;
|
|
NS_ASSERTION(NS_SUCCEEDED(res), "nsFileSpec = filed");
|
|
}
|
|
nsFileSpec nsFileSpec::operator + (const nsString& inRelativeUnixPath) const
|
|
{
|
|
nsFileSpec resultSpec;
|
|
char* tmp;
|
|
nsresult res;
|
|
if(NS_SUCCEEDED(res = nsFSStringConversion::UCSToNewFS((inRelativeUnixPath.GetUnicode()), &tmp))) {
|
|
resultSpec = *this;
|
|
resultSpec += tmp;
|
|
nsMemory::Free(tmp);
|
|
}
|
|
NS_ASSERTION(NS_SUCCEEDED(res), "nsFileSpec + filed");
|
|
return resultSpec;
|
|
}
|
|
void nsFileSpec::operator += (const nsString& inRelativeUnixPath)
|
|
{
|
|
char* tmp;
|
|
nsresult res;
|
|
if(NS_SUCCEEDED(res = nsFSStringConversion::UCSToNewFS((inRelativeUnixPath.GetUnicode()), &tmp))) {
|
|
*this += tmp;
|
|
nsMemory::Free(tmp);
|
|
}
|
|
mError = res;
|
|
NS_ASSERTION(NS_SUCCEEDED(res), "nsFileSpec + filed");
|
|
}
|
|
|
|
void nsFileSpec::SetLeafName (const nsString& inLeafName)
|
|
{
|
|
VOID_SET_UCS( SetLeafName , inLeafName.GetUnicode(),"nsFileSpec::SetLeafName failed");
|
|
}
|
|
void nsFileSpec::MakeUnique(const nsString& inSuggestedLeafName)
|
|
{
|
|
VOID_SET_UCS( MakeUnique,inSuggestedLeafName.GetUnicode(),"nsFileSpec::MakeUnique failed");
|
|
}
|
|
nsresult nsFileSpec::Rename(const nsString& inNewName)
|
|
{
|
|
SET_UCS( Rename , inNewName.GetUnicode());
|
|
}
|
|
nsresult nsFileSpec::Execute(const nsString& args) const
|
|
{
|
|
SET_UCS( Execute , args.GetUnicode());
|
|
}
|