mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
486 lines
12 KiB
C++
486 lines
12 KiB
C++
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||
|
*
|
||
|
* The contents of this file are subject to the Netscape Public License
|
||
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
||
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
||
|
* http://www.mozilla.org/NPL/
|
||
|
*
|
||
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* NPL.
|
||
|
*
|
||
|
* The Initial Developer of this code under the NPL is Netscape
|
||
|
* Communications Corporation. Portions created by Netscape are
|
||
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||
|
* Reserved.
|
||
|
*/
|
||
|
#include "nsIURL.h"
|
||
|
#include "nsIInputStream.h"
|
||
|
#include "nsINetService.h"
|
||
|
#include "nsIHttpUrl.h" /* NS_NewHttpUrl(...) */
|
||
|
#include "nsString.h"
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include <stdio.h>/* XXX */
|
||
|
#include "plstr.h"
|
||
|
#include "prprf.h" /* PR_snprintf(...) */
|
||
|
#include "prmem.h" /* PR_Malloc(...) / PR_Free(...) */
|
||
|
|
||
|
#ifdef XP_PC
|
||
|
#include <windows.h>
|
||
|
#endif
|
||
|
|
||
|
char *mangleResourceIntoFileURL(const char* aResourceFileName);
|
||
|
|
||
|
class URLImpl : public nsIURL {
|
||
|
public:
|
||
|
URLImpl(const nsString& aSpec);
|
||
|
URLImpl(const nsIURL* aURL, const nsString& aSpec);
|
||
|
~URLImpl();
|
||
|
|
||
|
NS_DECL_ISUPPORTS
|
||
|
|
||
|
virtual nsIInputStream* Open(PRInt32* aErrorCode);
|
||
|
virtual nsresult Open(nsIStreamListener *aListener);
|
||
|
|
||
|
virtual PRBool operator==(const nsIURL& aURL) const;
|
||
|
virtual nsresult Set(const char *aNewSpec);
|
||
|
|
||
|
virtual const char* GetProtocol() const;
|
||
|
virtual const char* GetHost() const;
|
||
|
virtual const char* GetFile() const;
|
||
|
virtual const char* GetRef() const;
|
||
|
virtual const char* GetSpec() const;
|
||
|
virtual PRInt32 GetPort() const;
|
||
|
|
||
|
virtual void ToString(nsString& aString) const;
|
||
|
|
||
|
char* mSpec;
|
||
|
char* mProtocol;
|
||
|
char* mHost;
|
||
|
char* mFile;
|
||
|
char* mRef;
|
||
|
PRInt32 mPort;
|
||
|
PRBool mOK;
|
||
|
|
||
|
nsISupports* mProtocolUrl;
|
||
|
|
||
|
nsresult ParseURL(const nsIURL* aURL, const nsString& aSpec);
|
||
|
void CreateProtocolURL();
|
||
|
};
|
||
|
|
||
|
URLImpl::URLImpl(const nsString& aSpec)
|
||
|
{
|
||
|
NS_INIT_REFCNT();
|
||
|
mProtocolUrl = nsnull;
|
||
|
|
||
|
ParseURL(nsnull, aSpec);
|
||
|
}
|
||
|
|
||
|
URLImpl::URLImpl(const nsIURL* aURL, const nsString& aSpec)
|
||
|
{
|
||
|
NS_INIT_REFCNT();
|
||
|
mProtocolUrl = nsnull;
|
||
|
|
||
|
ParseURL(aURL, aSpec);
|
||
|
}
|
||
|
|
||
|
NS_IMPL_ADDREF(URLImpl)
|
||
|
NS_IMPL_RELEASE(URLImpl)
|
||
|
|
||
|
NS_DEFINE_IID(kURLIID, NS_IURL_IID);
|
||
|
|
||
|
nsresult URLImpl::QueryInterface(const nsIID &aIID, void** aInstancePtr)
|
||
|
{
|
||
|
if (NULL == aInstancePtr) {
|
||
|
return NS_ERROR_NULL_POINTER;
|
||
|
}
|
||
|
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
|
||
|
if (aIID.Equals(kURLIID)) {
|
||
|
*aInstancePtr = (void*) ((nsIURL*)this);
|
||
|
AddRef();
|
||
|
return NS_OK;
|
||
|
}
|
||
|
if (aIID.Equals(kISupportsIID)) {
|
||
|
*aInstancePtr = (void*) ((nsISupports *)this);
|
||
|
AddRef();
|
||
|
return NS_OK;
|
||
|
}
|
||
|
if (nsnull == mProtocolUrl) {
|
||
|
CreateProtocolURL();
|
||
|
}
|
||
|
if (nsnull != mProtocolUrl) {
|
||
|
return mProtocolUrl->QueryInterface(aIID, aInstancePtr);
|
||
|
}
|
||
|
return NS_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
|
||
|
URLImpl::~URLImpl()
|
||
|
{
|
||
|
NS_IF_RELEASE(mProtocolUrl);
|
||
|
|
||
|
free(mSpec);
|
||
|
free(mProtocol);
|
||
|
free(mHost);
|
||
|
free(mFile);
|
||
|
free(mRef);
|
||
|
}
|
||
|
|
||
|
nsresult URLImpl::Set(const char *aNewSpec)
|
||
|
{
|
||
|
return ParseURL(nsnull, aNewSpec);
|
||
|
}
|
||
|
|
||
|
|
||
|
PRBool URLImpl::operator==(const nsIURL& aURL) const
|
||
|
{
|
||
|
URLImpl& other = (URLImpl&)aURL; // XXX ?
|
||
|
return PRBool((0 == PL_strcmp(mProtocol, other.mProtocol)) &&
|
||
|
(0 == PL_strcasecmp(mHost, other.mHost)) &&
|
||
|
(0 == PL_strcmp(mFile, other.mFile)));
|
||
|
}
|
||
|
|
||
|
const char* URLImpl::GetProtocol() const
|
||
|
{
|
||
|
return mProtocol;
|
||
|
}
|
||
|
|
||
|
const char* URLImpl::GetHost() const
|
||
|
{
|
||
|
return mHost;
|
||
|
}
|
||
|
|
||
|
const char* URLImpl::GetFile() const
|
||
|
{
|
||
|
return mFile;
|
||
|
}
|
||
|
|
||
|
const char* URLImpl::GetSpec() const
|
||
|
{
|
||
|
return mSpec;
|
||
|
}
|
||
|
|
||
|
const char* URLImpl::GetRef() const
|
||
|
{
|
||
|
return mRef;
|
||
|
}
|
||
|
|
||
|
PRInt32 URLImpl::GetPort() const
|
||
|
{
|
||
|
return mPort;
|
||
|
}
|
||
|
|
||
|
|
||
|
void URLImpl::ToString(nsString& aString) const
|
||
|
{
|
||
|
aString.SetLength(0);
|
||
|
aString.Append(mProtocol);
|
||
|
aString.Append("://");
|
||
|
if (nsnull != mHost) {
|
||
|
aString.Append(mHost);
|
||
|
if (0 < mPort) {
|
||
|
aString.Append(':');
|
||
|
aString.Append(mPort, 10);
|
||
|
}
|
||
|
}
|
||
|
aString.Append(mFile);
|
||
|
if (nsnull != mRef) {
|
||
|
aString.Append('#');
|
||
|
aString.Append(mRef);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// XXX recode to use nsString api's
|
||
|
|
||
|
// XXX don't bother with port numbers
|
||
|
// XXX don't bother with ref's
|
||
|
// XXX null pointer checks are incomplete
|
||
|
nsresult URLImpl::ParseURL(const nsIURL* aURL, const nsString& aSpec)
|
||
|
{
|
||
|
// XXX hack!
|
||
|
char* cSpec = aSpec.ToNewCString();
|
||
|
|
||
|
const char* uProtocol = nsnull;
|
||
|
const char* uHost = nsnull;
|
||
|
const char* uFile = nsnull;
|
||
|
PRInt32 uPort = -1;
|
||
|
if (nsnull != aURL) {
|
||
|
uProtocol = aURL->GetProtocol();
|
||
|
uHost = aURL->GetHost();
|
||
|
uFile = aURL->GetFile();
|
||
|
uPort = aURL->GetPort();
|
||
|
}
|
||
|
|
||
|
mProtocol = nsnull;
|
||
|
mHost = nsnull;
|
||
|
mFile = nsnull;
|
||
|
mRef = nsnull;
|
||
|
mPort = -1;
|
||
|
mSpec = nsnull;
|
||
|
|
||
|
if (nsnull == cSpec) {
|
||
|
delete cSpec;
|
||
|
if (nsnull == aURL) {
|
||
|
delete cSpec;
|
||
|
return NS_ERROR_ILLEGAL_VALUE;
|
||
|
}
|
||
|
mProtocol = (nsnull != uProtocol) ? PL_strdup(uProtocol) : nsnull;
|
||
|
mHost = (nsnull != uHost) ? PL_strdup(uHost) : nsnull;
|
||
|
mPort = uPort;
|
||
|
mFile = (nsnull != uFile) ? PL_strdup(uFile) : nsnull;
|
||
|
delete cSpec;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
mSpec = PL_strdup(cSpec);
|
||
|
const char* cp = PL_strchr(cSpec, ':');
|
||
|
if (nsnull == cp) {
|
||
|
// relative spec
|
||
|
if (nsnull == aURL) {
|
||
|
delete cSpec;
|
||
|
return NS_ERROR_ILLEGAL_VALUE;
|
||
|
}
|
||
|
|
||
|
// keep protocol and host
|
||
|
mProtocol = (nsnull != uProtocol) ? PL_strdup(uProtocol) : nsnull;
|
||
|
mHost = (nsnull != uHost) ? PL_strdup(uHost) : nsnull;
|
||
|
mPort = uPort;
|
||
|
|
||
|
// figure out file name
|
||
|
PRInt32 len = PL_strlen(cSpec) + 1;
|
||
|
if ((len > 1) && (cSpec[0] == '/')) {
|
||
|
// Relative spec is absolute to the server
|
||
|
mFile = PL_strdup(cSpec);
|
||
|
} else {
|
||
|
char* dp = PL_strrchr(uFile, '/');
|
||
|
PRInt32 dirlen = (dp + 1) - uFile;
|
||
|
mFile = (char*) malloc(dirlen + len);
|
||
|
PL_strncpy(mFile, uFile, dirlen);
|
||
|
PL_strcpy(mFile + dirlen, cSpec);
|
||
|
}
|
||
|
} else {
|
||
|
// absolute spec
|
||
|
|
||
|
// get protocol first
|
||
|
PRInt32 plen = cp - cSpec;
|
||
|
mProtocol = (char*) malloc(plen + 1);
|
||
|
PL_strncpy(mProtocol, cSpec, plen);
|
||
|
mProtocol[plen] = 0;
|
||
|
cp++; // eat : in protocol
|
||
|
|
||
|
// skip over one, two or three slashes
|
||
|
if (*cp == '/') {
|
||
|
cp++;
|
||
|
if (*cp == '/') {
|
||
|
cp++;
|
||
|
if (*cp == '/') {
|
||
|
cp++;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
delete cSpec;
|
||
|
return NS_ERROR_ILLEGAL_VALUE;
|
||
|
}
|
||
|
|
||
|
const char* cp0 = cp;
|
||
|
if ((PL_strcmp(mProtocol, "resource") == 0) ||
|
||
|
(PL_strcmp(mProtocol, "file") == 0)) {
|
||
|
// resource/file url's do not have host names.
|
||
|
// The remainder of the string is the file name
|
||
|
PRInt32 flen = PL_strlen(cp);
|
||
|
mFile = (char*) malloc(flen + 1);
|
||
|
PL_strcpy(mFile, cp);
|
||
|
|
||
|
#ifdef NS_WIN32
|
||
|
if (PL_strcmp(mProtocol, "file") == 0) {
|
||
|
// If the filename starts with a "x|" where is an single
|
||
|
// character then we assume it's a drive name and change the
|
||
|
// vertical bar back to a ":"
|
||
|
if ((flen >= 2) && (mFile[1] == '|')) {
|
||
|
mFile[1] = ':';
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
} else {
|
||
|
// Host name follows protocol for http style urls
|
||
|
cp = PL_strchr(cp, '/');
|
||
|
if (nsnull == cp) {
|
||
|
// There is no file name, only a host name
|
||
|
PRInt32 hlen = PL_strlen(cp0);
|
||
|
mHost = (char*) malloc(hlen + 1);
|
||
|
PL_strcpy(mHost, cp0);
|
||
|
|
||
|
// Set filename to "/"
|
||
|
mFile = (char*) malloc(2);
|
||
|
mFile[0] = '/';
|
||
|
mFile[1] = 0;
|
||
|
}
|
||
|
else {
|
||
|
PRInt32 hlen = cp - cp0;
|
||
|
mHost = (char*) malloc(hlen + 1);
|
||
|
PL_strncpy(mHost, cp0, hlen);
|
||
|
mHost[hlen] = 0;
|
||
|
|
||
|
// The rest is the file name
|
||
|
PRInt32 flen = PL_strlen(cp);
|
||
|
mFile = (char*) malloc(flen + 1);
|
||
|
PL_strcpy(mFile, cp);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//printf("protocol='%s' host='%s' file='%s'\n", mProtocol, mHost, mFile);
|
||
|
delete cSpec;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsIInputStream* URLImpl::Open(PRInt32* aErrorCode)
|
||
|
{
|
||
|
nsresult rv;
|
||
|
nsIInputStream* in = nsnull;
|
||
|
nsINetService *inet;
|
||
|
|
||
|
// XXX: Rewrite the resource: URL into a file: URL
|
||
|
if (PL_strcmp(mProtocol, "resource") == 0) {
|
||
|
char* fileName;
|
||
|
|
||
|
fileName = mangleResourceIntoFileURL(mFile);
|
||
|
Set(fileName);
|
||
|
PR_Free(fileName);
|
||
|
}
|
||
|
|
||
|
rv = NS_NewINetService(&inet, nsnull);
|
||
|
if (NS_OK == rv) {
|
||
|
rv = inet->OpenBlockingStream(this, NULL, &in);
|
||
|
}
|
||
|
// XXX: The INetService should be released...
|
||
|
|
||
|
*aErrorCode = rv;
|
||
|
return in;
|
||
|
}
|
||
|
|
||
|
nsresult URLImpl::Open(nsIStreamListener *aListener)
|
||
|
{
|
||
|
nsINetService *inet;
|
||
|
nsresult rv;
|
||
|
|
||
|
// XXX: Rewrite the resource: URL into a file: URL
|
||
|
if (PL_strcmp(mProtocol, "resource") == 0) {
|
||
|
char *fileName;
|
||
|
|
||
|
fileName = mangleResourceIntoFileURL(mFile);
|
||
|
Set(fileName);
|
||
|
PR_Free(fileName);
|
||
|
}
|
||
|
|
||
|
rv = NS_NewINetService(&inet, nsnull);
|
||
|
if (NS_OK == rv) {
|
||
|
rv = inet->OpenStream(this, aListener);
|
||
|
}
|
||
|
// XXX: The INetService should be released...
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
|
||
|
void URLImpl::CreateProtocolURL()
|
||
|
{
|
||
|
nsresult result;
|
||
|
|
||
|
result = NS_NewHttpUrl(&mProtocolUrl, this);
|
||
|
}
|
||
|
|
||
|
|
||
|
NS_NET nsresult NS_NewURL(nsIURL** aInstancePtrResult,
|
||
|
const nsString& aSpec)
|
||
|
{
|
||
|
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
|
||
|
if (nsnull == aInstancePtrResult) {
|
||
|
return NS_ERROR_NULL_POINTER;
|
||
|
}
|
||
|
URLImpl* it = new URLImpl(aSpec);
|
||
|
if (nsnull == it) {
|
||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||
|
}
|
||
|
return it->QueryInterface(kURLIID, (void **) aInstancePtrResult);
|
||
|
}
|
||
|
|
||
|
NS_NET nsresult NS_NewURL(nsIURL** aInstancePtrResult,
|
||
|
const nsIURL* aURL,
|
||
|
const nsString& aSpec)
|
||
|
{
|
||
|
URLImpl* it = new URLImpl(aURL, aSpec);
|
||
|
if (nsnull == it) {
|
||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||
|
}
|
||
|
return it->QueryInterface(kURLIID, (void **) aInstancePtrResult);
|
||
|
}
|
||
|
|
||
|
NS_NET nsresult NS_MakeAbsoluteURL(nsIURL* aURL,
|
||
|
const nsString& aBaseURL,
|
||
|
const nsString& aSpec,
|
||
|
nsString& aResult)
|
||
|
{
|
||
|
if (0 < aBaseURL.Length()) {
|
||
|
URLImpl base(aBaseURL);
|
||
|
URLImpl url(&base, aSpec);
|
||
|
url.ToString(aResult);
|
||
|
} else {
|
||
|
URLImpl url(aURL, aSpec);
|
||
|
url.ToString(aResult);
|
||
|
}
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
char *mangleResourceIntoFileURL(const char* aResourceFileName)
|
||
|
{
|
||
|
// XXX For now, resources are not in jar files
|
||
|
// Find base path name to the resource file
|
||
|
char* resourceBase;
|
||
|
char* cp;
|
||
|
|
||
|
#ifdef XP_PC
|
||
|
// XXX For now, all resources are relative to the .exe file
|
||
|
resourceBase = (char *)PR_Malloc(2000);;
|
||
|
DWORD mfnLen = GetModuleFileName(NULL, resourceBase, 2000);
|
||
|
// Truncate the executable name from the rest of the path...
|
||
|
cp = strrchr(resourceBase, '\\');
|
||
|
if (nsnull != cp) {
|
||
|
*cp = '\0';
|
||
|
}
|
||
|
// Change the first ':' into a '|'
|
||
|
cp = strchr(resourceBase, ':');
|
||
|
if (nsnull != cp) {
|
||
|
*cp = '|';
|
||
|
}
|
||
|
#endif
|
||
|
#ifdef XP_UNIX
|
||
|
// FIX ME: write me!;
|
||
|
#endif
|
||
|
|
||
|
// Join base path to resource name
|
||
|
if (aResourceFileName[0] == '/') {
|
||
|
aResourceFileName++;
|
||
|
}
|
||
|
PRInt32 baseLen = strlen(resourceBase);
|
||
|
PRInt32 resLen = strlen(aResourceFileName);
|
||
|
PRInt32 totalLen = 8 + baseLen + 1 + resLen + 1;
|
||
|
char* fileName = (char *)PR_Malloc(totalLen);
|
||
|
PR_snprintf(fileName, totalLen, "file:///%s/%s", resourceBase, aResourceFileName);
|
||
|
|
||
|
#ifdef XP_PC
|
||
|
// Change any backslashes into foreward slashes...
|
||
|
while ((cp = strchr(fileName, '\\')) != 0) {
|
||
|
*cp = '/';
|
||
|
cp++;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef XP_PC
|
||
|
PR_Free(resourceBase);
|
||
|
#endif
|
||
|
|
||
|
return fileName;
|
||
|
}
|