Back to index

lightning-sunbird  0.9+nobinonly
nsHttpHandler.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 cin: */
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.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Darin Fisher <darin@netscape.com> (original author)
00025  *   Gagan Saksena <gagan@netscape.com>
00026  *   Pierre Phaneuf <pp@ludusdesign.com>
00027  *   Christopher Blizzard <blizzard@mozilla.org>
00028  *   Adrian Havill <havill@redhat.com>
00029  *   Gervase Markham <gerv@gerv.net>
00030  *   Bradley Baetz <bbaetz@netscape.com>
00031  *   Benjamin Smedberg <bsmedberg@covad.net>
00032  *   Josh Aas <josh@mozilla.com>
00033  *
00034  * Alternatively, the contents of this file may be used under the terms of
00035  * either the GNU General Public License Version 2 or later (the "GPL"), or
00036  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00037  * in which case the provisions of the GPL or the LGPL are applicable instead
00038  * of those above. If you wish to allow use of your version of this file only
00039  * under the terms of either the GPL or the LGPL, and not to allow others to
00040  * use your version of this file under the terms of the MPL, indicate your
00041  * decision by deleting the provisions above and replace them with the notice
00042  * and other provisions required by the GPL or the LGPL. If you do not delete
00043  * the provisions above, a recipient may use your version of this file under
00044  * the terms of any one of the MPL, the GPL or the LGPL.
00045  *
00046  * ***** END LICENSE BLOCK ***** */
00047 
00048 #include "nsHttp.h"
00049 #include "nsHttpHandler.h"
00050 #include "nsHttpChannel.h"
00051 #include "nsHttpConnection.h"
00052 #include "nsHttpResponseHead.h"
00053 #include "nsHttpTransaction.h"
00054 #include "nsHttpAuthCache.h"
00055 #include "nsStandardURL.h"
00056 #include "nsIHttpChannel.h"
00057 #include "nsIURL.h"
00058 #include "nsIStandardURL.h"
00059 #include "nsICacheService.h"
00060 #include "nsICategoryManager.h"
00061 #include "nsCategoryManagerUtils.h"
00062 #include "nsICacheService.h"
00063 #include "nsIPrefService.h"
00064 #include "nsIPrefBranch2.h"
00065 #include "nsIPrefLocalizedString.h"
00066 #include "nsISocketProviderService.h"
00067 #include "nsISocketProvider.h"
00068 #include "nsPrintfCString.h"
00069 #include "nsCOMPtr.h"
00070 #include "nsNetCID.h"
00071 #include "nsAutoLock.h"
00072 #include "prprf.h"
00073 #include "nsReadableUtils.h"
00074 #include "nsQuickSort.h"
00075 #include "nsNetUtil.h"
00076 #include "nsIOService.h"
00077 
00078 #if defined(XP_UNIX) || defined(XP_BEOS)
00079 #include <sys/utsname.h>
00080 #endif
00081 
00082 #if defined(XP_WIN)
00083 #include <windows.h>
00084 #endif
00085 
00086 #ifdef DEBUG
00087 // defined by the socket transport service while active
00088 extern PRThread *gSocketThread;
00089 #endif
00090 
00091 static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
00092 static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID);
00093 static NS_DEFINE_CID(kCookieServiceCID, NS_COOKIESERVICE_CID);
00094 static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID);
00095 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00096 static NS_DEFINE_CID(kSocketProviderServiceCID, NS_SOCKETPROVIDERSERVICE_CID);
00097 
00098 #define UA_PREF_PREFIX          "general.useragent."
00099 #define UA_APPNAME              "Mozilla"
00100 #define UA_APPVERSION           "5.0"
00101 #define UA_APPSECURITY_FALLBACK "N"
00102 
00103 #define HTTP_PREF_PREFIX        "network.http."
00104 #define INTL_ACCEPT_LANGUAGES   "intl.accept_languages"
00105 #define INTL_ACCEPT_CHARSET     "intl.charset.default"
00106 #define NETWORK_ENABLEIDN       "network.enableIDN"
00107 #define BROWSER_PREF_PREFIX     "browser.cache."
00108 
00109 #define UA_PREF(_pref) UA_PREF_PREFIX _pref
00110 #define HTTP_PREF(_pref) HTTP_PREF_PREFIX _pref
00111 #define BROWSER_PREF(_pref) BROWSER_PREF_PREFIX _pref
00112 
00113 //-----------------------------------------------------------------------------
00114 
00115 static nsresult
00116 NewURI(const nsACString &aSpec,
00117        const char *aCharset,
00118        nsIURI *aBaseURI,
00119        PRInt32 aDefaultPort,
00120        nsIURI **aURI)
00121 {
00122     nsStandardURL *url = new nsStandardURL();
00123     if (!url)
00124         return NS_ERROR_OUT_OF_MEMORY;
00125     NS_ADDREF(url);
00126 
00127     nsresult rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY,
00128                             aDefaultPort, aSpec, aCharset, aBaseURI);
00129     if (NS_FAILED(rv)) {
00130         NS_RELEASE(url);
00131         return rv;
00132     }
00133 
00134     *aURI = url; // no QI needed
00135     return NS_OK;
00136 }
00137 
00138 //-----------------------------------------------------------------------------
00139 // nsHttpHandler <public>
00140 //-----------------------------------------------------------------------------
00141 
00142 nsHttpHandler *gHttpHandler = nsnull;
00143 
00144 nsHttpHandler::nsHttpHandler()
00145     : mConnMgr(nsnull)
00146     , mHttpVersion(NS_HTTP_VERSION_1_1)
00147     , mProxyHttpVersion(NS_HTTP_VERSION_1_1)
00148     , mCapabilities(NS_HTTP_ALLOW_KEEPALIVE)
00149     , mProxyCapabilities(NS_HTTP_ALLOW_KEEPALIVE)
00150     , mReferrerLevel(0xff) // by default we always send a referrer
00151     , mIdleTimeout(10)
00152     , mMaxRequestAttempts(10)
00153     , mMaxRequestDelay(10)
00154     , mMaxConnections(24)
00155     , mMaxConnectionsPerServer(8)
00156     , mMaxPersistentConnectionsPerServer(2)
00157     , mMaxPersistentConnectionsPerProxy(4)
00158     , mMaxPipelinedRequests(2)
00159     , mRedirectionLimit(10)
00160     , mPhishyUserPassLength(1)
00161     , mLastUniqueID(NowInSeconds())
00162     , mSessionStartTime(0)
00163     , mUserAgentIsDirty(PR_TRUE)
00164     , mUseCache(PR_TRUE)
00165     , mSendSecureXSiteReferrer(PR_TRUE)
00166     , mEnablePersistentHttpsCaching(PR_FALSE)
00167 {
00168 #if defined(PR_LOGGING)
00169     gHttpLog = PR_NewLogModule("nsHttp");
00170 #endif
00171 
00172     LOG(("Creating nsHttpHandler [this=%x].\n", this));
00173 
00174     NS_ASSERTION(!gHttpHandler, "HTTP handler already created!");
00175     gHttpHandler = this;
00176 }
00177 
00178 nsHttpHandler::~nsHttpHandler()
00179 {
00180     // We do not deal with the timer cancellation in the destructor since
00181     // it is taken care of in xpcom shutdown event in the Observe method.
00182 
00183     LOG(("Deleting nsHttpHandler [this=%x]\n", this));
00184 
00185     // make sure the connection manager is shutdown
00186     if (mConnMgr) {
00187         mConnMgr->Shutdown();
00188         NS_RELEASE(mConnMgr);
00189     }
00190 
00191     nsHttp::DestroyAtomTable();
00192 
00193     gHttpHandler = nsnull;
00194 }
00195 
00196 nsresult
00197 nsHttpHandler::Init()
00198 {
00199     nsresult rv;
00200 
00201     LOG(("nsHttpHandler::Init\n"));
00202 
00203     rv = nsHttp::CreateAtomTable();
00204     if (NS_FAILED(rv))
00205         return rv;
00206 
00207     mIOService = do_GetService(kIOServiceCID, &rv);
00208     if (NS_FAILED(rv)) {
00209         NS_WARNING("unable to continue without io service");
00210         return rv;
00211     }
00212 
00213     InitUserAgentComponents();
00214 
00215     // monitor some preference changes
00216     nsCOMPtr<nsIPrefBranch2> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
00217     if (prefBranch) {
00218         prefBranch->AddObserver(HTTP_PREF_PREFIX, this, PR_TRUE);
00219         prefBranch->AddObserver(UA_PREF_PREFIX, this, PR_TRUE);
00220         prefBranch->AddObserver(INTL_ACCEPT_LANGUAGES, this, PR_TRUE); 
00221         prefBranch->AddObserver(INTL_ACCEPT_CHARSET, this, PR_TRUE);
00222         prefBranch->AddObserver(NETWORK_ENABLEIDN, this, PR_TRUE);
00223         prefBranch->AddObserver(BROWSER_PREF("disk_cache_ssl"), this, PR_TRUE);
00224 
00225         PrefsChanged(prefBranch, nsnull);
00226     }
00227 
00228     mMisc.AssignLiteral("rv:" MOZILLA_VERSION);
00229 
00230 #if DEBUG
00231     // dump user agent prefs
00232     LOG(("> app-name = %s\n", mAppName.get()));
00233     LOG(("> app-version = %s\n", mAppVersion.get()));
00234     LOG(("> platform = %s\n", mPlatform.get()));
00235     LOG(("> oscpu = %s\n", mOscpu.get()));
00236     LOG(("> security = %s\n", mSecurity.get()));
00237     LOG(("> language = %s\n", mLanguage.get()));
00238     LOG(("> misc = %s\n", mMisc.get()));
00239     LOG(("> vendor = %s\n", mVendor.get()));
00240     LOG(("> vendor-sub = %s\n", mVendorSub.get()));
00241     LOG(("> vendor-comment = %s\n", mVendorComment.get()));
00242     LOG(("> extra = %s\n", mExtraUA.get()));
00243     LOG(("> product = %s\n", mProduct.get()));
00244     LOG(("> product-sub = %s\n", mProductSub.get()));
00245     LOG(("> product-comment = %s\n", mProductComment.get()));
00246     LOG(("> user-agent = %s\n", UserAgent().get()));
00247 #endif
00248 
00249     mSessionStartTime = NowInSeconds();
00250 
00251     rv = mAuthCache.Init();
00252     if (NS_FAILED(rv)) return rv;
00253 
00254     rv = InitConnectionMgr();
00255     if (NS_FAILED(rv)) return rv;
00256 
00257     // Startup the http category
00258     // Bring alive the objects in the http-protocol-startup category
00259     NS_CreateServicesFromCategory(NS_HTTP_STARTUP_CATEGORY,
00260                                   NS_STATIC_CAST(nsISupports*,NS_STATIC_CAST(void*,this)),
00261                                   NS_HTTP_STARTUP_TOPIC);    
00262     
00263     mObserverService = do_GetService("@mozilla.org/observer-service;1");
00264     if (mObserverService) {
00265         mObserverService->AddObserver(this, "profile-change-net-teardown", PR_TRUE);
00266         mObserverService->AddObserver(this, "profile-change-net-restore", PR_TRUE);
00267         mObserverService->AddObserver(this, "session-logout", PR_TRUE);
00268         mObserverService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE);
00269     }
00270  
00271     StartPruneDeadConnectionsTimer();
00272     return NS_OK;
00273 }
00274 
00275 nsresult
00276 nsHttpHandler::InitConnectionMgr()
00277 {
00278     nsresult rv;
00279 
00280     if (!mConnMgr) {
00281         mConnMgr = new nsHttpConnectionMgr();
00282         if (!mConnMgr)
00283             return NS_ERROR_OUT_OF_MEMORY;
00284         NS_ADDREF(mConnMgr);
00285     }
00286 
00287     rv = mConnMgr->Init(mMaxConnections,
00288                         mMaxConnectionsPerServer,
00289                         mMaxConnectionsPerServer,
00290                         mMaxPersistentConnectionsPerServer,
00291                         mMaxPersistentConnectionsPerProxy,
00292                         mMaxRequestDelay,
00293                         mMaxPipelinedRequests);
00294     return rv;
00295 }
00296 
00297 void
00298 nsHttpHandler::StartPruneDeadConnectionsTimer()
00299 {
00300     LOG(("nsHttpHandler::StartPruneDeadConnectionsTimer\n"));
00301 
00302     mTimer = do_CreateInstance("@mozilla.org/timer;1");
00303     NS_ASSERTION(mTimer, "no timer");
00304     // failure to create a timer is not a fatal error, but idle connections
00305     // will not be cleaned up until we try to use them.
00306     if (mTimer)
00307         mTimer->Init(this, 15*1000, // every 15 seconds
00308                      nsITimer::TYPE_REPEATING_SLACK);
00309 }
00310 
00311 void
00312 nsHttpHandler::StopPruneDeadConnectionsTimer()
00313 {
00314     LOG(("nsHttpHandler::StopPruneDeadConnectionsTimer\n"));
00315 
00316     if (mTimer) {
00317         mTimer->Cancel();
00318         mTimer = 0;
00319     }
00320 }
00321 
00322 nsresult
00323 nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request,
00324                                          PRUint8 caps,
00325                                          PRBool useProxy)
00326 {
00327     nsresult rv;
00328 
00329     LOG(("nsHttpHandler::AddStandardRequestHeaders\n"));
00330 
00331     // Add the "User-Agent" header
00332     rv = request->SetHeader(nsHttp::User_Agent, UserAgent());
00333     if (NS_FAILED(rv)) return rv;
00334 
00335     // MIME based content negotiation lives!
00336     // Add the "Accept" header
00337     rv = request->SetHeader(nsHttp::Accept, mAccept);
00338     if (NS_FAILED(rv)) return rv;
00339 
00340     // Add the "Accept-Language" header
00341     if (!mAcceptLanguages.IsEmpty()) {
00342         // Add the "Accept-Language" header
00343         rv = request->SetHeader(nsHttp::Accept_Language, mAcceptLanguages);
00344         if (NS_FAILED(rv)) return rv;
00345     }
00346 
00347     // Add the "Accept-Encoding" header
00348     rv = request->SetHeader(nsHttp::Accept_Encoding, mAcceptEncodings);
00349     if (NS_FAILED(rv)) return rv;
00350 
00351     // Add the "Accept-Charset" header
00352     rv = request->SetHeader(nsHttp::Accept_Charset, mAcceptCharsets);
00353     if (NS_FAILED(rv)) return rv;
00354 
00355     // RFC2616 section 19.6.2 states that the "Connection: keep-alive"
00356     // and "Keep-alive" request headers should not be sent by HTTP/1.1
00357     // user-agents.  Otherwise, problems with proxy servers (especially
00358     // transparent proxies) can result.
00359     //
00360     // However, we need to send something so that we can use keepalive
00361     // with HTTP/1.0 servers/proxies. We use "Proxy-Connection:" when 
00362     // we're talking to an http proxy, and "Connection:" otherwise
00363     
00364     NS_NAMED_LITERAL_CSTRING(close, "close");
00365     NS_NAMED_LITERAL_CSTRING(keepAlive, "keep-alive");
00366 
00367     const nsACString *connectionType = &close;
00368     if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
00369         rv = request->SetHeader(nsHttp::Keep_Alive, nsPrintfCString("%u", mIdleTimeout));
00370         if (NS_FAILED(rv)) return rv;
00371         connectionType = &keepAlive;
00372     } else if (useProxy) {
00373         // Bug 92006
00374         request->SetHeader(nsHttp::Connection, close);
00375     }
00376 
00377     const nsHttpAtom &header = useProxy ? nsHttp::Proxy_Connection
00378                                         : nsHttp::Connection;
00379     return request->SetHeader(header, *connectionType);
00380 }
00381 
00382 PRBool
00383 nsHttpHandler::IsAcceptableEncoding(const char *enc)
00384 {
00385     if (!enc)
00386         return PR_FALSE;
00387 
00388     // HTTP 1.1 allows servers to send x-gzip and x-compress instead
00389     // of gzip and compress, for example.  So, we'll always strip off
00390     // an "x-" prefix before matching the encoding to one we claim
00391     // to accept.
00392     if (!PL_strncasecmp(enc, "x-", 2))
00393         enc += 2;
00394     
00395     return PL_strcasestr(mAcceptEncodings.get(), enc) != nsnull;
00396 }
00397 
00398 nsresult
00399 nsHttpHandler::GetCacheSession(nsCacheStoragePolicy storagePolicy,
00400                                nsICacheSession **result)
00401 {
00402     nsresult rv;
00403 
00404     // Skip cache if disabled in preferences
00405     if (!mUseCache)
00406         return NS_ERROR_NOT_AVAILABLE;
00407 
00408     if (!mCacheSession_ANY) {
00409         nsCOMPtr<nsICacheService> serv = do_GetService(kCacheServiceCID, &rv);
00410         if (NS_FAILED(rv)) return rv;
00411 
00412         rv = serv->CreateSession("HTTP",
00413                                  nsICache::STORE_ANYWHERE,
00414                                  nsICache::STREAM_BASED,
00415                                  getter_AddRefs(mCacheSession_ANY));
00416         if (NS_FAILED(rv)) return rv;
00417 
00418         rv = mCacheSession_ANY->SetDoomEntriesIfExpired(PR_FALSE);
00419         if (NS_FAILED(rv)) return rv;
00420 
00421         rv = serv->CreateSession("HTTP-memory-only",
00422                                  nsICache::STORE_IN_MEMORY,
00423                                  nsICache::STREAM_BASED,
00424                                  getter_AddRefs(mCacheSession_MEM));
00425         if (NS_FAILED(rv)) return rv;
00426 
00427         rv = mCacheSession_MEM->SetDoomEntriesIfExpired(PR_FALSE);
00428         if (NS_FAILED(rv)) return rv;
00429     }
00430 
00431     if (storagePolicy == nsICache::STORE_IN_MEMORY)
00432         NS_ADDREF(*result = mCacheSession_MEM);
00433     else
00434         NS_ADDREF(*result = mCacheSession_ANY);
00435 
00436     return NS_OK;
00437 }
00438 
00439 nsresult
00440 nsHttpHandler::GetCurrentEventQ(nsIEventQueue **result)
00441 {
00442     if (!mEventQueueService) {
00443         nsresult rv;
00444         mEventQueueService = do_GetService(kEventQueueServiceCID, &rv);
00445         if (NS_FAILED(rv)) return rv;
00446     }
00447     return mEventQueueService->ResolveEventQueue(NS_CURRENT_EVENTQ, result);
00448 }
00449 
00450 nsresult
00451 nsHttpHandler::GetStreamConverterService(nsIStreamConverterService **result)
00452 {
00453     if (!mStreamConvSvc) {
00454         nsresult rv;
00455         mStreamConvSvc = do_GetService(kStreamConverterServiceCID, &rv);
00456         if (NS_FAILED(rv)) return rv;
00457     }
00458     *result = mStreamConvSvc;
00459     NS_ADDREF(*result);
00460     return NS_OK;
00461 }
00462 
00463 nsICookieService *
00464 nsHttpHandler::GetCookieService()
00465 {
00466     if (!mCookieService)
00467         mCookieService = do_GetService(kCookieServiceCID);
00468     return mCookieService;
00469 }
00470 
00471 nsresult 
00472 nsHttpHandler::GetIOService(nsIIOService** result)
00473 {
00474     NS_ADDREF(*result = mIOService);
00475     return NS_OK;
00476 }
00477 
00478 
00479 void
00480 nsHttpHandler::NotifyObservers(nsIHttpChannel *chan, const char *event)
00481 {
00482     LOG(("nsHttpHandler::NotifyObservers [chan=%x event=\"%s\"]\n", chan, event));
00483     if (mObserverService)
00484         mObserverService->NotifyObservers(chan, event, nsnull);
00485 }
00486 
00487 nsresult
00488 nsHttpHandler::OnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
00489                                  PRUint32 flags)
00490 {
00491     // First, the global observer
00492     NS_ASSERTION(gIOService, "Must have an IO service at this point");
00493     nsresult rv = gIOService->OnChannelRedirect(oldChan, newChan, flags);
00494     if (NS_FAILED(rv))
00495         return rv;
00496 
00497     // Now, the per-channel observers
00498     nsCOMPtr<nsIChannelEventSink> sink;
00499     NS_QueryNotificationCallbacks(oldChan, sink);
00500     if (sink)
00501         rv = sink->OnChannelRedirect(oldChan, newChan, flags);
00502 
00503     return rv;
00504 }
00505 
00506 //-----------------------------------------------------------------------------
00507 // nsHttpHandler <private>
00508 //-----------------------------------------------------------------------------
00509 
00510 const nsAFlatCString &
00511 nsHttpHandler::UserAgent()
00512 {
00513     if (mUserAgentOverride) {
00514         LOG(("using general.useragent.override : %s\n", mUserAgentOverride.get()));
00515         return mUserAgentOverride;
00516     }
00517 
00518     if (mUserAgentIsDirty) {
00519         BuildUserAgent();
00520         mUserAgentIsDirty = PR_FALSE;
00521     }
00522 
00523     return mUserAgent;
00524 }
00525 
00526 void
00527 nsHttpHandler::BuildUserAgent()
00528 {
00529     LOG(("nsHttpHandler::BuildUserAgent\n"));
00530 
00531     NS_ASSERTION(!mAppName.IsEmpty() &&
00532                  !mAppVersion.IsEmpty() &&
00533                  !mPlatform.IsEmpty() &&
00534                  !mSecurity.IsEmpty() &&
00535                  !mOscpu.IsEmpty(),
00536                  "HTTP cannot send practical requests without this much");
00537 
00538     // preallocate to worst-case size, which should always be better
00539     // than if we didn't preallocate at all.
00540     mUserAgent.SetCapacity(mAppName.Length() + 
00541                            mAppVersion.Length() + 
00542                            mPlatform.Length() + 
00543                            mSecurity.Length() +
00544                            mOscpu.Length() +
00545                            mLanguage.Length() +
00546                            mMisc.Length() +
00547                            mProduct.Length() +
00548                            mProductSub.Length() +
00549                            mProductComment.Length() +
00550                            mVendor.Length() +
00551                            mVendorSub.Length() +
00552                            mVendorComment.Length() +
00553                            mExtraUA.Length() +
00554                            22);
00555 
00556     // Application portion
00557     mUserAgent.Assign(mAppName);
00558     mUserAgent += '/';
00559     mUserAgent += mAppVersion;
00560     mUserAgent += ' ';
00561 
00562     // Application comment
00563     mUserAgent += '(';
00564     mUserAgent += mPlatform;
00565     mUserAgent.AppendLiteral("; ");
00566     mUserAgent += mSecurity;
00567     mUserAgent.AppendLiteral("; ");
00568     mUserAgent += mOscpu;
00569     if (!mLanguage.IsEmpty()) {
00570         mUserAgent.AppendLiteral("; ");
00571         mUserAgent += mLanguage;
00572     }
00573     if (!mMisc.IsEmpty()) {
00574         mUserAgent.AppendLiteral("; ");
00575         mUserAgent += mMisc;
00576     }
00577     mUserAgent += ')';
00578 
00579     // Product portion
00580     if (!mProduct.IsEmpty()) {
00581         mUserAgent += ' ';
00582         mUserAgent += mProduct;
00583         if (!mProductSub.IsEmpty()) {
00584             mUserAgent += '/';
00585             mUserAgent += mProductSub;
00586         }
00587         if (!mProductComment.IsEmpty()) {
00588             mUserAgent.AppendLiteral(" (");
00589             mUserAgent += mProductComment;
00590             mUserAgent += ')';
00591         }
00592     }
00593 
00594     // Vendor portion
00595     if (!mVendor.IsEmpty()) {
00596         mUserAgent += ' ';
00597         mUserAgent += mVendor;
00598         if (!mVendorSub.IsEmpty()) {
00599             mUserAgent += '/';
00600             mUserAgent += mVendorSub;
00601         }
00602         if (!mVendorComment.IsEmpty()) {
00603             mUserAgent.AppendLiteral(" (");
00604             mUserAgent += mVendorComment;
00605             mUserAgent += ')';
00606         }
00607     }
00608 
00609     if (!mExtraUA.IsEmpty())
00610         mUserAgent += mExtraUA;
00611 }
00612 
00613 void
00614 nsHttpHandler::InitUserAgentComponents()
00615 {
00616 
00617       // Gather platform.
00618     mPlatform.AssignLiteral(
00619 #if defined(MOZ_WIDGET_PHOTON)
00620     "Photon"
00621 #elif defined(XP_OS2)
00622     "OS/2"
00623 #elif defined(XP_WIN)
00624     "Windows"
00625 #elif defined(XP_MACOSX)
00626     "Macintosh"
00627 #elif defined(XP_BEOS)
00628     "BeOS"
00629 #elif !defined(MOZ_X11)
00630     "?"
00631 #else
00632     "X11"
00633 #endif
00634     );
00635 
00636     // Gather OS/CPU.
00637 #if defined(XP_OS2)
00638     ULONG os2ver = 0;
00639     DosQuerySysInfo(QSV_VERSION_MINOR, QSV_VERSION_MINOR,
00640                     &os2ver, sizeof(os2ver));
00641     if (os2ver == 11)
00642         mOscpu.AssignLiteral("2.11");
00643     else if (os2ver == 30)
00644         mOscpu.AssignLiteral("Warp 3");
00645     else if (os2ver == 40)
00646         mOscpu.AssignLiteral("Warp 4");
00647     else if (os2ver == 45)
00648         mOscpu.AssignLiteral("Warp 4.5");
00649 
00650 #elif defined(WINCE)
00651     OSVERSIONINFO info = { sizeof(OSVERSIONINFO) };
00652     if (GetVersionEx(&info)) {
00653         char *buf = PR_smprintf("Windows CE %ld.%ld",
00654                                 info.dwMajorVersion,
00655                                 info.dwMinorVersion);
00656         if (buf) {
00657             mOscpu = buf;
00658             PR_smprintf_free(buf);
00659         }
00660     }
00661 #elif defined(XP_WIN)
00662     OSVERSIONINFO info = { sizeof(OSVERSIONINFO) };
00663     if (GetVersionEx(&info)) {
00664         if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) {
00665             if (info.dwMajorVersion      == 3)
00666                 mOscpu.AssignLiteral("WinNT3.51");
00667             else if (info.dwMajorVersion == 4)
00668                 mOscpu.AssignLiteral("WinNT4.0");
00669             else {
00670                 char *buf = PR_smprintf("Windows NT %ld.%ld",
00671                                         info.dwMajorVersion,
00672                                         info.dwMinorVersion);
00673                 if (buf) {
00674                     mOscpu = buf;
00675                     PR_smprintf_free(buf);
00676                 }
00677             }
00678         } else if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
00679                    info.dwMajorVersion == 4) {
00680             if (info.dwMinorVersion == 90)
00681                 mOscpu.AssignLiteral("Win 9x 4.90");  // Windows Me
00682             else if (info.dwMinorVersion > 0)
00683                 mOscpu.AssignLiteral("Win98");
00684             else
00685                 mOscpu.AssignLiteral("Win95");
00686         } else {
00687             char *buf = PR_smprintf("Windows %ld.%ld",
00688                                     info.dwMajorVersion,
00689                                     info.dwMinorVersion);
00690             if (buf) {
00691                 mOscpu = buf;
00692                 PR_smprintf_free(buf);
00693             }
00694         }
00695     }
00696 #elif defined (XP_MACOSX) && defined(__ppc__)
00697     mOscpu.AssignLiteral("PPC Mac OS X Mach-O");
00698 #elif defined (XP_MACOSX) && defined(__i386__)
00699     mOscpu.AssignLiteral("Intel Mac OS X");
00700 #elif defined (XP_UNIX) || defined (XP_BEOS)
00701     struct utsname name;
00702     
00703     int ret = uname(&name);
00704     if (ret >= 0) {
00705         nsCAutoString buf;
00706         buf =  (char*)name.sysname;
00707 
00708         if (strcmp(name.machine, "x86_64") == 0 &&
00709             sizeof(void *) == sizeof(PRInt32)) {
00710             // We're running 32-bit code on x86_64. Make this browser
00711             // look like it's running on i686 hardware, but append "
00712             // (x86_64)" to the end of the oscpu identifier to be able
00713             // to differentiate this from someone running 64-bit code
00714             // on x86_64..
00715 
00716             buf += " i686 (x86_64)";
00717         } else {
00718             buf += ' ';
00719 
00720 #ifdef AIX
00721             // AIX uname returns machine specific info in the uname.machine
00722             // field and does not return the cpu type like other platforms.
00723             // We use the AIX version and release numbers instead.
00724             buf += (char*)name.version;
00725             buf += '.';
00726             buf += (char*)name.release;
00727 #else
00728             buf += (char*)name.machine;
00729 #endif
00730         }
00731 
00732         mOscpu.Assign(buf);
00733     }
00734 #endif
00735 
00736     mUserAgentIsDirty = PR_TRUE;
00737 }
00738 
00739 static int StringCompare(const void* s1, const void* s2, void*)
00740 {
00741     return nsCRT::strcmp(*NS_STATIC_CAST(const char *const *, s1),
00742                          *NS_STATIC_CAST(const char *const *, s2));
00743 }
00744 
00745 void
00746 nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
00747 {
00748     nsresult rv = NS_OK;
00749     PRInt32 val;
00750 
00751     LOG(("nsHttpHandler::PrefsChanged [pref=%s]\n", pref));
00752 
00753 #define PREF_CHANGED(p) ((pref == nsnull) || !PL_strcmp(pref, p))
00754 #define MULTI_PREF_CHANGED(p) \
00755   ((pref == nsnull) || !PL_strncmp(pref, p, sizeof(p) - 1))
00756 
00757     //
00758     // UA components
00759     //
00760 
00761     // Gather application values.
00762     if (PREF_CHANGED(UA_PREF("appName"))) {
00763         prefs->GetCharPref(UA_PREF("appName"),
00764             getter_Copies(mAppName));
00765         if (mAppName.IsEmpty())
00766             mAppName.AssignLiteral(UA_APPNAME);
00767         mUserAgentIsDirty = PR_TRUE;
00768     }
00769     if (PREF_CHANGED(UA_PREF("appVersion"))) {
00770         prefs->GetCharPref(UA_PREF("appVersion"),
00771             getter_Copies(mAppVersion));
00772         if (mAppVersion.IsEmpty())
00773             mAppVersion.AssignLiteral(UA_APPVERSION);
00774         mUserAgentIsDirty = PR_TRUE;
00775     }
00776 
00777     // Gather vendor values.
00778     if (PREF_CHANGED(UA_PREF("vendor"))) {
00779         prefs->GetCharPref(UA_PREF("vendor"),
00780             getter_Copies(mVendor));
00781         mUserAgentIsDirty = PR_TRUE;
00782     }
00783     if (PREF_CHANGED(UA_PREF("vendorSub"))) {
00784         prefs->GetCharPref(UA_PREF("vendorSub"),
00785             getter_Copies(mVendorSub));
00786         mUserAgentIsDirty = PR_TRUE;
00787     }
00788     if (PREF_CHANGED(UA_PREF("vendorComment"))) {
00789         prefs->GetCharPref(UA_PREF("vendorComment"),
00790             getter_Copies(mVendorComment));
00791         mUserAgentIsDirty = PR_TRUE;
00792     }
00793 
00794     if (MULTI_PREF_CHANGED(UA_PREF("extra."))) {
00795         mExtraUA.Truncate();
00796 
00797         // Unfortunately, we can't do this using the pref branch.
00798         nsCOMPtr<nsIPrefService> service =
00799             do_GetService(NS_PREFSERVICE_CONTRACTID);
00800         nsCOMPtr<nsIPrefBranch> branch;
00801         service->GetBranch(UA_PREF("extra."), getter_AddRefs(branch));
00802         if (branch) {
00803             PRUint32 extraCount;
00804             char **extraItems;
00805             rv = branch->GetChildList("", &extraCount, &extraItems);
00806             if (NS_SUCCEEDED(rv) && extraItems) {
00807                 NS_QuickSort(extraItems, extraCount, sizeof(extraItems[0]),
00808                              StringCompare, nsnull);
00809                 for (char **item = extraItems,
00810                       **item_end = extraItems + extraCount;
00811                      item < item_end; ++item) {
00812                     nsXPIDLCString valStr;
00813                     branch->GetCharPref(*item, getter_Copies(valStr));
00814                     if (!valStr.IsEmpty())
00815                         mExtraUA += NS_LITERAL_CSTRING(" ") + valStr;
00816                 }
00817                 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(extraCount, extraItems);
00818             }
00819         }
00820 
00821         mUserAgentIsDirty = PR_TRUE;
00822     }
00823 
00824     // Gather product values.
00825     if (PREF_CHANGED(UA_PREF("product"))) {
00826         prefs->GetCharPref(UA_PREF_PREFIX "product",
00827             getter_Copies(mProduct));
00828         mUserAgentIsDirty = PR_TRUE;
00829     }
00830     if (PREF_CHANGED(UA_PREF("productSub"))) {
00831         prefs->GetCharPref(UA_PREF("productSub"),
00832             getter_Copies(mProductSub));
00833         mUserAgentIsDirty = PR_TRUE;
00834     }
00835     if (PREF_CHANGED(UA_PREF("productComment"))) {
00836         prefs->GetCharPref(UA_PREF("productComment"),
00837             getter_Copies(mProductComment));
00838         mUserAgentIsDirty = PR_TRUE;
00839     }
00840 
00841     // Get Security level supported
00842     if (PREF_CHANGED(UA_PREF("security"))) {
00843         prefs->GetCharPref(UA_PREF("security"), getter_Copies(mSecurity));
00844         if (!mSecurity)
00845             mSecurity.AssignLiteral(UA_APPSECURITY_FALLBACK);
00846         mUserAgentIsDirty = PR_TRUE;
00847     }
00848 
00849     // Gather locale.
00850     if (PREF_CHANGED(UA_PREF("locale"))) {
00851         nsCOMPtr<nsIPrefLocalizedString> pls;
00852         prefs->GetComplexValue(UA_PREF("locale"),
00853                                NS_GET_IID(nsIPrefLocalizedString),
00854                                getter_AddRefs(pls));
00855         if (pls) {
00856             nsXPIDLString uval;
00857             pls->ToString(getter_Copies(uval));
00858             if (uval)
00859                 CopyUTF16toUTF8(uval, mLanguage);
00860         }
00861         else {
00862             nsXPIDLCString cval;
00863             rv = prefs->GetCharPref(UA_PREF("locale"), getter_Copies(cval));
00864             if (cval)
00865                 mLanguage.Assign(cval);
00866         }
00867 
00868         mUserAgentIsDirty = PR_TRUE;
00869     }
00870 
00871     // general.useragent.override
00872     if (PREF_CHANGED(UA_PREF("override"))) {
00873         prefs->GetCharPref(UA_PREF("override"),
00874                             getter_Copies(mUserAgentOverride));
00875         mUserAgentIsDirty = PR_TRUE;
00876     }
00877 
00878     //
00879     // HTTP options
00880     //
00881 
00882     if (PREF_CHANGED(HTTP_PREF("keep-alive.timeout"))) {
00883         rv = prefs->GetIntPref(HTTP_PREF("keep-alive.timeout"), &val);
00884         if (NS_SUCCEEDED(rv))
00885             mIdleTimeout = (PRUint16) CLAMP(val, 1, 0xffff);
00886     }
00887 
00888     if (PREF_CHANGED(HTTP_PREF("request.max-attempts"))) {
00889         rv = prefs->GetIntPref(HTTP_PREF("request.max-attempts"), &val);
00890         if (NS_SUCCEEDED(rv))
00891             mMaxRequestAttempts = (PRUint16) CLAMP(val, 1, 0xffff);
00892     }
00893 
00894     if (PREF_CHANGED(HTTP_PREF("request.max-start-delay"))) {
00895         rv = prefs->GetIntPref(HTTP_PREF("request.max-start-delay"), &val);
00896         if (NS_SUCCEEDED(rv)) {
00897             mMaxRequestDelay = (PRUint16) CLAMP(val, 0, 0xffff);
00898             if (mConnMgr)
00899                 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_REQUEST_DELAY,
00900                                       mMaxRequestDelay);
00901         }
00902     }
00903 
00904     if (PREF_CHANGED(HTTP_PREF("max-connections"))) {
00905         rv = prefs->GetIntPref(HTTP_PREF("max-connections"), &val);
00906         if (NS_SUCCEEDED(rv)) {
00907             mMaxConnections = (PRUint16) CLAMP(val, 1, 0xffff);
00908             if (mConnMgr)
00909                 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS,
00910                                       mMaxConnections);
00911         }
00912     }
00913 
00914     if (PREF_CHANGED(HTTP_PREF("max-connections-per-server"))) {
00915         rv = prefs->GetIntPref(HTTP_PREF("max-connections-per-server"), &val);
00916         if (NS_SUCCEEDED(rv)) {
00917             mMaxConnectionsPerServer = (PRUint8) CLAMP(val, 1, 0xff);
00918             if (mConnMgr) {
00919                 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS_PER_HOST,
00920                                       mMaxConnectionsPerServer);
00921                 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS_PER_PROXY,
00922                                       mMaxConnectionsPerServer);
00923             }
00924         }
00925     }
00926 
00927     if (PREF_CHANGED(HTTP_PREF("max-persistent-connections-per-server"))) {
00928         rv = prefs->GetIntPref(HTTP_PREF("max-persistent-connections-per-server"), &val);
00929         if (NS_SUCCEEDED(rv)) {
00930             mMaxPersistentConnectionsPerServer = (PRUint8) CLAMP(val, 1, 0xff);
00931             if (mConnMgr)
00932                 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PERSISTENT_CONNECTIONS_PER_HOST,
00933                                       mMaxPersistentConnectionsPerServer);
00934         }
00935     }
00936 
00937     if (PREF_CHANGED(HTTP_PREF("max-persistent-connections-per-proxy"))) {
00938         rv = prefs->GetIntPref(HTTP_PREF("max-persistent-connections-per-proxy"), &val);
00939         if (NS_SUCCEEDED(rv)) {
00940             mMaxPersistentConnectionsPerProxy = (PRUint8) CLAMP(val, 1, 0xff);
00941             if (mConnMgr)
00942                 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PERSISTENT_CONNECTIONS_PER_PROXY,
00943                                       mMaxPersistentConnectionsPerProxy);
00944         }
00945     }
00946 
00947     if (PREF_CHANGED(HTTP_PREF("sendRefererHeader"))) {
00948         rv = prefs->GetIntPref(HTTP_PREF("sendRefererHeader"), &val);
00949         if (NS_SUCCEEDED(rv))
00950             mReferrerLevel = (PRUint8) CLAMP(val, 0, 0xff);
00951     }
00952 
00953     if (PREF_CHANGED(HTTP_PREF("redirection-limit"))) {
00954         rv = prefs->GetIntPref(HTTP_PREF("redirection-limit"), &val);
00955         if (NS_SUCCEEDED(rv))
00956             mRedirectionLimit = (PRUint8) CLAMP(val, 0, 0xff);
00957     }
00958 
00959     if (PREF_CHANGED(HTTP_PREF("version"))) {
00960         nsXPIDLCString httpVersion;
00961         prefs->GetCharPref(HTTP_PREF("version"), getter_Copies(httpVersion));
00962         if (httpVersion) {
00963             if (!PL_strcmp(httpVersion, "1.1"))
00964                 mHttpVersion = NS_HTTP_VERSION_1_1;
00965             else if (!PL_strcmp(httpVersion, "0.9"))
00966                 mHttpVersion = NS_HTTP_VERSION_0_9;
00967             else
00968                 mHttpVersion = NS_HTTP_VERSION_1_0;
00969         }
00970     }
00971 
00972     if (PREF_CHANGED(HTTP_PREF("proxy.version"))) {
00973         nsXPIDLCString httpVersion;
00974         prefs->GetCharPref(HTTP_PREF("proxy.version"), getter_Copies(httpVersion));
00975         if (httpVersion) {
00976             if (!PL_strcmp(httpVersion, "1.1"))
00977                 mProxyHttpVersion = NS_HTTP_VERSION_1_1;
00978             else
00979                 mProxyHttpVersion = NS_HTTP_VERSION_1_0;
00980             // it does not make sense to issue a HTTP/0.9 request to a proxy server
00981         }
00982     }
00983 
00984     PRBool cVar = PR_FALSE;
00985 
00986     if (PREF_CHANGED(HTTP_PREF("keep-alive"))) {
00987         rv = prefs->GetBoolPref(HTTP_PREF("keep-alive"), &cVar);
00988         if (NS_SUCCEEDED(rv)) {
00989             if (cVar)
00990                 mCapabilities |= NS_HTTP_ALLOW_KEEPALIVE;
00991             else
00992                 mCapabilities &= ~NS_HTTP_ALLOW_KEEPALIVE;
00993         }
00994     }
00995 
00996     if (PREF_CHANGED(HTTP_PREF("proxy.keep-alive"))) {
00997         rv = prefs->GetBoolPref(HTTP_PREF("proxy.keep-alive"), &cVar);
00998         if (NS_SUCCEEDED(rv)) {
00999             if (cVar)
01000                 mProxyCapabilities |= NS_HTTP_ALLOW_KEEPALIVE;
01001             else
01002                 mProxyCapabilities &= ~NS_HTTP_ALLOW_KEEPALIVE;
01003         }
01004     }
01005 
01006     if (PREF_CHANGED(HTTP_PREF("pipelining"))) {
01007         rv = prefs->GetBoolPref(HTTP_PREF("pipelining"), &cVar);
01008         if (NS_SUCCEEDED(rv)) {
01009             if (cVar)
01010                 mCapabilities |=  NS_HTTP_ALLOW_PIPELINING;
01011             else
01012                 mCapabilities &= ~NS_HTTP_ALLOW_PIPELINING;
01013         }
01014     }
01015 
01016     if (PREF_CHANGED(HTTP_PREF("pipelining.maxrequests"))) {
01017         rv = prefs->GetIntPref(HTTP_PREF("pipelining.maxrequests"), &val);
01018         if (NS_SUCCEEDED(rv)) {
01019             mMaxPipelinedRequests = CLAMP(val, 1, NS_HTTP_MAX_PIPELINED_REQUESTS);
01020             if (mConnMgr)
01021                 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PIPELINED_REQUESTS,
01022                                       mMaxPipelinedRequests);
01023         }
01024     }
01025 
01026     if (PREF_CHANGED(HTTP_PREF("proxy.pipelining"))) {
01027         rv = prefs->GetBoolPref(HTTP_PREF("proxy.pipelining"), &cVar);
01028         if (NS_SUCCEEDED(rv)) {
01029             if (cVar)
01030                 mProxyCapabilities |=  NS_HTTP_ALLOW_PIPELINING;
01031             else
01032                 mProxyCapabilities &= ~NS_HTTP_ALLOW_PIPELINING;
01033         }
01034     }
01035 
01036     if (PREF_CHANGED(HTTP_PREF("sendSecureXSiteReferrer"))) {
01037         rv = prefs->GetBoolPref(HTTP_PREF("sendSecureXSiteReferrer"), &cVar);
01038         if (NS_SUCCEEDED(rv))
01039             mSendSecureXSiteReferrer = cVar;
01040     }
01041 
01042     if (PREF_CHANGED(HTTP_PREF("accept.default"))) {
01043         nsXPIDLCString accept;
01044         rv = prefs->GetCharPref(HTTP_PREF("accept.default"),
01045                                   getter_Copies(accept));
01046         if (NS_SUCCEEDED(rv))
01047             SetAccept(accept);
01048     }
01049     
01050     if (PREF_CHANGED(HTTP_PREF("accept-encoding"))) {
01051         nsXPIDLCString acceptEncodings;
01052         rv = prefs->GetCharPref(HTTP_PREF("accept-encoding"),
01053                                   getter_Copies(acceptEncodings));
01054         if (NS_SUCCEEDED(rv))
01055             SetAcceptEncodings(acceptEncodings);
01056     }
01057 
01058     if (PREF_CHANGED(HTTP_PREF("use-cache"))) {
01059         rv = prefs->GetBoolPref(HTTP_PREF("use-cache"), &cVar);
01060         if (NS_SUCCEEDED(rv)) {
01061             mUseCache = cVar;
01062             if (!mUseCache) {
01063                 // release our references to the cache
01064                 mCacheSession_ANY = 0;
01065                 mCacheSession_MEM = 0;
01066             }
01067         }
01068     }
01069 
01070     if (PREF_CHANGED(HTTP_PREF("default-socket-type"))) {
01071         nsXPIDLCString sval;
01072         rv = prefs->GetCharPref(HTTP_PREF("default-socket-type"),
01073                                 getter_Copies(sval));
01074         if (NS_SUCCEEDED(rv)) {
01075             if (sval.IsEmpty())
01076                 mDefaultSocketType.Adopt(0);
01077             else {
01078                 // verify that this socket type is actually valid
01079                 nsCOMPtr<nsISocketProviderService> sps(
01080                         do_GetService(kSocketProviderServiceCID, &rv));
01081                 if (NS_SUCCEEDED(rv)) {
01082                     nsCOMPtr<nsISocketProvider> sp;
01083                     rv = sps->GetSocketProvider(sval, getter_AddRefs(sp));
01084                     if (NS_SUCCEEDED(rv)) {
01085                         // OK, this looks like a valid socket provider.
01086                         mDefaultSocketType.Assign(sval);
01087                     }
01088                 }
01089             }
01090         }
01091     }
01092 
01093     // enable Persistent caching for HTTPS - bug#205921    
01094     if (PREF_CHANGED(BROWSER_PREF("disk_cache_ssl"))) {
01095         cVar = PR_FALSE;
01096         rv = prefs->GetBoolPref(BROWSER_PREF("disk_cache_ssl"), &cVar);
01097         if (NS_SUCCEEDED(rv))
01098             mEnablePersistentHttpsCaching = cVar;
01099     }
01100 
01101     if (PREF_CHANGED(HTTP_PREF("phishy-userpass-length"))) {
01102         rv = prefs->GetIntPref(HTTP_PREF("phishy-userpass-length"), &val);
01103         if (NS_SUCCEEDED(rv))
01104             mPhishyUserPassLength = (PRUint8) CLAMP(val, 0, 0xff);
01105     }
01106 
01107     //
01108     // INTL options
01109     //
01110 
01111     if (PREF_CHANGED(INTL_ACCEPT_LANGUAGES)) {
01112         nsCOMPtr<nsIPrefLocalizedString> pls;
01113         prefs->GetComplexValue(INTL_ACCEPT_LANGUAGES,
01114                                 NS_GET_IID(nsIPrefLocalizedString),
01115                                 getter_AddRefs(pls));
01116         if (pls) {
01117             nsXPIDLString uval;
01118             pls->ToString(getter_Copies(uval));
01119             if (uval)
01120                 SetAcceptLanguages(NS_ConvertUCS2toUTF8(uval).get());
01121         } 
01122     }
01123 
01124     if (PREF_CHANGED(INTL_ACCEPT_CHARSET)) {
01125         nsCOMPtr<nsIPrefLocalizedString> pls;
01126         prefs->GetComplexValue(INTL_ACCEPT_CHARSET,
01127                                 NS_GET_IID(nsIPrefLocalizedString),
01128                                 getter_AddRefs(pls));
01129         if (pls) {
01130             nsXPIDLString uval;
01131             pls->ToString(getter_Copies(uval));
01132             if (uval)
01133                 SetAcceptCharsets(NS_ConvertUCS2toUTF8(uval).get());
01134         } 
01135     }
01136 
01137     //
01138     // IDN options
01139     //
01140 
01141     if (PREF_CHANGED(NETWORK_ENABLEIDN)) {
01142         PRBool enableIDN = PR_FALSE;
01143         prefs->GetBoolPref(NETWORK_ENABLEIDN, &enableIDN);
01144         // No locking is required here since this method runs in the main
01145         // UI thread, and so do all the methods in nsHttpChannel.cpp
01146         // (mIDNConverter is used by nsHttpChannel)
01147         if (enableIDN && !mIDNConverter) {
01148             mIDNConverter = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
01149             NS_ASSERTION(NS_SUCCEEDED(rv), "idnSDK not installed");
01150         }
01151         else if (!enableIDN && mIDNConverter)
01152             mIDNConverter = nsnull;
01153     }
01154 
01155 #undef PREF_CHANGED
01156 #undef MULTI_PREF_CHANGED
01157 }
01158 
01172 static nsresult
01173 PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLanguages)
01174 {
01175     if (!i_AcceptLanguages)
01176         return NS_OK;
01177 
01178     PRUint32 n, size, wrote;
01179     double q, dec;
01180     char *p, *p2, *token, *q_Accept, *o_Accept;
01181     const char *comma;
01182     PRInt32 available;
01183 
01184     o_Accept = nsCRT::strdup(i_AcceptLanguages);
01185     if (!o_Accept)
01186         return NS_ERROR_OUT_OF_MEMORY;
01187     for (p = o_Accept, n = size = 0; '\0' != *p; p++) {
01188         if (*p == ',') n++;
01189             size++;
01190     }
01191 
01192     available = size + ++n * 11 + 1;
01193     q_Accept = new char[available];
01194     if (!q_Accept) {
01195         nsCRT::free(o_Accept);
01196         return NS_ERROR_OUT_OF_MEMORY;
01197     }
01198     *q_Accept = '\0';
01199     q = 1.0;
01200     dec = q / (double) n;
01201     n = 0;
01202     p2 = q_Accept;
01203     for (token = nsCRT::strtok(o_Accept, ",", &p);
01204          token != (char *) 0;
01205          token = nsCRT::strtok(p, ",", &p))
01206     {
01207         token = net_FindCharNotInSet(token, HTTP_LWS);
01208         char* trim;
01209         trim = net_FindCharInSet(token, ";" HTTP_LWS);
01210         if (trim != (char*)0)  // remove "; q=..." if present
01211             *trim = '\0';
01212 
01213         if (*token != '\0') {
01214             comma = n++ != 0 ? "," : ""; // delimiter if not first item
01215             PRUint32 u = QVAL_TO_UINT(q);
01216             if (u < 10)
01217                 wrote = PR_snprintf(p2, available, "%s%s;q=0.%u", comma, token, u);
01218             else
01219                 wrote = PR_snprintf(p2, available, "%s%s", comma, token);
01220             q -= dec;
01221             p2 += wrote;
01222             available -= wrote;
01223             NS_ASSERTION(available > 0, "allocated string not long enough");
01224         }
01225     }
01226     nsCRT::free(o_Accept);
01227 
01228     o_AcceptLanguages.Assign((const char *) q_Accept);
01229     delete [] q_Accept;
01230 
01231     return NS_OK;
01232 }
01233 
01234 nsresult
01235 nsHttpHandler::SetAcceptLanguages(const char *aAcceptLanguages) 
01236 {
01237     nsCAutoString buf;
01238     nsresult rv = PrepareAcceptLanguages(aAcceptLanguages, buf);
01239     if (NS_SUCCEEDED(rv))
01240         mAcceptLanguages.Assign(buf);
01241     return rv;
01242 }
01243 
01258 static nsresult
01259 PrepareAcceptCharsets(const char *i_AcceptCharset, nsACString &o_AcceptCharset)
01260 {
01261     PRUint32 n, size, wrote, u;
01262     PRInt32 available;
01263     double q, dec;
01264     char *p, *p2, *token, *q_Accept, *o_Accept;
01265     const char *acceptable, *comma;
01266     PRBool add_utf = PR_FALSE;
01267     PRBool add_asterisk = PR_FALSE;
01268 
01269     if (!i_AcceptCharset)
01270         acceptable = "";
01271     else
01272         acceptable = i_AcceptCharset;
01273     o_Accept = nsCRT::strdup(acceptable);
01274     if (nsnull == o_Accept)
01275         return NS_ERROR_OUT_OF_MEMORY;
01276     for (p = o_Accept, n = size = 0; '\0' != *p; p++) {
01277         if (*p == ',') n++;
01278             size++;
01279     }
01280 
01281     // only add "utf-8" and "*" to the list if they aren't
01282     // already specified.
01283 
01284     if (PL_strcasestr(acceptable, "utf-8") == NULL) {
01285         n++;
01286         add_utf = PR_TRUE;
01287     }
01288     if (PL_strstr(acceptable, "*") == NULL) {
01289         n++;
01290         add_asterisk = PR_TRUE;
01291     }
01292 
01293     available = size + ++n * 11 + 1;
01294     q_Accept = new char[available];
01295     if ((char *) 0 == q_Accept)
01296         return NS_ERROR_OUT_OF_MEMORY;
01297     *q_Accept = '\0';
01298     q = 1.0;
01299     dec = q / (double) n;
01300     n = 0;
01301     p2 = q_Accept;
01302     for (token = nsCRT::strtok(o_Accept, ",", &p);
01303          token != (char *) 0;
01304          token = nsCRT::strtok(p, ",", &p)) {
01305         token = net_FindCharNotInSet(token, HTTP_LWS);
01306         char* trim;
01307         trim = net_FindCharInSet(token, ";" HTTP_LWS);
01308         if (trim != (char*)0)  // remove "; q=..." if present
01309             *trim = '\0';
01310 
01311         if (*token != '\0') {
01312             comma = n++ != 0 ? "," : ""; // delimiter if not first item
01313             u = QVAL_TO_UINT(q);
01314             if (u < 10)
01315                 wrote = PR_snprintf(p2, available, "%s%s;q=0.%u", comma, token, u);
01316             else
01317                 wrote = PR_snprintf(p2, available, "%s%s", comma, token);
01318             q -= dec;
01319             p2 += wrote;
01320             available -= wrote;
01321             NS_ASSERTION(available > 0, "allocated string not long enough");
01322         }
01323     }
01324     if (add_utf) {
01325         comma = n++ != 0 ? "," : ""; // delimiter if not first item
01326         u = QVAL_TO_UINT(q);
01327         if (u < 10)
01328             wrote = PR_snprintf(p2, available, "%sutf-8;q=0.%u", comma, u);
01329         else
01330             wrote = PR_snprintf(p2, available, "%sutf-8", comma);
01331         q -= dec;
01332         p2 += wrote;
01333         available -= wrote;
01334         NS_ASSERTION(available > 0, "allocated string not long enough");
01335     }
01336     if (add_asterisk) {
01337         comma = n++ != 0 ? "," : ""; // delimiter if not first item
01338 
01339         // keep q of "*" equal to the lowest q value
01340         // in the event of a tie between the q of "*" and a non-wildcard
01341         // the non-wildcard always receives preference.
01342 
01343         q += dec;
01344         u = QVAL_TO_UINT(q);
01345         if (u < 10)
01346             wrote = PR_snprintf(p2, available, "%s*;q=0.%u", comma, u);
01347         else
01348             wrote = PR_snprintf(p2, available, "%s*", comma);
01349         available -= wrote;
01350         p2 += wrote;
01351         NS_ASSERTION(available > 0, "allocated string not long enough");
01352     }
01353     nsCRT::free(o_Accept);
01354 
01355     // change alloc from C++ new/delete to nsCRT::strdup's way
01356     o_AcceptCharset.Assign(q_Accept);
01357 #if defined DEBUG_havill
01358     printf("Accept-Charset: %s\n", q_Accept);
01359 #endif
01360     delete [] q_Accept;
01361     return NS_OK;
01362 }
01363 
01364 nsresult
01365 nsHttpHandler::SetAcceptCharsets(const char *aAcceptCharsets) 
01366 {
01367     nsCString buf;
01368     nsresult rv = PrepareAcceptCharsets(aAcceptCharsets, buf);
01369     if (NS_SUCCEEDED(rv))
01370         mAcceptCharsets.Assign(buf);
01371     return rv;
01372 }
01373 
01374 nsresult
01375 nsHttpHandler::SetAccept(const char *aAccept) 
01376 {
01377     mAccept = aAccept;
01378     return NS_OK;
01379 }
01380 
01381 nsresult
01382 nsHttpHandler::SetAcceptEncodings(const char *aAcceptEncodings) 
01383 {
01384     mAcceptEncodings = aAcceptEncodings;
01385     return NS_OK;
01386 }
01387 
01388 //-----------------------------------------------------------------------------
01389 // nsHttpHandler::nsISupports
01390 //-----------------------------------------------------------------------------
01391 
01392 NS_IMPL_THREADSAFE_ISUPPORTS5(nsHttpHandler,
01393                               nsIHttpProtocolHandler,
01394                               nsIProxiedProtocolHandler,
01395                               nsIProtocolHandler,
01396                               nsIObserver,
01397                               nsISupportsWeakReference)
01398 
01399 //-----------------------------------------------------------------------------
01400 // nsHttpHandler::nsIProtocolHandler
01401 //-----------------------------------------------------------------------------
01402 
01403 NS_IMETHODIMP
01404 nsHttpHandler::GetScheme(nsACString &aScheme)
01405 {
01406     aScheme.AssignLiteral("http");
01407     return NS_OK;
01408 }
01409 
01410 NS_IMETHODIMP
01411 nsHttpHandler::GetDefaultPort(PRInt32 *result)
01412 {
01413     *result = NS_HTTP_DEFAULT_PORT;
01414     return NS_OK;
01415 }
01416 
01417 NS_IMETHODIMP
01418 nsHttpHandler::GetProtocolFlags(PRUint32 *result)
01419 {
01420     *result = URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP;
01421     return NS_OK;
01422 }
01423 
01424 NS_IMETHODIMP
01425 nsHttpHandler::NewURI(const nsACString &aSpec,
01426                       const char *aCharset,
01427                       nsIURI *aBaseURI,
01428                       nsIURI **aURI)
01429 {
01430     LOG(("nsHttpHandler::NewURI\n"));
01431     return ::NewURI(aSpec, aCharset, aBaseURI, NS_HTTP_DEFAULT_PORT, aURI);
01432 }
01433 
01434 NS_IMETHODIMP
01435 nsHttpHandler::NewChannel(nsIURI *uri, nsIChannel **result)
01436 {
01437     LOG(("nsHttpHandler::NewChannel\n"));
01438 
01439     NS_ENSURE_ARG_POINTER(uri);
01440     NS_ENSURE_ARG_POINTER(result);
01441 
01442     PRBool isHttp = PR_FALSE, isHttps = PR_FALSE;
01443 
01444     // Verify that we have been given a valid scheme
01445     nsresult rv = uri->SchemeIs("http", &isHttp);
01446     if (NS_FAILED(rv)) return rv;
01447     if (!isHttp) {
01448         rv = uri->SchemeIs("https", &isHttps);
01449         if (NS_FAILED(rv)) return rv;
01450         if (!isHttps) {
01451             NS_WARNING("Invalid URI scheme");
01452             return NS_ERROR_UNEXPECTED;
01453         }
01454     }
01455     
01456     return NewProxiedChannel(uri, nsnull, result);
01457 }
01458 
01459 NS_IMETHODIMP 
01460 nsHttpHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
01461 {
01462     // don't override anything.  
01463     *_retval = PR_FALSE;
01464     return NS_OK;
01465 }
01466 
01467 //-----------------------------------------------------------------------------
01468 // nsHttpHandler::nsIProxiedProtocolHandler
01469 //-----------------------------------------------------------------------------
01470 
01471 NS_IMETHODIMP
01472 nsHttpHandler::NewProxiedChannel(nsIURI *uri,
01473                                  nsIProxyInfo* givenProxyInfo,
01474                                  nsIChannel **result)
01475 {
01476     nsHttpChannel *httpChannel = nsnull;
01477 
01478     LOG(("nsHttpHandler::NewProxiedChannel [proxyInfo=%p]\n",
01479         givenProxyInfo));
01480     
01481     nsCOMPtr<nsProxyInfo> proxyInfo;
01482     if (givenProxyInfo) {
01483         proxyInfo = do_QueryInterface(givenProxyInfo);
01484         NS_ENSURE_ARG(proxyInfo);
01485     }
01486 
01487     PRBool https;
01488     nsresult rv = uri->SchemeIs("https", &https);
01489     if (NS_FAILED(rv))
01490         return rv;
01491 
01492     NS_NEWXPCOM(httpChannel, nsHttpChannel);
01493     if (!httpChannel)
01494         return NS_ERROR_OUT_OF_MEMORY;
01495     NS_ADDREF(httpChannel);
01496 
01497     // select proxy caps if using a non-transparent proxy.  SSL tunneling
01498     // should not use proxy settings.
01499     PRInt8 caps;
01500     if (proxyInfo && !nsCRT::strcmp(proxyInfo->Type(), "http") && !https)
01501         caps = mProxyCapabilities;
01502     else
01503         caps = mCapabilities;
01504 
01505     if (https) {
01506         // HACK: make sure PSM gets initialized on the main thread.
01507         nsCOMPtr<nsISocketProviderService> spserv =
01508                 do_GetService(kSocketProviderServiceCID);
01509         if (spserv) {
01510             nsCOMPtr<nsISocketProvider> provider;
01511             spserv->GetSocketProvider("ssl", getter_AddRefs(provider));
01512         }
01513     }
01514 
01515     rv = httpChannel->Init(uri, caps, proxyInfo);
01516 
01517     if (NS_FAILED(rv)) {
01518         NS_RELEASE(httpChannel);
01519         return rv;
01520     }
01521 
01522     *result = httpChannel;
01523     return NS_OK;
01524 }
01525 
01526 //-----------------------------------------------------------------------------
01527 // nsHttpHandler::nsIHttpProtocolHandler
01528 //-----------------------------------------------------------------------------
01529 
01530 NS_IMETHODIMP
01531 nsHttpHandler::GetUserAgent(nsACString &value)
01532 {
01533     value = UserAgent();
01534     return NS_OK;
01535 }
01536 
01537 NS_IMETHODIMP
01538 nsHttpHandler::GetAppName(nsACString &value)
01539 {
01540     value = mAppName;
01541     return NS_OK;
01542 }
01543 
01544 NS_IMETHODIMP
01545 nsHttpHandler::GetAppVersion(nsACString &value)
01546 {
01547     value = mAppVersion;
01548     return NS_OK;
01549 }
01550 
01551 NS_IMETHODIMP
01552 nsHttpHandler::GetVendor(nsACString &value)
01553 {
01554     value = mVendor;
01555     return NS_OK;
01556 }
01557 NS_IMETHODIMP
01558 nsHttpHandler::SetVendor(const nsACString &value)
01559 {
01560     mVendor = value;
01561     mUserAgentIsDirty = PR_TRUE;
01562     return NS_OK;
01563 }
01564 
01565 NS_IMETHODIMP
01566 nsHttpHandler::GetVendorSub(nsACString &value)
01567 {
01568     value = mVendorSub;
01569     return NS_OK;
01570 }
01571 NS_IMETHODIMP
01572 nsHttpHandler::SetVendorSub(const nsACString &value)
01573 {
01574     mVendorSub = value;
01575     mUserAgentIsDirty = PR_TRUE;
01576     return NS_OK;
01577 }
01578 
01579 NS_IMETHODIMP
01580 nsHttpHandler::GetVendorComment(nsACString &value)
01581 {
01582     value = mVendorComment;
01583     return NS_OK;
01584 }
01585 NS_IMETHODIMP
01586 nsHttpHandler::SetVendorComment(const nsACString &value)
01587 {
01588     mVendorComment = value;
01589     mUserAgentIsDirty = PR_TRUE;
01590     return NS_OK;
01591 }
01592 
01593 NS_IMETHODIMP
01594 nsHttpHandler::GetProduct(nsACString &value)
01595 {
01596     value = mProduct;
01597     return NS_OK;
01598 }
01599 NS_IMETHODIMP
01600 nsHttpHandler::SetProduct(const nsACString &value)
01601 {
01602     mProduct = value;
01603     mUserAgentIsDirty = PR_TRUE;
01604     return NS_OK;
01605 }
01606 
01607 NS_IMETHODIMP
01608 nsHttpHandler::GetProductSub(nsACString &value)
01609 {
01610     value = mProductSub;
01611     return NS_OK;
01612 }
01613 NS_IMETHODIMP
01614 nsHttpHandler::SetProductSub(const nsACString &value)
01615 {
01616     mProductSub = value;
01617     mUserAgentIsDirty = PR_TRUE;
01618     return NS_OK;
01619 }
01620 
01621 NS_IMETHODIMP
01622 nsHttpHandler::GetProductComment(nsACString &value)
01623 {
01624     value = mProductComment;
01625     return NS_OK;
01626 }
01627 NS_IMETHODIMP
01628 nsHttpHandler::SetProductComment(const nsACString &value)
01629 {
01630     mProductComment = value;
01631     mUserAgentIsDirty = PR_TRUE;
01632     return NS_OK;
01633 }
01634 
01635 NS_IMETHODIMP
01636 nsHttpHandler::GetPlatform(nsACString &value)
01637 {
01638     value = mPlatform;
01639     return NS_OK;
01640 }
01641 
01642 NS_IMETHODIMP
01643 nsHttpHandler::GetOscpu(nsACString &value)
01644 {
01645     value = mOscpu;
01646     return NS_OK;
01647 }
01648 
01649 NS_IMETHODIMP
01650 nsHttpHandler::GetLanguage(nsACString &value)
01651 {
01652     value = mLanguage;
01653     return NS_OK;
01654 }
01655 NS_IMETHODIMP
01656 nsHttpHandler::SetLanguage(const nsACString &value)
01657 {
01658     mLanguage = value;
01659     mUserAgentIsDirty = PR_TRUE;
01660     return NS_OK;
01661 }
01662 
01663 NS_IMETHODIMP
01664 nsHttpHandler::GetMisc(nsACString &value)
01665 {
01666     value = mMisc;
01667     return NS_OK;
01668 }
01669 NS_IMETHODIMP
01670 nsHttpHandler::SetMisc(const nsACString &value)
01671 {
01672     mMisc = value;
01673     mUserAgentIsDirty = PR_TRUE;
01674     return NS_OK;
01675 }
01676 
01677 //-----------------------------------------------------------------------------
01678 // nsHttpHandler::nsIObserver
01679 //-----------------------------------------------------------------------------
01680 
01681 NS_IMETHODIMP
01682 nsHttpHandler::Observe(nsISupports *subject,
01683                        const char *topic,
01684                        const PRUnichar *data)
01685 {
01686     LOG(("nsHttpHandler::Observe [topic=\"%s\"]\n", topic));
01687 
01688     if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
01689         nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(subject);
01690         if (prefBranch)
01691             PrefsChanged(prefBranch, NS_ConvertUCS2toUTF8(data).get());
01692     }
01693     else if (strcmp(topic, "profile-change-net-teardown")    == 0 ||
01694              strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)    == 0) {
01695 
01696         // kill off the "prune dead connections" timer
01697         StopPruneDeadConnectionsTimer();
01698 
01699         // clear cache of all authentication credentials.
01700         mAuthCache.ClearAll();
01701 
01702         // ensure connection manager is shutdown
01703         if (mConnMgr)
01704             mConnMgr->Shutdown();
01705 
01706         // need to reset the session start time since cache validation may
01707         // depend on this value.
01708         mSessionStartTime = NowInSeconds();
01709     }
01710     else if (strcmp(topic, "session-logout") == 0) {
01711         // clear cache of all authentication credentials.
01712         mAuthCache.ClearAll();
01713 
01714         // need to reset the session start time since cache validation may
01715         // depend on this value.
01716         mSessionStartTime = NowInSeconds();
01717     }
01718     else if (strcmp(topic, "profile-change-net-restore") == 0) {
01719         // initialize connection manager
01720         InitConnectionMgr();
01721 
01722         // restart the "prune dead connections" timer
01723         StartPruneDeadConnectionsTimer();
01724     }
01725     else if (strcmp(topic, "timer-callback") == 0) {
01726         // prune dead connections
01727 #ifdef DEBUG
01728         nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
01729         NS_ASSERTION(timer == mTimer, "unexpected timer-callback");
01730 #endif
01731         if (mConnMgr)
01732             mConnMgr->PruneDeadConnections();
01733     }
01734 
01735     return NS_OK;
01736 }
01737 
01738 //-----------------------------------------------------------------------------
01739 // nsHttpsHandler implementation
01740 //-----------------------------------------------------------------------------
01741 
01742 NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpsHandler,
01743                               nsIHttpProtocolHandler,
01744                               nsIProxiedProtocolHandler,
01745                               nsIProtocolHandler,
01746                               nsISupportsWeakReference)
01747 
01748 nsresult
01749 nsHttpsHandler::Init()
01750 {
01751     nsCOMPtr<nsIProtocolHandler> httpHandler(
01752             do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http"));
01753     NS_ASSERTION(httpHandler.get() != nsnull, "no http handler?");
01754     return NS_OK;
01755 }
01756 
01757 NS_IMETHODIMP
01758 nsHttpsHandler::GetScheme(nsACString &aScheme)
01759 {
01760     aScheme.AssignLiteral("https");
01761     return NS_OK;
01762 }
01763 
01764 NS_IMETHODIMP
01765 nsHttpsHandler::GetDefaultPort(PRInt32 *aPort)
01766 {
01767     *aPort = NS_HTTPS_DEFAULT_PORT;
01768     return NS_OK;
01769 }
01770 
01771 NS_IMETHODIMP
01772 nsHttpsHandler::GetProtocolFlags(PRUint32 *aProtocolFlags)
01773 {
01774     return gHttpHandler->GetProtocolFlags(aProtocolFlags);
01775 }
01776 
01777 NS_IMETHODIMP
01778 nsHttpsHandler::NewURI(const nsACString &aSpec,
01779                        const char *aOriginCharset,
01780                        nsIURI *aBaseURI,
01781                        nsIURI **_retval)
01782 {
01783     return ::NewURI(aSpec, aOriginCharset, aBaseURI, NS_HTTPS_DEFAULT_PORT, _retval);
01784 }
01785 
01786 NS_IMETHODIMP
01787 nsHttpsHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval)
01788 {
01789     return gHttpHandler->NewChannel(aURI, _retval);
01790 }
01791 
01792 NS_IMETHODIMP
01793 nsHttpsHandler::AllowPort(PRInt32 aPort, const char *aScheme, PRBool *_retval)
01794 {
01795     // don't override anything.  
01796     *_retval = PR_FALSE;
01797     return NS_OK;
01798 }