Back to index

lightning-sunbird  0.9+nobinonly
nsFtpProtocolHandler.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK *****
00037  *
00038  *
00039  * This Original Code has been modified by IBM Corporation.
00040  * Modifications made by IBM described herein are
00041  * Copyright (c) International Business Machines
00042  * Corporation, 2000
00043  *
00044  * Modifications to Mozilla code or documentation
00045  * identified per MPL Section 3.3
00046  *
00047  * Date         Modified by     Description of modification
00048  * 03/27/2000   IBM Corp.       Added PR_CALLBACK for Optlink
00049  *                               use in OS2
00050  */
00051 
00052 #include "nsFtpProtocolHandler.h"
00053 #include "nsFTPChannel.h"
00054 #include "nsIURL.h"
00055 #include "nsIStandardURL.h"
00056 #include "nsCRT.h"
00057 #include "nsIComponentManager.h"
00058 #include "nsIInterfaceRequestor.h"
00059 #include "nsIInterfaceRequestorUtils.h"
00060 #include "nsIProgressEventSink.h"
00061 #include "prlog.h"
00062 #include "nsNetUtil.h"
00063 #include "nsIPrefService.h"
00064 #include "nsIPrefBranch2.h"
00065 #include "nsIObserverService.h"
00066 #include "nsEscape.h"
00067 
00068 //-----------------------------------------------------------------------------
00069 
00070 #if defined(PR_LOGGING)
00071 //
00072 // Log module for FTP Protocol logging...
00073 //
00074 // To enable logging (see prlog.h for full details):
00075 //
00076 //    set NSPR_LOG_MODULES=nsFTPProtocol:5
00077 //    set NSPR_LOG_FILE=nspr.log
00078 //
00079 // this enables PR_LOG_DEBUG level information and places all output in
00080 // the file nspr.log
00081 //
00082 PRLogModuleInfo* gFTPLog = nsnull;
00083 #endif
00084 #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
00085 
00086 //-----------------------------------------------------------------------------
00087 
00088 #define IDLE_TIMEOUT_PREF     "network.ftp.idleConnectionTimeout"
00089 #define IDLE_CONNECTION_LIMIT 8 /* XXX pref me */
00090 
00091 static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
00092 static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID);
00093 
00094 nsFtpProtocolHandler *gFtpHandler = nsnull;
00095 
00096 //-----------------------------------------------------------------------------
00097 
00098 nsFtpProtocolHandler::nsFtpProtocolHandler()
00099     : mIdleTimeout(-1)
00100 {
00101 #if defined(PR_LOGGING)
00102     if (!gFTPLog) gFTPLog = PR_NewLogModule("nsFTPProtocol");
00103 #endif
00104     LOG(("Creating nsFtpProtocolHandler @%x\n", this));
00105 
00106     gFtpHandler = this;
00107 }
00108 
00109 nsFtpProtocolHandler::~nsFtpProtocolHandler()
00110 {
00111     LOG(("Destroying nsFtpProtocolHandler @%x\n", this));
00112 
00113     NS_ASSERTION(mRootConnectionList.Count() == 0, "why wasn't Observe called?");
00114 
00115     gFtpHandler = nsnull;
00116 }
00117 
00118 NS_IMPL_THREADSAFE_ISUPPORTS4(nsFtpProtocolHandler,
00119                               nsIProtocolHandler,
00120                               nsIProxiedProtocolHandler,
00121                               nsIObserver,
00122                               nsISupportsWeakReference)
00123 
00124 nsresult
00125 nsFtpProtocolHandler::Init()
00126 {
00127     nsresult rv;
00128 
00129     if (mIdleTimeout == -1) {
00130         nsCOMPtr<nsIPrefBranch2> branch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
00131         if (NS_FAILED(rv)) return rv;
00132 
00133         rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &mIdleTimeout);
00134         if (NS_FAILED(rv))
00135             mIdleTimeout = 5*60; // 5 minute default
00136 
00137         rv = branch->AddObserver(IDLE_TIMEOUT_PREF, this, PR_TRUE);
00138         if (NS_FAILED(rv)) return rv;
00139     }
00140 
00141     nsCOMPtr<nsIObserverService> observerService =
00142         do_GetService("@mozilla.org/observer-service;1");
00143     if (observerService)
00144         observerService->AddObserver(this,
00145                                      "network:offline-about-to-go-offline",
00146                                      PR_FALSE);
00147     
00148     return NS_OK;
00149 }
00150 
00151     
00152 //-----------------------------------------------------------------------------
00153 // nsIProtocolHandler methods:
00154 
00155 NS_IMETHODIMP
00156 nsFtpProtocolHandler::GetScheme(nsACString &result)
00157 {
00158     result.AssignLiteral("ftp");
00159     return NS_OK;
00160 }
00161 
00162 NS_IMETHODIMP
00163 nsFtpProtocolHandler::GetDefaultPort(PRInt32 *result)
00164 {
00165     *result = 21; 
00166     return NS_OK;
00167 }
00168 
00169 NS_IMETHODIMP
00170 nsFtpProtocolHandler::GetProtocolFlags(PRUint32 *result)
00171 {
00172     *result = URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP; 
00173     return NS_OK;
00174 }
00175 
00176 NS_IMETHODIMP
00177 nsFtpProtocolHandler::NewURI(const nsACString &aSpec,
00178                              const char *aCharset,
00179                              nsIURI *aBaseURI,
00180                              nsIURI **result)
00181 {
00182     nsCAutoString spec(aSpec);
00183     char *fwdPtr = spec.BeginWriting();
00184 
00185     // now unescape it... %xx reduced inline to resulting character
00186 
00187     PRInt32 len = NS_UnescapeURL(fwdPtr);
00188 
00189     // NS_UnescapeURL() modified spec's buffer, truncate to ensure
00190     // spec knows its new length.
00191     spec.Truncate(len);
00192 
00193     // return an error if we find a NUL, CR, or LF in the path
00194     if (spec.FindCharInSet(CRLF) >= 0 || spec.FindChar('\0') >= 0)
00195         return NS_ERROR_MALFORMED_URI;
00196 
00197     nsresult rv;
00198     nsCOMPtr<nsIStandardURL> url = do_CreateInstance(kStandardURLCID, &rv);
00199     if (NS_FAILED(rv)) return rv;
00200 
00201     rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, 21, aSpec, aCharset, aBaseURI);
00202     if (NS_FAILED(rv)) return rv;
00203 
00204     return CallQueryInterface(url, result);
00205 }
00206 
00207 NS_IMETHODIMP
00208 nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result)
00209 {
00210     return NewProxiedChannel(url, nsnull, result);
00211 }
00212 
00213 NS_IMETHODIMP
00214 nsFtpProtocolHandler::NewProxiedChannel(nsIURI* url, nsIProxyInfo* proxyInfo, nsIChannel* *result)
00215 {
00216     NS_ENSURE_ARG_POINTER(url);
00217     nsFTPChannel *channel = new nsFTPChannel();
00218     if (!channel)
00219         return NS_ERROR_OUT_OF_MEMORY;
00220     NS_ADDREF(channel);
00221 
00222     nsCOMPtr<nsICacheService> cache = do_GetService(kCacheServiceCID);
00223     if (cache) {
00224         cache->CreateSession("FTP",
00225                              nsICache::STORE_ANYWHERE,
00226                              nsICache::STREAM_BASED,
00227                              getter_AddRefs(mCacheSession));
00228         if (mCacheSession)
00229             mCacheSession->SetDoomEntriesIfExpired(PR_FALSE);
00230     }
00231 
00232     nsresult rv = channel->Init(url, proxyInfo, mCacheSession);
00233     if (NS_FAILED(rv)) {
00234         LOG(("nsFtpProtocolHandler::NewChannel() FAILED\n"));
00235         NS_RELEASE(channel);
00236         return rv;
00237     }
00238     
00239     *result = channel;
00240     return rv;
00241 }
00242 
00243 NS_IMETHODIMP 
00244 nsFtpProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
00245 {
00246     *_retval = (port == 21 || port == 22);
00247     return NS_OK;
00248 }
00249 
00250 // connection cache methods
00251 
00252 void
00253 nsFtpProtocolHandler::Timeout(nsITimer *aTimer, void *aClosure)
00254 {
00255     LOG(("Timeout reached for %0x\n", aClosure));
00256 
00257     PRBool found = gFtpHandler->mRootConnectionList.RemoveElement(aClosure);
00258     if (!found) {
00259         NS_ERROR("timerStruct not found");
00260         return;
00261     }
00262 
00263     timerStruct* s = (timerStruct*)aClosure;
00264     delete s;
00265 }
00266 
00267 nsresult
00268 nsFtpProtocolHandler::RemoveConnection(nsIURI *aKey, nsFtpControlConnection* *_retval)
00269 {
00270     NS_ASSERTION(_retval, "null pointer");
00271     NS_ASSERTION(aKey, "null pointer");
00272     
00273     *_retval = nsnull;
00274 
00275     nsCAutoString spec;
00276     aKey->GetPrePath(spec);
00277     
00278     LOG(("Removing connection for %s\n", spec.get()));
00279    
00280     timerStruct* ts = nsnull;
00281     PRInt32 i;
00282     PRBool found = PR_FALSE;
00283     
00284     for (i=0;i<mRootConnectionList.Count();++i) {
00285         ts = (timerStruct*)mRootConnectionList[i];
00286         if (strcmp(spec.get(), ts->key) == 0) {
00287             found = PR_TRUE;
00288             mRootConnectionList.RemoveElementAt(i);
00289             break;
00290         }
00291     }
00292 
00293     if (!found)
00294         return NS_ERROR_FAILURE;
00295 
00296     // swap connection ownership
00297     *_retval = ts->conn;
00298     ts->conn = nsnull;
00299     delete ts;
00300 
00301     return NS_OK;
00302 }
00303 
00304 nsresult
00305 nsFtpProtocolHandler::InsertConnection(nsIURI *aKey, nsFtpControlConnection *aConn)
00306 {
00307     NS_ASSERTION(aConn, "null pointer");
00308     NS_ASSERTION(aKey, "null pointer");
00309     
00310     nsCAutoString spec;
00311     aKey->GetPrePath(spec);
00312 
00313     LOG(("Inserting connection for %s\n", spec.get()));
00314 
00315     nsresult rv;
00316     nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
00317     if (NS_FAILED(rv)) return rv;
00318     
00319     timerStruct* ts = new timerStruct();
00320     if (!ts)
00321         return NS_ERROR_OUT_OF_MEMORY;
00322 
00323     rv = timer->InitWithFuncCallback(nsFtpProtocolHandler::Timeout,
00324                                      ts,
00325                                      mIdleTimeout*1000,
00326                                      nsITimer::TYPE_REPEATING_SLACK);
00327     if (NS_FAILED(rv)) {
00328         delete ts;
00329         return rv;
00330     }
00331     
00332     ts->key = ToNewCString(spec);
00333     if (!ts->key) {
00334         delete ts;
00335         return NS_ERROR_OUT_OF_MEMORY;
00336     }
00337 
00338     NS_ADDREF(aConn);
00339     ts->conn = aConn;
00340     ts->timer = timer;
00341 
00342     //
00343     // limit number of idle connections.  if limit is reached, then prune
00344     // eldest connection with matching key.  if none matching, then prune
00345     // eldest connection.
00346     //
00347     if (mRootConnectionList.Count() == IDLE_CONNECTION_LIMIT) {
00348         PRInt32 i;
00349         for (i=0;i<mRootConnectionList.Count();++i) {
00350             timerStruct *candidate = (timerStruct *) mRootConnectionList[i];
00351             if (strcmp(candidate->key, ts->key) == 0) {
00352                 mRootConnectionList.RemoveElementAt(i);
00353                 delete candidate;
00354                 break;
00355             }
00356         }
00357         if (mRootConnectionList.Count() == IDLE_CONNECTION_LIMIT) {
00358             timerStruct *eldest = (timerStruct *) mRootConnectionList[0];
00359             mRootConnectionList.RemoveElementAt(0);
00360             delete eldest;
00361         }
00362     }
00363 
00364     mRootConnectionList.AppendElement(ts);
00365     return NS_OK;
00366 }
00367 
00368 //-----------------------------------------------------------------------------
00369 // nsIObserver
00370 
00371 NS_IMETHODIMP
00372 nsFtpProtocolHandler::Observe(nsISupports *aSubject,
00373                               const char *aTopic,
00374                               const PRUnichar *aData)
00375 {
00376     LOG(("nsFtpProtocolHandler::Observe [topic=%s]\n", aTopic));
00377 
00378     if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
00379         nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(aSubject);
00380         if (!branch) {
00381             NS_ERROR("no prefbranch");
00382             return NS_ERROR_UNEXPECTED;
00383         }
00384         PRInt32 timeout;
00385         nsresult rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &timeout);
00386         if (NS_SUCCEEDED(rv))
00387             mIdleTimeout = timeout;
00388     }
00389     else if (!strcmp(aTopic, "network:offline-about-to-go-offline")) {
00390         PRInt32 i;
00391         for (i=0;i<mRootConnectionList.Count();++i)
00392             delete (timerStruct*)mRootConnectionList[i];
00393         mRootConnectionList.Clear();
00394     }
00395     else {
00396         NS_NOTREACHED("unexpected topic");
00397     }
00398 
00399     return NS_OK;
00400 }