Mozilla/mozilla/netwerk/base/src/nsProtocolProxyService.cpp

584 lines
17 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsProtocolProxyService.h"
#include "nsIServiceManager.h"
#include "nsXPIDLString.h"
#include "nsIProxyAutoConfig.h"
#include "nsAutoLock.h"
#include "nsNetCID.h"
#include "nsIIOService.h"
#include "nsIEventQueueService.h"
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
static const char PROXY_PREFS[] = "network.proxy";
static PRInt32 PR_CALLBACK ProxyPrefsCallback(const char* pref, void* instance)
{
nsProtocolProxyService* proxyServ = (nsProtocolProxyService*) instance;
NS_ASSERTION(proxyServ, "bad instance data");
if (proxyServ) proxyServ->PrefsChanged(pref);
return 0;
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsProtocolProxyService, nsIProtocolProxyService);
nsProtocolProxyService::nsProtocolProxyService():
mArrayLock(PR_NewLock()),
mUseProxy(0),
mPAC(nsnull)
{
NS_INIT_REFCNT();
}
nsProtocolProxyService::~nsProtocolProxyService()
{
if(mArrayLock)
PR_DestroyLock(mArrayLock);
if (mFiltersArray.Count() > 0)
{
mFiltersArray.EnumerateForwards(
(nsVoidArrayEnumFunc)this->CleanupFilterArray, nsnull);
mFiltersArray.Clear();
}
}
// nsProtocolProxyService methods
NS_IMETHODIMP
nsProtocolProxyService::Init() {
nsresult rv = NS_OK;
mPrefs = do_GetService(kPrefServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
// register for change callbacks
rv = mPrefs->RegisterCallback(PROXY_PREFS, ProxyPrefsCallback, (void*)this);
if (NS_FAILED(rv)) return rv;
PrefsChanged(nsnull);
return NS_OK;
}
NS_METHOD
nsProtocolProxyService::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
nsresult rv;
if (aOuter) return NS_ERROR_NO_AGGREGATION;
nsProtocolProxyService* serv = new nsProtocolProxyService();
if (!serv) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(serv);
rv = serv->Init();
if (NS_FAILED(rv)) {
delete serv;
return rv;
}
rv = serv->QueryInterface(aIID, aResult);
NS_RELEASE(serv);
return rv;
}
void
nsProtocolProxyService::PrefsChanged(const char* pref) {
NS_ASSERTION(mPrefs, "No preference service available!");
if (!mPrefs) return;
nsresult rv = NS_OK;
PRBool reloadPAC = PR_FALSE;
nsXPIDLCString tempString;
if (!pref || !PL_strcmp(pref, "network.proxy.type"))
{
PRInt32 type = -1;
rv = mPrefs->GetIntPref("network.proxy.type",&type);
if (NS_SUCCEEDED(rv)) {
mUseProxy = type; // type == 2 is autoconfig stuff
reloadPAC = PR_TRUE;
}
}
if (!pref || !PL_strcmp(pref, "network.proxy.http"))
{
rv = mPrefs->CopyCharPref("network.proxy.http",
getter_Copies(mHTTPProxyHost));
if (!NS_SUCCEEDED(rv))
mHTTPProxyHost.Adopt(nsCRT::strdup(""));
}
if (!pref || !PL_strcmp(pref, "network.proxy.http_port"))
{
mHTTPProxyPort = -1;
PRInt32 proxyPort;
rv = mPrefs->GetIntPref("network.proxy.http_port",&proxyPort);
if (NS_SUCCEEDED(rv))
mHTTPProxyPort = proxyPort;
}
if (!pref || !PL_strcmp(pref, "network.proxy.ssl"))
{
rv = mPrefs->CopyCharPref("network.proxy.ssl",
getter_Copies(mHTTPSProxyHost));
if (!NS_SUCCEEDED(rv))
mHTTPSProxyHost.Adopt(nsCRT::strdup(""));
}
if (!pref || !PL_strcmp(pref, "network.proxy.ssl_port"))
{
mHTTPSProxyPort = -1;
PRInt32 proxyPort;
rv = mPrefs->GetIntPref("network.proxy.ssl_port",&proxyPort);
if (NS_SUCCEEDED(rv))
mHTTPSProxyPort = proxyPort;
}
if (!pref || !PL_strcmp(pref, "network.proxy.ftp"))
{
rv = mPrefs->CopyCharPref("network.proxy.ftp",
getter_Copies(mFTPProxyHost));
if (!NS_SUCCEEDED(rv))
mFTPProxyHost.Adopt(nsCRT::strdup(""));
}
if (!pref || !PL_strcmp(pref, "network.proxy.ftp_port"))
{
mFTPProxyPort = -1;
PRInt32 proxyPort;
rv = mPrefs->GetIntPref("network.proxy.ftp_port",&proxyPort);
if (NS_SUCCEEDED(rv))
mFTPProxyPort = proxyPort;
}
if (!pref || !PL_strcmp(pref, "network.proxy.gopher"))
{
rv = mPrefs->CopyCharPref("network.proxy.gopher",
getter_Copies(mGopherProxyHost));
if (!NS_SUCCEEDED(rv) || !mGopherProxyHost)
mGopherProxyHost.Adopt(nsCRT::strdup(""));
}
if (!pref || !PL_strcmp(pref, "network.proxy.gopher_port"))
{
mGopherProxyPort = -1;
PRInt32 proxyPort = -1;
rv = mPrefs->GetIntPref("network.proxy.gopher_port",&proxyPort);
if (NS_SUCCEEDED(rv) && proxyPort>0)
mGopherProxyPort = proxyPort;
}
if (!pref || !PL_strcmp(pref, "network.proxy.socks"))
{
rv = mPrefs->CopyCharPref("network.proxy.socks",
getter_Copies(mSOCKSProxyHost));
if (!NS_SUCCEEDED(rv))
mSOCKSProxyHost.Adopt(nsCRT::strdup(""));
}
if (!pref || !PL_strcmp(pref, "network.proxy.socks_port"))
{
mSOCKSProxyPort = -1;
PRInt32 proxyPort;
rv = mPrefs->GetIntPref("network.proxy.socks_port",&proxyPort);
if (NS_SUCCEEDED(rv))
mSOCKSProxyPort = proxyPort;
}
if (!pref || !PL_strcmp(pref, "network.proxy.no_proxies_on"))
{
rv = mPrefs->CopyCharPref("network.proxy.no_proxies_on",
getter_Copies(tempString));
if (NS_SUCCEEDED(rv))
(void)LoadFilters((const char*)tempString);
}
if ((!pref || !PL_strcmp(pref, "network.proxy.autoconfig_url") || reloadPAC) &&
(mUseProxy == 2))
{
rv = mPrefs->CopyCharPref("network.proxy.autoconfig_url",
getter_Copies(tempString));
if (NS_SUCCEEDED(rv) && (!reloadPAC || PL_strcmp(tempString, mPACURL))) {
mPACURL.Adopt(nsCRT::strdup(tempString));
// create pac js component
mPAC = do_CreateInstance(NS_PROXY_AUTO_CONFIG_CONTRACTID, &rv);
if (!mPAC || NS_FAILED(rv)) {
NS_ERROR("Cannot load PAC js component");
return;
}
/* now we need to setup a callback from the main ui thread
in which we will load the pac file from the specified
url. loading it now, in the current thread results in a
browser crash */
// get event queue service
nsCOMPtr<nsIEventQueueService> eqs =
do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID);
if (!eqs) {
NS_ERROR("Failed to get EventQueue service");
return;
}
// get ui thread's event queue
nsCOMPtr<nsIEventQueue> eq = nsnull;
rv = eqs->GetThreadEventQueue(NS_UI_THREAD, getter_AddRefs(eq));
if (NS_FAILED(rv) || !eqs) {
NS_ERROR("Failed to get UI EventQueue");
return;
}
// create an event
PLEvent* event = new PLEvent;
// AddRef this because it is being placed in the PLEvent struct
// It will be Released when DestroyPACLoadEvent is called
NS_ADDREF_THIS();
PL_InitEvent(event,
this,
(PLHandleEventProc)
nsProtocolProxyService::HandlePACLoadEvent,
(PLDestroyEventProc)
nsProtocolProxyService::DestroyPACLoadEvent);
// post the event into the ui event queue
if (eq->PostEvent(event) == PR_FAILURE) {
NS_ERROR("Failed to post PAC load event to UI EventQueue");
NS_RELEASE_THIS();
delete event;
return;
}
}
}
}
// this is the main ui thread calling us back, load the pac now
void PR_CALLBACK nsProtocolProxyService::HandlePACLoadEvent(PLEvent* aEvent)
{
nsresult rv = NS_OK;
nsProtocolProxyService *pps =
(nsProtocolProxyService*) PL_GetEventOwner(aEvent);
if (!pps) {
NS_ERROR("HandlePACLoadEvent owner is null");
return;
}
if (!pps->mPAC) {
NS_ERROR("HandlePACLoadEvent: js PAC component is null");
return;
}
if (!pps->mPACURL) {
NS_ERROR("HandlePACLoadEvent: js PACURL component is null");
return;
}
NS_WITH_SERVICE(nsIIOService, pIOService, kIOServiceCID, &rv);
if (!pIOService || NS_FAILED(rv)) {
NS_ERROR("Cannot get IO Service");
return;
}
nsCOMPtr<nsIURI> pURL;
rv = pIOService->NewURI((const char*) pps->mPACURL, nsnull,
getter_AddRefs(pURL));
if (NS_FAILED(rv)) {
NS_ERROR("New URI failed");
return;
}
rv = pps->mPAC->LoadPACFromURL(pURL, pIOService);
if (NS_FAILED(rv)) {
NS_ERROR("Load PAC failed");
return;
}
}
void PR_CALLBACK nsProtocolProxyService::DestroyPACLoadEvent(PLEvent* aEvent)
{
nsProtocolProxyService *pps =
(nsProtocolProxyService*) PL_GetEventOwner(aEvent);
NS_IF_RELEASE(pps);
delete aEvent;
}
PRBool
nsProtocolProxyService::CanUseProxy(nsIURI* aURI)
{
if (mFiltersArray.Count() == 0)
return PR_TRUE;
PRInt32 port;
nsXPIDLCString host;
nsresult rv = aURI->GetHost(getter_Copies(host));
if (NS_FAILED(rv) || !host || !*host)
return PR_FALSE;
rv = aURI->GetPort(&port);
if (NS_FAILED(rv)) {
return PR_FALSE;
}
PRInt32 index = -1;
int host_len = PL_strlen(host);
int filter_host_len;
while (++index < mFiltersArray.Count())
{
host_port* hp = (host_port*) mFiltersArray[index];
// only if port doesn't exist or matches
if (((hp->port == -1) || (hp->port == port)) &&
hp->host)
{
filter_host_len = hp->host->Length();
if ((host_len >= filter_host_len) &&
(0 == PL_strncasecmp(host + host_len - filter_host_len,
hp->host->get(), filter_host_len)))
return PR_FALSE;
}
}
return PR_TRUE;
}
// nsIProtocolProxyService
NS_IMETHODIMP
nsProtocolProxyService::ExamineForProxy(nsIURI *aURI, char * *aProxyHost, PRInt32 *aProxyPort, char * *aProxyType) {
nsresult rv = NS_OK;
NS_ASSERTION(aURI, "need a uri folks.");
NS_ENSURE_ARG_POINTER(aProxyHost);
NS_ENSURE_ARG_POINTER(aProxyType);
*aProxyHost = nsnull;
*aProxyType = nsnull;
*aProxyPort = -1;
// we only know about http, https, ftp and gopher as yet...
// return nothing for others. See what happens otherwise in bug 81214
PRBool validScheme = PR_FALSE;
if ((NS_FAILED(aURI->SchemeIs("http", &validScheme)) || !validScheme) &&
(NS_FAILED(aURI->SchemeIs("https", &validScheme)) || !validScheme) &&
(NS_FAILED(aURI->SchemeIs("ftp", &validScheme)) || !validScheme ) &&
(NS_FAILED(aURI->SchemeIs("gopher", &validScheme)) || !validScheme))
return NS_OK;
// if proxies are enabled and this host:port combo is
// supposed to use a proxy, check for a proxy.
if ((0 == mUseProxy) ||
((1 == mUseProxy) && !CanUseProxy(aURI))) {
return NS_OK;
}
// Proxy auto config magic...
if (2 == mUseProxy)
{
if (!mPAC) {
NS_ERROR("ERROR: PAC js component is null");
return NS_ERROR_NULL_POINTER;
}
rv = mPAC->ProxyForURL(aURI,
aProxyHost,
aProxyPort,
aProxyType);
if (NS_SUCCEEDED(rv)) {
if (*aProxyType == nsnull || !PL_strcasecmp("direct", *aProxyType)) {
if (*aProxyHost) {
nsMemory::Free(*aProxyHost);
*aProxyHost = nsnull;
}
if (*aProxyType) {
nsMemory::Free(*aProxyType);
*aProxyType = nsnull;
}
*aProxyPort = -1;
} else if (*aProxyPort <= 0) {
*aProxyPort = -1;
}
}
return rv;
}
nsXPIDLCString scheme;
rv = aURI->GetScheme(getter_Copies(scheme));
if (NS_FAILED(rv)) return rv;
if (mHTTPProxyHost.get()[0] && mHTTPProxyPort > 0 &&
!PL_strcasecmp(scheme, "http")) {
*aProxyHost = PL_strdup(mHTTPProxyHost);
*aProxyType = PL_strdup("http");
*aProxyPort = mHTTPProxyPort;
return NS_OK;
}
if (mHTTPSProxyHost.get()[0] && mHTTPSProxyPort > 0 &&
!PL_strcasecmp(scheme, "https")) {
*aProxyHost = PL_strdup(mHTTPSProxyHost);
*aProxyType = PL_strdup("http");
*aProxyPort = mHTTPSProxyPort;
return NS_OK;
}
if (mFTPProxyHost.get()[0] && mFTPProxyPort > 0 &&
!PL_strcasecmp(scheme, "ftp")) {
*aProxyHost = PL_strdup(mFTPProxyHost);
*aProxyType = PL_strdup("http");
*aProxyPort = mFTPProxyPort;
return NS_OK;
}
if (mGopherProxyHost.get()[0] && mGopherProxyPort > 0 &&
!PL_strcasecmp(scheme, "gopher")) {
*aProxyHost = PL_strdup(mGopherProxyHost);
*aProxyType = PL_strdup("http");
*aProxyPort = mGopherProxyPort;
return NS_OK;
}
if (mSOCKSProxyHost.get()[0] && mSOCKSProxyPort > 0) {
*aProxyHost = PL_strdup(mSOCKSProxyHost);
*aProxyType = PL_strdup("socks");
*aProxyPort = mSOCKSProxyPort;
return NS_OK;
}
return NS_OK;
}
NS_IMETHODIMP
nsProtocolProxyService::GetProxyEnabled(PRBool* o_Enabled)
{
if (!o_Enabled)
return NS_ERROR_NULL_POINTER;
*o_Enabled = mUseProxy;
return NS_OK;
}
NS_IMETHODIMP
nsProtocolProxyService::AddNoProxyFor(const char* iHost, PRInt32 iPort)
{
if (!iHost)
return NS_ERROR_NULL_POINTER;
host_port* hp = new host_port();
if (!hp)
return NS_ERROR_OUT_OF_MEMORY;
hp->host = new nsCString(iHost);
hp->port = iPort;
nsAutoLock lock(mArrayLock);
return (mFiltersArray.AppendElement(hp)) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsProtocolProxyService::RemoveNoProxyFor(const char* iHost, PRInt32 iPort)
{
if (!iHost)
return NS_ERROR_NULL_POINTER;
nsAutoLock lock(mArrayLock);
if (mFiltersArray.Count()==0)
return NS_ERROR_FAILURE;
PRInt32 index = -1;
while (++index < mFiltersArray.Count())
{
host_port* hp = (host_port*) mFiltersArray[index];
if ((hp && hp->host) &&
(iPort == hp->port) &&
(0 == PL_strcasecmp((const char*)hp->host, iHost)))
{
delete hp->host;
delete hp;
mFiltersArray.RemoveElementAt(index);
return NS_OK;
}
}
return NS_ERROR_FAILURE; // not found
}
PRBool
nsProtocolProxyService::CleanupFilterArray(void* aElement, void* aData)
{
if (aElement)
{
host_port* hp = (host_port*)aElement;
delete hp->host;
delete hp;
}
return PR_TRUE;
}
void
nsProtocolProxyService::LoadFilters(const char* filters)
{
host_port* hp;
// check to see the owners flag? /!?/ TODO
if (mFiltersArray.Count() > 0)
{
mFiltersArray.EnumerateForwards(
(nsVoidArrayEnumFunc)this->CleanupFilterArray, nsnull);
mFiltersArray.Clear();
}
if (!filters)
return ;//fail silently...
char* np = (char*)filters;
while (*np)
{
// skip over spaces and ,
while (*np && (*np == ',' || nsCRT::IsAsciiSpace(*np)))
np++;
char* endproxy = np+1; // at least that...
char* portLocation = 0;
PRInt32 nport = 0; // no proxy port
while (*endproxy && (*endproxy != ',' &&
!nsCRT::IsAsciiSpace(*endproxy)))
{
if (*endproxy == ':')
portLocation = endproxy;
endproxy++;
}
if (portLocation)
nport = atoi(portLocation+1);
hp = new host_port();
if (!hp)
return; // fail silently
hp->host = new nsCString(np, endproxy-np);
if (!hp->host)
return;
hp->port = nport>0 ? nport : -1;
mFiltersArray.AppendElement(hp);
np = endproxy;
}
}