Back to index

lightning-sunbird  0.9+nobinonly
nsProtocolProxyService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim:set ts=4 sw=4 sts=4 et: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *  Malcolm Smith <malsmith@cs.rmit.edu.au>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsProtocolProxyService.h"
00041 #include "nsProxyInfo.h"
00042 #include "nsIServiceManager.h"
00043 #include "nsXPIDLString.h"
00044 #include "nsIProxyAutoConfig.h"
00045 #include "nsAutoLock.h"
00046 #include "nsEventQueueUtils.h"
00047 #include "nsIIOService.h"
00048 #include "nsIObserverService.h"
00049 #include "nsIProtocolHandler.h"
00050 #include "nsIProtocolProxyCallback.h"
00051 #include "nsICancelable.h"
00052 #include "nsIDNSService.h"
00053 #include "nsIPrefService.h"
00054 #include "nsIPrefBranch2.h"
00055 #include "nsReadableUtils.h"
00056 #include "nsString.h"
00057 #include "nsNetUtil.h"
00058 #include "nsCRT.h"
00059 #include "prnetdb.h"
00060 #include "nsEventQueueUtils.h"
00061 #include "nsPACMan.h"
00062 
00063 //----------------------------------------------------------------------------
00064 
00065 #include "prlog.h"
00066 #if defined(PR_LOGGING)
00067 static PRLogModuleInfo *sLog = PR_NewLogModule("proxy");
00068 #endif
00069 #define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
00070 
00071 //----------------------------------------------------------------------------
00072 
00073 // This structure is intended to be allocated on the stack
00074 struct nsProtocolInfo {
00075     nsCAutoString scheme;
00076     PRUint32 flags;
00077     PRInt32 defaultPort;
00078 };
00079 
00080 //----------------------------------------------------------------------------
00081 
00082 class nsAsyncResolveRequest : public PLEvent
00083                             , public nsPACManCallback 
00084                             , public nsICancelable
00085 {
00086 public:
00087     NS_DECL_ISUPPORTS
00088 
00089     nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIURI *uri,
00090                           nsIProtocolProxyCallback *callback)
00091         : mStatus(NS_OK)
00092         , mDispatched(PR_FALSE)
00093         , mPPS(pps)
00094         , mURI(uri)
00095         , mCallback(callback)
00096     {
00097         NS_ASSERTION(mCallback, "null callback");
00098         PL_InitEvent(this, nsnull, HandleEvent, CleanupEvent);
00099     }
00100 
00101     void SetResult(nsresult status, nsIProxyInfo *pi)
00102     {
00103         mStatus = status;
00104         mProxyInfo = pi;
00105     }
00106 
00107     NS_IMETHOD Cancel(nsresult reason)
00108     {
00109         NS_ENSURE_ARG(NS_FAILED(reason));
00110 
00111         // If we've already called DoCallback then, nothing more to do.
00112         if (!mCallback)
00113             return NS_OK;
00114 
00115         SetResult(reason, nsnull);
00116         return DispatchCallback();
00117     }
00118 
00119     nsresult DispatchCallback()
00120     {
00121         if (mDispatched)  // Only need to dispatch once
00122             return NS_OK;
00123 
00124         nsCOMPtr<nsIEventQueue> eventQ;
00125         nsresult rv = NS_GetCurrentEventQ(getter_AddRefs(eventQ));
00126         if (NS_FAILED(rv))
00127             NS_WARNING("could not get current event queue");
00128         else {
00129             NS_ADDREF_THIS();
00130             rv = eventQ->PostEvent(this);
00131             if (NS_FAILED(rv)) {
00132                 NS_WARNING("unable to dispatch callback event");
00133                 PL_DestroyEvent(this);
00134             }
00135             else {
00136                 mDispatched = PR_TRUE;
00137                 return NS_OK;
00138             }
00139         }
00140 
00141         mCallback = nsnull;  // break possible reference cycle
00142         return rv;
00143     }
00144 
00145 private:
00146 
00147     // Called asynchronously, so we do not need to post another PLEvent
00148     // before calling DoCallback.
00149     void OnQueryComplete(nsresult status, const nsCString &pacString)
00150     {
00151         // If we've already called DoCallback then, nothing more to do.
00152         if (!mCallback)
00153             return;
00154 
00155         // Provided we haven't been canceled...
00156         if (mStatus == NS_OK) {
00157             mStatus = status;
00158             mPACString = pacString;
00159         }
00160 
00161         // In the cancelation case, we may still have another PLEvent in
00162         // the queue that wants to call DoCallback.  No need to wait for
00163         // it, just run the callback now.
00164         DoCallback();
00165     }
00166 
00167     void DoCallback()
00168     {
00169         // Generate proxy info from the PAC string if appropriate
00170         if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty())
00171             mPPS->ProcessPACString(mPACString, getter_AddRefs(mProxyInfo));
00172 
00173         // Now apply proxy filters
00174         if (NS_SUCCEEDED(mStatus)) {
00175             nsProtocolInfo info;
00176             mStatus = mPPS->GetProtocolInfo(mURI, &info);
00177             if (NS_SUCCEEDED(mStatus))
00178                 mPPS->ApplyFilters(mURI, info, mProxyInfo);
00179             else
00180                 mProxyInfo = nsnull;
00181         }
00182 
00183         mCallback->OnProxyAvailable(this, mURI, mProxyInfo, mStatus);
00184         mCallback = nsnull;  // in case the callback holds an owning ref to us
00185     }
00186 
00187     PR_STATIC_CALLBACK(void *) HandleEvent(PLEvent *ev)
00188     {
00189         nsAsyncResolveRequest *self =
00190                 NS_STATIC_CAST(nsAsyncResolveRequest *, ev);
00191         if (self->mCallback)
00192             self->DoCallback();
00193         return nsnull;
00194     }
00195 
00196     PR_STATIC_CALLBACK(void) CleanupEvent(PLEvent *ev)
00197     {
00198         nsAsyncResolveRequest *self =
00199                 NS_STATIC_CAST(nsAsyncResolveRequest *, ev);
00200         NS_RELEASE(self);  // balance AddRef in DispatchCallback
00201     }
00202 
00203 private:
00204 
00205     nsresult  mStatus;
00206     nsCString mPACString;
00207     PRBool    mDispatched;
00208 
00209     nsRefPtr<nsProtocolProxyService>   mPPS;
00210     nsCOMPtr<nsIURI>                   mURI;
00211     nsCOMPtr<nsIProtocolProxyCallback> mCallback;
00212     nsCOMPtr<nsIProxyInfo>             mProxyInfo;
00213 };
00214 
00215 NS_IMPL_ISUPPORTS1(nsAsyncResolveRequest, nsICancelable)
00216 
00217 //----------------------------------------------------------------------------
00218 
00219 #define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t')
00220 
00221 //
00222 // apply mask to address (zeros out excluded bits).
00223 //
00224 // NOTE: we do the byte swapping here to minimize overall swapping.
00225 //
00226 static void
00227 proxy_MaskIPv6Addr(PRIPv6Addr &addr, PRUint16 mask_len)
00228 {
00229     if (mask_len == 128)
00230         return;
00231 
00232     if (mask_len > 96) {
00233         addr.pr_s6_addr32[3] = PR_htonl(
00234                 PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len)));
00235     }
00236     else if (mask_len > 64) {
00237         addr.pr_s6_addr32[3] = 0;
00238         addr.pr_s6_addr32[2] = PR_htonl(
00239                 PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len)));
00240     }
00241     else if (mask_len > 32) {
00242         addr.pr_s6_addr32[3] = 0;
00243         addr.pr_s6_addr32[2] = 0;
00244         addr.pr_s6_addr32[1] = PR_htonl(
00245                 PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len)));
00246     }
00247     else {
00248         addr.pr_s6_addr32[3] = 0;
00249         addr.pr_s6_addr32[2] = 0;
00250         addr.pr_s6_addr32[1] = 0;
00251         addr.pr_s6_addr32[0] = PR_htonl(
00252                 PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len)));
00253     }
00254 }
00255 
00256 static void
00257 proxy_GetStringPref(nsIPrefBranch *aPrefBranch,
00258                     const char    *aPref,
00259                     nsCString     &aResult)
00260 {
00261     nsXPIDLCString temp;
00262     nsresult rv = aPrefBranch->GetCharPref(aPref, getter_Copies(temp));
00263     if (NS_FAILED(rv))
00264         aResult.Truncate();
00265     else {
00266         aResult.Assign(temp);
00267         // all of our string prefs are hostnames, so we should remove any
00268         // whitespace characters that the user might have unknowingly entered.
00269         aResult.StripWhitespace();
00270     }
00271 }
00272 
00273 static void
00274 proxy_GetIntPref(nsIPrefBranch *aPrefBranch,
00275                  const char    *aPref,
00276                  PRInt32       &aResult)
00277 {
00278     PRInt32 temp;
00279     nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
00280     if (NS_FAILED(rv)) 
00281         aResult = -1;
00282     else
00283         aResult = temp;
00284 }
00285 
00286 static void
00287 proxy_GetBoolPref(nsIPrefBranch *aPrefBranch,
00288                  const char    *aPref,
00289                  PRBool        &aResult)
00290 {
00291     PRBool temp;
00292     nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
00293     if (NS_FAILED(rv)) 
00294         aResult = PR_FALSE;
00295     else
00296         aResult = temp;
00297 }
00298 
00299 //----------------------------------------------------------------------------
00300 
00301 NS_IMPL_ISUPPORTS3(nsProtocolProxyService,
00302                    nsIProtocolProxyService,
00303                    nsPIProtocolProxyService,
00304                    nsIObserver)
00305 
00306 nsProtocolProxyService::nsProtocolProxyService()
00307     : mFilters(nsnull)
00308     , mProxyConfig(eProxyConfig_Direct)
00309     , mHTTPProxyPort(-1)
00310     , mFTPProxyPort(-1)
00311     , mGopherProxyPort(-1)
00312     , mHTTPSProxyPort(-1)
00313     , mSOCKSProxyPort(-1)
00314     , mSOCKSProxyVersion(4)
00315     , mSOCKSProxyRemoteDNS(PR_FALSE)
00316     , mPACMan(nsnull)
00317     , mSessionStart(PR_Now())
00318     , mFailedProxyTimeout(30 * 60) // 30 minute default
00319 {
00320 }
00321 
00322 nsProtocolProxyService::~nsProtocolProxyService()
00323 {
00324     // These should have been cleaned up in our Observe method.
00325     NS_ASSERTION(mHostFiltersArray.Count() == 0 && mFilters == nsnull &&
00326                  mPACMan == nsnull, "what happened to xpcom-shutdown?");
00327 }
00328 
00329 // nsProtocolProxyService methods
00330 nsresult
00331 nsProtocolProxyService::Init()
00332 {
00333     if (!mFailedProxies.Init())
00334         return NS_ERROR_OUT_OF_MEMORY;
00335 
00336     // failure to access prefs is non-fatal
00337     nsCOMPtr<nsIPrefBranch2> prefBranch =
00338             do_GetService(NS_PREFSERVICE_CONTRACTID);
00339     if (prefBranch) {
00340         // monitor proxy prefs
00341         prefBranch->AddObserver("network.proxy", this, PR_FALSE);
00342 
00343         // read all prefs
00344         PrefsChanged(prefBranch, nsnull);
00345     }
00346 
00347     // register for shutdown notification so we can clean ourselves up properly.
00348     nsCOMPtr<nsIObserverService> obs =
00349             do_GetService("@mozilla.org/observer-service;1");
00350     if (obs)
00351         obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
00352 
00353     return NS_OK;
00354 }
00355 
00356 NS_IMETHODIMP
00357 nsProtocolProxyService::Observe(nsISupports     *aSubject,
00358                                 const char      *aTopic,
00359                                 const PRUnichar *aData)
00360 {
00361     if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
00362         // cleanup
00363         if (mHostFiltersArray.Count() > 0) {
00364             mHostFiltersArray.EnumerateForwards(CleanupFilterArray, nsnull);
00365             mHostFiltersArray.Clear();
00366         }
00367         if (mFilters) {
00368             delete mFilters;
00369             mFilters = nsnull;
00370         }
00371         if (mPACMan) {
00372             mPACMan->Shutdown();
00373             mPACMan = nsnull;
00374         }
00375     }
00376     else {
00377         NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
00378                      "what is this random observer event?");
00379         nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
00380         if (prefs)
00381             PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
00382     }
00383     return NS_OK;
00384 }
00385 
00386 void
00387 nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
00388                                      const char    *pref)
00389 {
00390     nsresult rv = NS_OK;
00391     PRBool reloadPAC = PR_FALSE;
00392     nsXPIDLCString tempString;
00393 
00394     if (!pref || !strcmp(pref, "network.proxy.type")) {
00395         PRInt32 type = -1;
00396         rv = prefBranch->GetIntPref("network.proxy.type",&type);
00397         if (NS_SUCCEEDED(rv)) {
00398             // bug 115720 - for ns4.x backwards compatability
00399             if (type == eProxyConfig_Direct4x) {
00400                 type = eProxyConfig_Direct;
00401                 // Reset the type so that the dialog looks correct, and we
00402                 // don't have to handle this case everywhere else
00403                 // I'm paranoid about a loop of some sort - only do this
00404                 // if we're enumerating all prefs, and ignore any error
00405                 if (!pref)
00406                     prefBranch->SetIntPref("network.proxy.type", type);
00407             } else if (type >= eProxyConfig_Last) {
00408                 LOG(("unknown proxy type: %lu; assuming direct\n", type));
00409                 type = eProxyConfig_Direct;
00410             }
00411             mProxyConfig = NS_STATIC_CAST(ProxyConfig, type);
00412             reloadPAC = PR_TRUE;
00413         }
00414     }
00415 
00416     if (!pref || !strcmp(pref, "network.proxy.http"))
00417         proxy_GetStringPref(prefBranch, "network.proxy.http", mHTTPProxyHost);
00418 
00419     if (!pref || !strcmp(pref, "network.proxy.http_port"))
00420         proxy_GetIntPref(prefBranch, "network.proxy.http_port", mHTTPProxyPort);
00421 
00422     if (!pref || !strcmp(pref, "network.proxy.ssl"))
00423         proxy_GetStringPref(prefBranch, "network.proxy.ssl", mHTTPSProxyHost);
00424 
00425     if (!pref || !strcmp(pref, "network.proxy.ssl_port"))
00426         proxy_GetIntPref(prefBranch, "network.proxy.ssl_port", mHTTPSProxyPort);
00427 
00428     if (!pref || !strcmp(pref, "network.proxy.ftp"))
00429         proxy_GetStringPref(prefBranch, "network.proxy.ftp", mFTPProxyHost);
00430 
00431     if (!pref || !strcmp(pref, "network.proxy.ftp_port"))
00432         proxy_GetIntPref(prefBranch, "network.proxy.ftp_port", mFTPProxyPort);
00433 
00434     if (!pref || !strcmp(pref, "network.proxy.gopher"))
00435         proxy_GetStringPref(prefBranch, "network.proxy.gopher", mGopherProxyHost);
00436 
00437     if (!pref || !strcmp(pref, "network.proxy.gopher_port"))
00438         proxy_GetIntPref(prefBranch, "network.proxy.gopher_port", mGopherProxyPort);
00439 
00440     if (!pref || !strcmp(pref, "network.proxy.socks"))
00441         proxy_GetStringPref(prefBranch, "network.proxy.socks", mSOCKSProxyHost);
00442     
00443     if (!pref || !strcmp(pref, "network.proxy.socks_port"))
00444         proxy_GetIntPref(prefBranch, "network.proxy.socks_port", mSOCKSProxyPort);
00445 
00446     if (!pref || !strcmp(pref, "network.proxy.socks_version")) {
00447         PRInt32 version;
00448         proxy_GetIntPref(prefBranch, "network.proxy.socks_version", version);
00449         // make sure this preference value remains sane
00450         if (version == 5)
00451             mSOCKSProxyVersion = 5;
00452         else
00453             mSOCKSProxyVersion = 4;
00454     }
00455 
00456     if (!pref || !strcmp(pref, "network.proxy.socks_remote_dns"))
00457         proxy_GetBoolPref(prefBranch, "network.proxy.socks_remote_dns", mSOCKSProxyRemoteDNS);
00458 
00459     if (!pref || !strcmp(pref, "network.proxy.failover_timeout"))
00460         proxy_GetIntPref(prefBranch, "network.proxy.failover_timeout", mFailedProxyTimeout);
00461 
00462     if (!pref || !strcmp(pref, "network.proxy.no_proxies_on")) {
00463         rv = prefBranch->GetCharPref("network.proxy.no_proxies_on",
00464                                      getter_Copies(tempString));
00465         if (NS_SUCCEEDED(rv))
00466             LoadHostFilters(tempString.get());
00467     }
00468 
00469     // We're done if not using PAC or WPAD
00470     if (mProxyConfig != eProxyConfig_PAC && mProxyConfig != eProxyConfig_WPAD)
00471         return;
00472 
00473     // OK, we need to reload the PAC file if:
00474     //  1) network.proxy.type changed, or
00475     //  2) network.proxy.autoconfig_url changed and PAC is configured
00476 
00477     if (!pref || !strcmp(pref, "network.proxy.autoconfig_url"))
00478         reloadPAC = PR_TRUE;
00479 
00480     if (reloadPAC) {
00481         tempString.Truncate();
00482         if (mProxyConfig == eProxyConfig_PAC) {
00483             prefBranch->GetCharPref("network.proxy.autoconfig_url",
00484                                     getter_Copies(tempString));
00485         }
00486         else if (mProxyConfig == eProxyConfig_WPAD) {
00487             // We diverge from the WPAD spec here in that we don't walk the
00488             // hosts's FQDN, stripping components until we hit a TLD.  Doing so
00489             // is dangerous in the face of an incomplete list of TLDs, and TLDs
00490             // get added over time.  We could consider doing only a single
00491             // substitution of the first component, if that proves to help
00492             // compatibility.
00493             tempString.AssignLiteral("http://wpad/wpad.dat");
00494         }
00495         ConfigureFromPAC(tempString);
00496     }
00497 }
00498 
00499 PRBool
00500 nsProtocolProxyService::CanUseProxy(nsIURI *aURI, PRInt32 defaultPort) 
00501 {
00502     if (mHostFiltersArray.Count() == 0)
00503         return PR_TRUE;
00504 
00505     PRInt32 port;
00506     nsCAutoString host;
00507  
00508     nsresult rv = aURI->GetAsciiHost(host);
00509     if (NS_FAILED(rv) || host.IsEmpty())
00510         return PR_FALSE;
00511 
00512     rv = aURI->GetPort(&port);
00513     if (NS_FAILED(rv))
00514         return PR_FALSE;
00515     if (port == -1)
00516         port = defaultPort;
00517 
00518     PRNetAddr addr;
00519     PRBool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
00520 
00521     PRIPv6Addr ipv6;
00522     if (is_ipaddr) {
00523         // convert parsed address to IPv6
00524         if (addr.raw.family == PR_AF_INET) {
00525             // convert to IPv4-mapped address
00526             PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
00527         }
00528         else if (addr.raw.family == PR_AF_INET6) {
00529             // copy the address
00530             memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
00531         }
00532         else {
00533             NS_WARNING("unknown address family");
00534             return PR_TRUE; // allow proxying
00535         }
00536     }
00537     
00538     PRInt32 index = -1;
00539     while (++index < mHostFiltersArray.Count()) {
00540         HostInfo *hinfo = (HostInfo *) mHostFiltersArray[index];
00541 
00542         if (is_ipaddr != hinfo->is_ipaddr)
00543             continue;
00544         if (hinfo->port && hinfo->port != port)
00545             continue;
00546 
00547         if (is_ipaddr) {
00548             // generate masked version of target IPv6 address
00549             PRIPv6Addr masked;
00550             memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
00551             proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
00552 
00553             // check for a match
00554             if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0)
00555                 return PR_FALSE; // proxy disallowed
00556         }
00557         else {
00558             PRUint32 host_len = host.Length();
00559             PRUint32 filter_host_len = hinfo->name.host_len;
00560 
00561             if (host_len >= filter_host_len) {
00562                 //
00563                 // compare last |filter_host_len| bytes of target hostname.
00564                 //
00565                 const char *host_tail = host.get() + host_len - filter_host_len;
00566                 if (!PL_strncasecmp(host_tail, hinfo->name.host, filter_host_len))
00567                     return PR_FALSE; // proxy disallowed
00568             }
00569         }
00570     }
00571     return PR_TRUE;
00572 }
00573 
00574 static const char kProxyType_HTTP[]    = "http";
00575 static const char kProxyType_PROXY[]   = "proxy";
00576 static const char kProxyType_SOCKS[]   = "socks";
00577 static const char kProxyType_SOCKS4[]  = "socks4";
00578 static const char kProxyType_SOCKS5[]  = "socks5";
00579 static const char kProxyType_DIRECT[]  = "direct";
00580 static const char kProxyType_UNKNOWN[] = "unknown";
00581 
00582 const char *
00583 nsProtocolProxyService::ExtractProxyInfo(const char *start, nsProxyInfo **result)
00584 {
00585     *result = nsnull;
00586     PRUint32 flags = 0;
00587 
00588     // see BNF in nsIProxyAutoConfig.idl
00589 
00590     // find end of proxy info delimiter
00591     const char *end = start;
00592     while (*end && *end != ';') ++end;
00593 
00594     // find end of proxy type delimiter
00595     const char *sp = start;
00596     while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
00597 
00598     PRUint32 len = sp - start;
00599     const char *type = nsnull;
00600     switch (len) {
00601     case 5:
00602         if (PL_strncasecmp(start, kProxyType_PROXY, 5) == 0)
00603             type = kProxyType_HTTP;
00604         else if (PL_strncasecmp(start, kProxyType_SOCKS, 5) == 0)
00605             type = kProxyType_SOCKS4; // assume v4 for 4x compat
00606         break;
00607     case 6:
00608         if (PL_strncasecmp(start, kProxyType_DIRECT, 6) == 0)
00609             type = kProxyType_DIRECT;
00610         else if (PL_strncasecmp(start, kProxyType_SOCKS4, 6) == 0)
00611             type = kProxyType_SOCKS4;
00612         else if (PL_strncasecmp(start, kProxyType_SOCKS5, 6) == 0)
00613             // map "SOCKS5" to "socks" to match contract-id of registered
00614             // SOCKS-v5 socket provider.
00615             type = kProxyType_SOCKS;
00616         break;
00617     }
00618     if (type) {
00619         const char *host = nsnull, *hostEnd = nsnull;
00620         PRInt32 port = -1;
00621 
00622         // If it's a SOCKS5 proxy, do name resolution on the server side.
00623         // We could use this with SOCKS4a servers too, but they might not
00624         // support it.
00625         if (type == kProxyType_SOCKS)
00626             flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
00627 
00628         // extract host:port
00629         start = sp;
00630         while ((*start == ' ' || *start == '\t') && start < end)
00631             start++;
00632         if (start < end) {
00633             host = start;
00634             hostEnd = strchr(host, ':');
00635             if (!hostEnd || hostEnd > end) {
00636                 hostEnd = end;
00637                 // no port, so assume default
00638                 if (type == kProxyType_HTTP)
00639                     port = 80;
00640                 else
00641                     port = 1080;
00642             }
00643             else
00644                 port = atoi(hostEnd + 1);
00645         }
00646         nsProxyInfo *pi = new nsProxyInfo;
00647         if (pi) {
00648             pi->mType = type;
00649             pi->mFlags = flags;
00650             pi->mTimeout = mFailedProxyTimeout;
00651             // YES, it is ok to specify a null proxy host.
00652             if (host) {
00653                 pi->mHost.Assign(host, hostEnd - host);
00654                 pi->mPort = port;
00655             }
00656             NS_ADDREF(*result = pi);
00657         }
00658     }
00659 
00660     while (*end == ';' || *end == ' ' || *end == '\t')
00661         ++end;
00662     return end;
00663 }
00664 
00665 void
00666 nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key)
00667 {
00668     key.AssignASCII(pi->mType);
00669     if (!pi->mHost.IsEmpty()) {
00670         key.Append(' ');
00671         key.Append(pi->mHost);
00672         key.Append(':');
00673         key.AppendInt(pi->mPort);
00674     }
00675 } 
00676 
00677 PRUint32
00678 nsProtocolProxyService::SecondsSinceSessionStart()
00679 {
00680     PRTime now = PR_Now();
00681 
00682     // get time elapsed since session start
00683     PRInt64 diff;
00684     LL_SUB(diff, now, mSessionStart);
00685 
00686     // convert microseconds to seconds
00687     PRTime ups;
00688     LL_I2L(ups, PR_USEC_PER_SEC);
00689     LL_DIV(diff, diff, ups);
00690 
00691     // convert to 32 bit value
00692     PRUint32 dsec;
00693     LL_L2UI(dsec, diff);
00694 
00695     return dsec;
00696 }
00697 
00698 void
00699 nsProtocolProxyService::EnableProxy(nsProxyInfo *pi)
00700 {
00701     nsCAutoString key;
00702     GetProxyKey(pi, key);
00703     mFailedProxies.Remove(key);
00704 }
00705 
00706 void
00707 nsProtocolProxyService::DisableProxy(nsProxyInfo *pi)
00708 {
00709     nsCAutoString key;
00710     GetProxyKey(pi, key);
00711 
00712     PRUint32 dsec = SecondsSinceSessionStart();
00713 
00714     // Add timeout to interval (this is the time when the proxy can
00715     // be tried again).
00716     dsec += pi->mTimeout;
00717 
00718     // NOTE: The classic codebase would increase the timeout value
00719     //       incrementally each time a subsequent failure occured.
00720     //       We could do the same, but it would require that we not
00721     //       remove proxy entries in IsProxyDisabled or otherwise
00722     //       change the way we are recording disabled proxies.
00723     //       Simpler is probably better for now, and at least the
00724     //       user can tune the timeout setting via preferences.
00725 
00726     LOG(("DisableProxy %s %d\n", key.get(), dsec));
00727 
00728     // If this fails, oh well... means we don't have enough memory
00729     // to remember the failed proxy.
00730     mFailedProxies.Put(key, dsec);
00731 }
00732 
00733 PRBool
00734 nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi)
00735 {
00736     nsCAutoString key;
00737     GetProxyKey(pi, key);
00738 
00739     PRUint32 val;
00740     if (!mFailedProxies.Get(key, &val))
00741         return PR_FALSE;
00742 
00743     PRUint32 dsec = SecondsSinceSessionStart();
00744 
00745     // if time passed has exceeded interval, then try proxy again.
00746     if (dsec > val) {
00747         mFailedProxies.Remove(key);
00748         return PR_FALSE;
00749     }
00750 
00751     return PR_TRUE;
00752 }
00753 
00754 void
00755 nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
00756                                          nsIProxyInfo **result)
00757 {
00758     if (pacString.IsEmpty()) {
00759         *result = nsnull;
00760         return;
00761     }
00762 
00763     const char *proxies = pacString.get();
00764 
00765     nsProxyInfo *pi = nsnull, *first = nsnull, *last = nsnull;
00766     while (*proxies) {
00767         proxies = ExtractProxyInfo(proxies, &pi);
00768         if (pi) {
00769             if (last) {
00770                 NS_ASSERTION(last->mNext == nsnull, "leaking nsProxyInfo");
00771                 last->mNext = pi;
00772             }
00773             else
00774                 first = pi;
00775             last = pi;
00776         }
00777     }
00778     *result = first;
00779 }
00780 
00781 // nsIProtocolProxyService
00782 NS_IMETHODIMP
00783 nsProtocolProxyService::Resolve(nsIURI *uri, PRUint32 flags,
00784                                 nsIProxyInfo **result)
00785 {
00786     nsProtocolInfo info;
00787     nsresult rv = GetProtocolInfo(uri, &info);
00788     if (NS_FAILED(rv))
00789         return rv;
00790 
00791     PRBool usePAC;
00792     rv = Resolve_Internal(uri, info, &usePAC, result);
00793     if (NS_FAILED(rv))
00794         return rv;
00795 
00796     if (usePAC && mPACMan) {
00797         NS_ASSERTION(*result == nsnull, "we should not have a result yet");
00798 
00799         // If the caller didn't want us to invoke PAC, then error out.
00800         if (flags & RESOLVE_NON_BLOCKING)
00801             return NS_BASE_STREAM_WOULD_BLOCK;
00802 
00803         // Query the PAC file synchronously.
00804         nsCString pacString;
00805         rv = mPACMan->GetProxyForURI(uri, pacString);
00806         if (NS_SUCCEEDED(rv))
00807             ProcessPACString(pacString, result);
00808         else if (rv == NS_ERROR_IN_PROGRESS) {
00809             // Construct a special UNKNOWN proxy entry that informs the caller
00810             // that the proxy info is yet to be determined.
00811             rv = NewProxyInfo_Internal(kProxyType_UNKNOWN, EmptyCString(), -1,
00812                                        0, 0, nsnull, result);
00813             if (NS_FAILED(rv))
00814                 return rv;
00815         }
00816         else
00817             NS_WARNING("failed querying PAC file; trying DIRECT");
00818     }
00819 
00820     ApplyFilters(uri, info, result);
00821     return NS_OK;
00822 }
00823 
00824 NS_IMETHODIMP
00825 nsProtocolProxyService::AsyncResolve(nsIURI *uri, PRUint32 flags,
00826                                      nsIProtocolProxyCallback *callback,
00827                                      nsICancelable **result)
00828 {
00829     nsRefPtr<nsAsyncResolveRequest> ctx =
00830             new nsAsyncResolveRequest(this, uri, callback);
00831     if (!ctx)
00832         return NS_ERROR_OUT_OF_MEMORY;
00833 
00834     nsProtocolInfo info;
00835     nsresult rv = GetProtocolInfo(uri, &info);
00836     if (NS_FAILED(rv))
00837         return rv;
00838 
00839     PRBool usePAC;
00840     nsCOMPtr<nsIProxyInfo> pi;
00841     rv = Resolve_Internal(uri, info, &usePAC, getter_AddRefs(pi));
00842     if (NS_FAILED(rv))
00843         return rv;
00844 
00845     if (!usePAC || !mPACMan) {
00846         ApplyFilters(uri, info, pi);
00847 
00848         ctx->SetResult(NS_OK, pi);
00849         return ctx->DispatchCallback();
00850     }
00851 
00852     // else kick off a PAC query
00853     rv = mPACMan->AsyncGetProxyForURI(uri, ctx);
00854     if (NS_SUCCEEDED(rv)) {
00855         *result = ctx;
00856         NS_ADDREF(*result);
00857     }
00858     return rv;
00859 }
00860 
00861 NS_IMETHODIMP
00862 nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
00863                                      const nsACString &aHost,
00864                                      PRInt32 aPort,
00865                                      PRUint32 aFlags,
00866                                      PRUint32 aFailoverTimeout,
00867                                      nsIProxyInfo *aFailoverProxy,
00868                                      nsIProxyInfo **aResult)
00869 {
00870     static const char *types[] = {
00871         kProxyType_HTTP,
00872         kProxyType_SOCKS,
00873         kProxyType_SOCKS4,
00874         kProxyType_DIRECT
00875     };
00876 
00877     // resolve type; this allows us to avoid copying the type string into each
00878     // proxy info instance.  we just reference the string literals directly :)
00879     const char *type = nsnull;
00880     for (PRUint32 i=0; i<NS_ARRAY_LENGTH(types); ++i) {
00881         if (aType.LowerCaseEqualsASCII(types[i])) {
00882             type = types[i];
00883             break;
00884         }
00885     }
00886     NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
00887 
00888     if (aPort <= 0)
00889         aPort = -1;
00890 
00891     return NewProxyInfo_Internal(type, aHost, aPort, aFlags, aFailoverTimeout,
00892                                  aFailoverProxy, aResult);
00893 }
00894 
00895 NS_IMETHODIMP
00896 nsProtocolProxyService::ConfigureFromPAC(const nsACString &spec)
00897 {
00898     if (!mPACMan) {
00899         mPACMan = new nsPACMan();
00900         if (!mPACMan)
00901             return NS_ERROR_OUT_OF_MEMORY;
00902     }
00903 
00904     mFailedProxies.Clear();
00905 
00906     nsCOMPtr<nsIURI> pacURI;
00907     nsresult rv = NS_NewURI(getter_AddRefs(pacURI), spec);
00908     if (NS_FAILED(rv))
00909         return rv;
00910 
00911     return mPACMan->LoadPACFromURI(pacURI);
00912 }
00913 
00914 NS_IMETHODIMP
00915 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo  *aProxy,
00916                                             nsIURI        *aURI,
00917                                             nsresult       aStatus,
00918                                             nsIProxyInfo **aResult)
00919 {
00920     // We only support failover when a PAC file is configured.
00921     if (mProxyConfig != eProxyConfig_PAC && mProxyConfig != eProxyConfig_WPAD)
00922         return NS_ERROR_NOT_AVAILABLE;
00923 
00924     // Verify that |aProxy| is one of our nsProxyInfo objects.
00925     nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
00926     NS_ENSURE_ARG(pi);
00927     // OK, the QI checked out.  We can proceed.
00928 
00929     // Remember that this proxy is down.
00930     DisableProxy(pi);
00931 
00932     // NOTE: At this point, we might want to prompt the user if we have
00933     //       not already tried going DIRECT.  This is something that the
00934     //       classic codebase supported; however, IE6 does not prompt.
00935 
00936     if (!pi->mNext)
00937         return NS_ERROR_NOT_AVAILABLE;
00938 
00939     LOG(("PAC failover from %s %s:%d to %s %s:%d\n",
00940         pi->mType, pi->mHost.get(), pi->mPort,
00941         pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
00942 
00943     NS_ADDREF(*aResult = pi->mNext);
00944     return NS_OK;
00945 }
00946 
00947 NS_IMETHODIMP
00948 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
00949                                        PRUint32 position)
00950 {
00951     UnregisterFilter(filter);  // remove this filter if we already have it
00952 
00953     FilterLink *link = new FilterLink(position, filter);
00954     if (!link)
00955         return NS_ERROR_OUT_OF_MEMORY;
00956 
00957     if (!mFilters) {
00958         mFilters = link;
00959         return NS_OK;
00960     }
00961 
00962     // insert into mFilters in sorted order
00963     FilterLink *last = nsnull;
00964     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
00965         if (position < iter->position) {
00966             if (last) {
00967                 link->next = last->next;
00968                 last->next = link;
00969             } else {
00970                 link->next = mFilters;
00971                 mFilters = link;
00972             }
00973             return NS_OK;
00974         }
00975         last = iter;
00976     }
00977     // our position is equal to or greater than the last link in the list
00978     last->next = link;
00979     return NS_OK;
00980 }
00981 
00982 NS_IMETHODIMP
00983 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter)
00984 {
00985     // QI to nsISupports so we can safely test object identity.
00986     nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
00987 
00988     FilterLink *last = nsnull;
00989     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
00990         nsCOMPtr<nsISupports> object = do_QueryInterface(iter->filter);
00991         if (object == givenObject) {
00992             if (last)
00993                 last->next = iter->next;
00994             else
00995                 mFilters = iter->next;
00996             iter->next = nsnull;
00997             delete iter;
00998             return NS_OK;
00999         }
01000         last = iter;
01001     }
01002 
01003     // No need to throw an exception in this case.
01004     return NS_OK;
01005 }
01006 
01007 PRBool PR_CALLBACK
01008 nsProtocolProxyService::CleanupFilterArray(void *aElement, void *aData) 
01009 {
01010     if (aElement)
01011         delete (HostInfo *) aElement;
01012 
01013     return PR_TRUE;
01014 }
01015 
01016 void
01017 nsProtocolProxyService::LoadHostFilters(const char *filters)
01018 {
01019     // check to see the owners flag? /!?/ TODO
01020     if (mHostFiltersArray.Count() > 0) {
01021         mHostFiltersArray.EnumerateForwards(CleanupFilterArray, nsnull);
01022         mHostFiltersArray.Clear();
01023     }
01024 
01025     if (!filters)
01026         return; // fail silently...
01027 
01028     //
01029     // filter  = ( host | domain | ipaddr ["/" mask] ) [":" port] 
01030     // filters = filter *( "," LWS filter)
01031     //
01032     while (*filters) {
01033         // skip over spaces and ,
01034         while (*filters && (*filters == ',' || IS_ASCII_SPACE(*filters)))
01035             filters++;
01036 
01037         const char *starthost = filters;
01038         const char *endhost = filters + 1; // at least that...
01039         const char *portLocation = 0; 
01040         const char *maskLocation = 0;
01041 
01042         //
01043         // XXX this needs to be fixed to support IPv6 address literals,
01044         // which in this context will need to be []-escaped.
01045         //
01046         while (*endhost && (*endhost != ',' && !IS_ASCII_SPACE(*endhost))) {
01047             if (*endhost == ':')
01048                 portLocation = endhost;
01049             else if (*endhost == '/')
01050                 maskLocation = endhost;
01051             endhost++;
01052         }
01053 
01054         filters = endhost; // advance iterator up front
01055 
01056         HostInfo *hinfo = new HostInfo();
01057         if (!hinfo)
01058             return; // fail silently
01059         hinfo->port = portLocation ? atoi(portLocation + 1) : 0;
01060 
01061         // locate end of host
01062         const char *end = maskLocation ? maskLocation :
01063                           portLocation ? portLocation :
01064                           endhost;
01065 
01066         nsCAutoString str(starthost, end - starthost);
01067 
01068         PRNetAddr addr;
01069         if (PR_StringToNetAddr(str.get(), &addr) == PR_SUCCESS) {
01070             hinfo->is_ipaddr   = PR_TRUE;
01071             hinfo->ip.family   = PR_AF_INET6; // we always store address as IPv6
01072             hinfo->ip.mask_len = maskLocation ? atoi(maskLocation + 1) : 128;
01073 
01074             if (hinfo->ip.mask_len == 0) {
01075                 NS_WARNING("invalid mask");
01076                 goto loser;
01077             }
01078 
01079             if (addr.raw.family == PR_AF_INET) {
01080                 // convert to IPv4-mapped address
01081                 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
01082                 // adjust mask_len accordingly
01083                 if (hinfo->ip.mask_len <= 32)
01084                     hinfo->ip.mask_len += 96;
01085             }
01086             else if (addr.raw.family == PR_AF_INET6) {
01087                 // copy the address
01088                 memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
01089             }
01090             else {
01091                 NS_WARNING("unknown address family");
01092                 goto loser;
01093             }
01094 
01095             // apply mask to IPv6 address
01096             proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
01097         }
01098         else {
01099             PRUint32 startIndex, endIndex;
01100             if (str.First() == '*')
01101                 startIndex = 1; // *.domain -> .domain
01102             else
01103                 startIndex = 0;
01104             endIndex = (portLocation ? portLocation : endhost) - starthost;
01105 
01106             hinfo->is_ipaddr = PR_FALSE;
01107             hinfo->name.host = ToNewCString(Substring(str, startIndex, endIndex));
01108 
01109             if (!hinfo->name.host)
01110                 goto loser;
01111 
01112             hinfo->name.host_len = endIndex - startIndex;
01113         }
01114 
01115 //#define DEBUG_DUMP_FILTERS
01116 #ifdef DEBUG_DUMP_FILTERS
01117         printf("loaded filter[%u]:\n", mHostFiltersArray.Count());
01118         printf("  is_ipaddr = %u\n", hinfo->is_ipaddr);
01119         printf("  port = %u\n", hinfo->port);
01120         if (hinfo->is_ipaddr) {
01121             printf("  ip.family = %x\n", hinfo->ip.family);
01122             printf("  ip.mask_len = %u\n", hinfo->ip.mask_len);
01123 
01124             PRNetAddr netAddr;
01125             PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
01126             memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
01127 
01128             char buf[256];
01129             PR_NetAddrToString(&netAddr, buf, sizeof(buf));
01130 
01131             printf("  ip.addr = %s\n", buf);
01132         }
01133         else {
01134             printf("  name.host = %s\n", hinfo->name.host);
01135         }
01136 #endif
01137 
01138         mHostFiltersArray.AppendElement(hinfo);
01139         hinfo = nsnull;
01140 loser:
01141         if (hinfo)
01142             delete hinfo;
01143     }
01144 }
01145 
01146 nsresult
01147 nsProtocolProxyService::GetProtocolInfo(nsIURI *uri, nsProtocolInfo *info)
01148 {
01149     nsresult rv;
01150 
01151     rv = uri->GetScheme(info->scheme);
01152     if (NS_FAILED(rv))
01153         return rv;
01154 
01155     nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
01156     if (NS_FAILED(rv))
01157         return rv;
01158 
01159     nsCOMPtr<nsIProtocolHandler> handler;
01160     rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler));
01161     if (NS_FAILED(rv))
01162         return rv;
01163 
01164     rv = handler->GetProtocolFlags(&info->flags);
01165     if (NS_FAILED(rv))
01166         return rv;
01167 
01168     rv = handler->GetDefaultPort(&info->defaultPort);
01169     return rv;
01170 }
01171 
01172 nsresult
01173 nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
01174                                               const nsACString &aHost,
01175                                               PRInt32 aPort,
01176                                               PRUint32 aFlags,
01177                                               PRUint32 aFailoverTimeout,
01178                                               nsIProxyInfo *aFailoverProxy,
01179                                               nsIProxyInfo **aResult)
01180 {
01181     nsCOMPtr<nsProxyInfo> failover;
01182     if (aFailoverProxy) {
01183         failover = do_QueryInterface(aFailoverProxy);
01184         NS_ENSURE_ARG(failover);
01185     }
01186 
01187     nsProxyInfo *proxyInfo = new nsProxyInfo();
01188     if (!proxyInfo)
01189         return NS_ERROR_OUT_OF_MEMORY;
01190 
01191     proxyInfo->mType = aType;
01192     proxyInfo->mHost = aHost;
01193     proxyInfo->mPort = aPort;
01194     proxyInfo->mFlags = aFlags;
01195     proxyInfo->mTimeout = aFailoverTimeout == PR_UINT32_MAX
01196         ? mFailedProxyTimeout : aFailoverTimeout;
01197     failover.swap(proxyInfo->mNext);
01198 
01199     NS_ADDREF(*aResult = proxyInfo);
01200     return NS_OK;
01201 }
01202 
01203 nsresult
01204 nsProtocolProxyService::Resolve_Internal(nsIURI *uri,
01205                                          const nsProtocolInfo &info,
01206                                          PRBool *usePAC,
01207                                          nsIProxyInfo **result)
01208 {
01209     NS_ENSURE_ARG_POINTER(uri);
01210 
01211     *usePAC = PR_FALSE;
01212     *result = nsnull;
01213 
01214     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
01215         return NS_OK;  // Can't proxy this (filters may not override)
01216 
01217     // if proxies are enabled and this host:port combo is supposed to use a
01218     // proxy, check for a proxy.
01219     if (mProxyConfig == eProxyConfig_Direct ||
01220             (mProxyConfig == eProxyConfig_Manual &&
01221              !CanUseProxy(uri, info.defaultPort)))
01222         return NS_OK;
01223     
01224     // Proxy auto config magic...
01225     if (mProxyConfig == eProxyConfig_PAC || mProxyConfig == eProxyConfig_WPAD) {
01226         // Do not query PAC now.
01227         *usePAC = PR_TRUE;
01228         return NS_OK;
01229     }
01230 
01231     // proxy info values
01232     const char *type = nsnull;
01233     const nsACString *host = nsnull;
01234     PRInt32 port = -1;
01235 
01236     PRUint32 proxyFlags = 0;
01237 
01238     if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
01239         info.scheme.EqualsLiteral("http")) {
01240         host = &mHTTPProxyHost;
01241         type = kProxyType_HTTP;
01242         port = mHTTPProxyPort;
01243     }
01244     else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
01245              info.scheme.EqualsLiteral("https")) {
01246         host = &mHTTPSProxyHost;
01247         type = kProxyType_HTTP;
01248         port = mHTTPSProxyPort;
01249     }
01250     else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 &&
01251              info.scheme.EqualsLiteral("ftp")) {
01252         host = &mFTPProxyHost;
01253         type = kProxyType_HTTP;
01254         port = mFTPProxyPort;
01255     }
01256     else if (!mGopherProxyHost.IsEmpty() && mGopherProxyPort > 0 &&
01257              info.scheme.EqualsLiteral("gopher")) {
01258         host = &mGopherProxyHost;
01259         type = kProxyType_HTTP;
01260         port = mGopherProxyPort;
01261     }
01262     else if (!mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
01263         host = &mSOCKSProxyHost;
01264         if (mSOCKSProxyVersion == 4) 
01265             type = kProxyType_SOCKS4;
01266         else
01267             type = kProxyType_SOCKS;
01268         port = mSOCKSProxyPort;
01269         if (mSOCKSProxyRemoteDNS)
01270             proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
01271     }
01272 
01273     if (type) {
01274         nsresult rv = NewProxyInfo_Internal(type, *host, port, proxyFlags,
01275                                             PR_UINT32_MAX, nsnull, result);
01276         if (NS_FAILED(rv))
01277             return rv;
01278     }
01279 
01280     return NS_OK;
01281 }
01282 
01283 void
01284 nsProtocolProxyService::ApplyFilters(nsIURI *uri, const nsProtocolInfo &info,
01285                                      nsIProxyInfo **list)
01286 {
01287     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
01288         return;
01289 
01290     // We prune the proxy list prior to invoking each filter.  This may be
01291     // somewhat inefficient, but it seems like a good idea since we want each
01292     // filter to "see" a valid proxy list.
01293 
01294     nsresult rv;
01295     nsCOMPtr<nsIProxyInfo> result;
01296 
01297     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
01298         PruneProxyInfo(info, list);
01299 
01300         rv = iter->filter->ApplyFilter(this, uri, *list,
01301                                        getter_AddRefs(result));
01302         if (NS_FAILED(rv))
01303             continue;
01304         result.swap(*list);
01305     }
01306 
01307     PruneProxyInfo(info, list);
01308 }
01309 
01310 void
01311 nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
01312                                        nsIProxyInfo **list)
01313 {
01314     if (!*list)
01315         return;
01316     nsProxyInfo *head = nsnull;
01317     CallQueryInterface(*list, &head);
01318     if (!head) {
01319         NS_NOTREACHED("nsIProxyInfo must QI to nsProxyInfo");
01320         return;
01321     }
01322     NS_RELEASE(*list);
01323 
01324     // Pruning of disabled proxies works like this:
01325     //   - If all proxies are disabled, return the full list
01326     //   - Otherwise, remove the disabled proxies.
01327     //
01328     // Pruning of disallowed proxies works like this:
01329     //   - If the protocol handler disallows the proxy, then we disallow it.
01330 
01331     // Start by removing all disallowed proxies if required:
01332     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
01333         nsProxyInfo *last = nsnull, *iter = head; 
01334         while (iter) {
01335             if (iter->Type() == kProxyType_HTTP) {
01336                 // reject!
01337                 if (last)
01338                     last->mNext = iter->mNext;
01339                 else
01340                     head = iter->mNext;
01341                 nsProxyInfo *next = iter->mNext;
01342                 iter->mNext = nsnull;
01343                 iter->Release();
01344                 iter = next;
01345             } else {
01346                 last = iter;
01347                 iter = iter->mNext;
01348             }
01349         }
01350         if (!head)
01351             return;
01352     }
01353 
01354     // Now, scan to see if all remaining proxies are disabled.  If so, then
01355     // we'll just bail and return them all.  Otherwise, we'll go and prune the
01356     // disabled ones.
01357     
01358     PRBool allDisabled = PR_TRUE;
01359 
01360     nsProxyInfo *iter;
01361     for (iter = head; iter; iter = iter->mNext) {
01362         if (!IsProxyDisabled(iter)) {
01363             allDisabled = PR_FALSE;
01364             break;
01365         }
01366     }
01367 
01368     if (allDisabled)
01369         LOG(("All proxies are disabled, so trying all again"));
01370     else {
01371         // remove any disabled proxies.
01372         nsProxyInfo *last = nsnull; 
01373         for (iter = head; iter; ) {
01374             if (IsProxyDisabled(iter)) {
01375                 // reject!
01376                 nsProxyInfo *reject = iter;
01377 
01378                 iter = iter->mNext;
01379                 if (last)
01380                     last->mNext = iter;
01381                 else
01382                     head = iter;
01383 
01384                 reject->mNext = nsnull;
01385                 NS_RELEASE(reject);
01386                 continue;
01387             }
01388 
01389             // since we are about to use this proxy, make sure it is not on
01390             // the disabled proxy list.  we'll add it back to that list if
01391             // we have to (in GetFailoverForProxy).
01392             //
01393             // XXX(darin): It might be better to do this as a final pass.
01394             //
01395             EnableProxy(iter);
01396 
01397             last = iter;
01398             iter = iter->mNext;
01399         }
01400     }
01401 
01402     // if only DIRECT was specified then return no proxy info, and we're done.
01403     if (head && !head->mNext && head->mType == kProxyType_DIRECT)
01404         NS_RELEASE(head);
01405 
01406     *list = head;  // Transfer ownership
01407 }