Back to index

lightning-sunbird  0.9+nobinonly
nsFileChannel.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.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  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsFileChannel.h"
00040 #include "nsDirectoryIndexStream.h"
00041 #include "nsMimeTypes.h"
00042 #include "nsNetUtil.h"
00043 #include "nsEventQueueUtils.h"
00044 
00045 #include "nsIServiceManager.h"
00046 #include "nsIStreamConverterService.h"
00047 #include "nsIStreamTransportService.h"
00048 #include "nsITransport.h"
00049 #include "nsIFileURL.h"
00050 #include "nsIMIMEService.h"
00051 #include "nsIPrefService.h"
00052 #include "nsIPrefBranch.h"
00053 #include "nsURLHelper.h"
00054 
00055 static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID);
00056 static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
00057 
00058 //-----------------------------------------------------------------------------
00059 
00060 nsFileChannel::nsFileChannel()
00061     : mContentLength(-1)
00062     , mUploadLength(-1)
00063     , mLoadFlags(LOAD_NORMAL)
00064     , mStatus(NS_OK)
00065     , mIsDir(PR_FALSE)
00066     , mUploading(PR_FALSE)
00067 {
00068 }
00069 
00070 nsresult
00071 nsFileChannel::Init(nsIURI *uri)
00072 {
00073     nsresult rv = nsHashPropertyBag::Init();
00074     if (NS_FAILED(rv))
00075         return rv;
00076     mURL = do_QueryInterface(uri, &rv);
00077     return rv;
00078 }
00079 
00080 nsresult
00081 nsFileChannel::GetClonedFile(nsIFile **result)
00082 {
00083     nsresult rv;
00084 
00085     nsCOMPtr<nsIFile> file;
00086     rv = mURL->GetFile(getter_AddRefs(file));
00087     if (NS_FAILED(rv)) return rv;
00088 
00089     return file->Clone(result);
00090 }
00091 
00092 nsresult
00093 nsFileChannel::EnsureStream()
00094 {
00095     NS_ENSURE_TRUE(mURL, NS_ERROR_NOT_INITIALIZED);
00096 
00097     nsresult rv;
00098     nsCOMPtr<nsIFile> file;
00099 
00100     // don't assume nsIFile impl is threadsafe; pass a clone to the stream.
00101     rv = GetClonedFile(getter_AddRefs(file));
00102     if (NS_FAILED(rv)) return rv;
00103 
00104     // we accept that this might result in a disk hit to stat the file
00105     rv = file->IsDirectory(&mIsDir);
00106     if (NS_FAILED(rv)) {
00107         // canonicalize error message
00108         if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
00109             rv = NS_ERROR_FILE_NOT_FOUND;
00110         return rv;
00111     }
00112 
00113     if (mIsDir)
00114         rv = nsDirectoryIndexStream::Create(file, getter_AddRefs(mStream));
00115     else
00116         rv = NS_NewLocalFileInputStream(getter_AddRefs(mStream), file);
00117 
00118     if (NS_FAILED(rv)) return rv;
00119 
00120     // fixup content length
00121     if (mStream && (mContentLength < 0))
00122         mStream->Available((PRUint32 *) &mContentLength);
00123 
00124     return NS_OK;
00125 }
00126 
00127 //-----------------------------------------------------------------------------
00128 // nsISupports
00129 //-----------------------------------------------------------------------------
00130 
00131 NS_IMPL_ADDREF_INHERITED(nsFileChannel, nsHashPropertyBag)
00132 NS_IMPL_RELEASE_INHERITED(nsFileChannel, nsHashPropertyBag)
00133 NS_INTERFACE_MAP_BEGIN(nsFileChannel)
00134     NS_INTERFACE_MAP_ENTRY(nsIRequest)
00135     NS_INTERFACE_MAP_ENTRY(nsIChannel)
00136     NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
00137     NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
00138     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
00139     NS_INTERFACE_MAP_ENTRY(nsIFileChannel)
00140     NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
00141 NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag)
00142 
00143 //-----------------------------------------------------------------------------
00144 // nsIRequest
00145 //-----------------------------------------------------------------------------
00146 
00147 NS_IMETHODIMP
00148 nsFileChannel::GetName(nsACString &result)
00149 {
00150     return mURL->GetSpec(result);
00151 }
00152 
00153 NS_IMETHODIMP
00154 nsFileChannel::IsPending(PRBool *result)
00155 {
00156     *result = (mRequest != nsnull);
00157     return NS_OK;
00158 }
00159 
00160 NS_IMETHODIMP
00161 nsFileChannel::GetStatus(nsresult *status)
00162 {
00163     if (NS_SUCCEEDED(mStatus) && mRequest)
00164         mRequest->GetStatus(status);
00165     else
00166         *status = mStatus;
00167     return NS_OK;
00168 }
00169 
00170 NS_IMETHODIMP
00171 nsFileChannel::Cancel(nsresult status)
00172 {
00173     NS_ENSURE_TRUE(mRequest, NS_ERROR_UNEXPECTED);
00174     mStatus = status;
00175     return mRequest->Cancel(status);
00176 }
00177 
00178 NS_IMETHODIMP
00179 nsFileChannel::Suspend()
00180 {
00181     NS_ENSURE_TRUE(mRequest, NS_ERROR_UNEXPECTED);
00182     return mRequest->Suspend();
00183 }
00184 
00185 NS_IMETHODIMP
00186 nsFileChannel::Resume()
00187 {
00188     NS_ENSURE_TRUE(mRequest, NS_ERROR_UNEXPECTED);
00189     return mRequest->Resume();
00190 }
00191 
00192 NS_IMETHODIMP
00193 nsFileChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
00194 {
00195     *aLoadFlags = mLoadFlags;
00196     return NS_OK;
00197 }
00198 
00199 NS_IMETHODIMP
00200 nsFileChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
00201 {
00202     mLoadFlags = aLoadFlags;
00203     return NS_OK;
00204 }
00205 
00206 NS_IMETHODIMP
00207 nsFileChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
00208 {
00209     NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
00210     return NS_OK;
00211 }
00212 
00213 NS_IMETHODIMP
00214 nsFileChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
00215 {
00216     mLoadGroup = aLoadGroup;
00217     mProgressSink = nsnull;
00218     return NS_OK;
00219 }
00220 
00221 //-----------------------------------------------------------------------------
00222 // nsIChannel
00223 //-----------------------------------------------------------------------------
00224 
00225 NS_IMETHODIMP
00226 nsFileChannel::GetOriginalURI(nsIURI **aURI)
00227 {
00228     if (mOriginalURI)
00229         *aURI = mOriginalURI;
00230     else
00231         *aURI = mURL;
00232     NS_IF_ADDREF(*aURI);
00233     return NS_OK;
00234 }
00235 
00236 NS_IMETHODIMP
00237 nsFileChannel::SetOriginalURI(nsIURI *aURI)
00238 {
00239     mOriginalURI = aURI;
00240     return NS_OK;
00241 }
00242 
00243 NS_IMETHODIMP
00244 nsFileChannel::GetURI(nsIURI **aURI)
00245 {
00246     NS_IF_ADDREF(*aURI = mURL);
00247     return NS_OK;
00248 }
00249 
00250 NS_IMETHODIMP
00251 nsFileChannel::GetOwner(nsISupports **aOwner)
00252 {
00253     NS_IF_ADDREF(*aOwner = mOwner);
00254     return NS_OK;
00255 }
00256 
00257 NS_IMETHODIMP
00258 nsFileChannel::SetOwner(nsISupports *aOwner)
00259 {
00260     mOwner = aOwner;
00261     return NS_OK;
00262 }
00263 
00264 NS_IMETHODIMP
00265 nsFileChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
00266 {
00267     NS_IF_ADDREF(*aCallbacks = mCallbacks);
00268     return NS_OK;
00269 }
00270 
00271 NS_IMETHODIMP
00272 nsFileChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
00273 {
00274     mCallbacks = aCallbacks;
00275     mProgressSink = nsnull;
00276     return NS_OK;
00277 }
00278 
00279 NS_IMETHODIMP 
00280 nsFileChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
00281 {
00282     *aSecurityInfo = nsnull;
00283     return NS_OK;
00284 }
00285 
00286 NS_IMETHODIMP
00287 nsFileChannel::GetContentType(nsACString &aContentType)
00288 {
00289     NS_PRECONDITION(mURL, "Why is this being called?");
00290     
00291     if (mContentType.IsEmpty()) {
00292         if (mIsDir) {
00293             mContentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
00294         } else {
00295             // Get content type from file extension
00296             nsCOMPtr<nsIFile> file;
00297             nsresult rv = mURL->GetFile(getter_AddRefs(file));
00298             if (NS_FAILED(rv)) return rv;
00299             
00300             nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1", &rv);
00301             if (NS_SUCCEEDED(rv))
00302                 mime->GetTypeFromFile(file, mContentType);
00303 
00304             if (mContentType.IsEmpty())
00305                 mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
00306         }
00307     }
00308     
00309     aContentType = mContentType;
00310     return NS_OK;
00311 }
00312 
00313 NS_IMETHODIMP
00314 nsFileChannel::SetContentType(const nsACString &aContentType)
00315 {
00316     // If someone gives us a type hint we should just use that type instead of
00317     // doing our guessing.  So we don't care when this is being called.
00318 
00319     PRBool dummy;
00320     net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
00321     return NS_OK;
00322 }
00323 
00324 NS_IMETHODIMP
00325 nsFileChannel::GetContentCharset(nsACString &aContentCharset)
00326 {
00327     aContentCharset = mContentCharset;
00328     return NS_OK;
00329 }
00330 
00331 NS_IMETHODIMP
00332 nsFileChannel::SetContentCharset(const nsACString &aContentCharset)
00333 {
00334     // If someone gives us a charset hint we should just use that charset.
00335     // So we don't care when this is being called.
00336     mContentCharset = aContentCharset;
00337     return NS_OK;
00338 }
00339 
00340 NS_IMETHODIMP
00341 nsFileChannel::GetContentLength(PRInt32 *aContentLength)
00342 {
00343     *aContentLength = mContentLength;
00344     return NS_OK;
00345 }
00346 
00347 NS_IMETHODIMP
00348 nsFileChannel::SetContentLength(PRInt32 aContentLength)
00349 {
00350     // XXX does this really make any sense at all?
00351     mContentLength = aContentLength;
00352     return NS_OK;
00353 }
00354 
00355 NS_IMETHODIMP
00356 nsFileChannel::Open(nsIInputStream **result)
00357 {
00358     NS_ENSURE_TRUE(!mRequest, NS_ERROR_IN_PROGRESS);
00359     NS_ENSURE_TRUE(!mUploading, NS_ERROR_NOT_IMPLEMENTED); // XXX implement me!
00360 
00361     nsresult rv;
00362 
00363     rv = EnsureStream();
00364     if (NS_FAILED(rv)) return rv;
00365 
00366     NS_ADDREF(*result = mStream);
00367 
00368     return NS_OK;
00369 }
00370 
00371 NS_IMETHODIMP
00372 nsFileChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
00373 {
00374     NS_ENSURE_TRUE(!mRequest, NS_ERROR_IN_PROGRESS);
00375 
00376     nsCOMPtr<nsIStreamListener> grip;
00377     nsCOMPtr<nsIEventQueue> currentEventQ;
00378     nsresult rv;
00379 
00380     rv = NS_GetCurrentEventQ(getter_AddRefs(currentEventQ));
00381     if (NS_FAILED(rv)) return rv;
00382 
00383     nsCOMPtr<nsIStreamTransportService> sts =
00384             do_GetService(kStreamTransportServiceCID, &rv);
00385     if (NS_FAILED(rv)) return rv;
00386 
00387     if (mUploading) {
00388         //
00389         // open file output stream.  since output stream will be accessed on a
00390         // background thread, we should not give it a reference to "our" nsIFile
00391         // instance.
00392         //
00393         nsCOMPtr<nsIFile> file;
00394         rv = GetClonedFile(getter_AddRefs(file));
00395         if (NS_FAILED(rv)) return rv;
00396 
00397         nsCOMPtr<nsIOutputStream> fileOut;
00398         rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOut), file,
00399                                          PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
00400                                          PR_IRUSR | PR_IWUSR);
00401         if (NS_FAILED(rv)) return rv;
00402 
00403         //
00404         // create asynchronous output stream wrapping file output stream.
00405         //
00406         // XXX 64-bit upload length would be nice; it would require a new
00407         // nsIUploadChannel, though.
00408         nsCOMPtr<nsITransport> transport;
00409         rv = sts->CreateOutputTransport(fileOut, nsInt64(-1),
00410                                         nsInt64(mUploadLength), PR_TRUE,
00411                                         getter_AddRefs(transport));
00412         if (NS_FAILED(rv)) return rv;
00413 
00414         rv = transport->SetEventSink(this, currentEventQ);
00415         if (NS_FAILED(rv)) return rv;
00416 
00417         nsCOMPtr<nsIOutputStream> asyncOut;
00418         rv = transport->OpenOutputStream(0, 0, 0, getter_AddRefs(asyncOut));
00419         if (NS_FAILED(rv)) return rv;
00420 
00421         //
00422         // create async stream copier
00423         //
00424         // XXX copier might read more than mUploadLength from mStream!!  not
00425         // a huge deal, but probably should be fixed.
00426         //
00427         nsCOMPtr<nsIAsyncStreamCopier> copier;
00428         rv = NS_NewAsyncStreamCopier(getter_AddRefs(copier), mStream, asyncOut,
00429                                      nsnull,   // perform copy using default i/o thread
00430                                      PR_FALSE, // assume the upload stream is unbuffered
00431                                      PR_TRUE); // but, the async output stream is buffered!
00432         if (NS_FAILED(rv)) return rv;
00433 
00434         rv = copier->AsyncCopy(this, nsnull);
00435         if (NS_FAILED(rv)) return rv;
00436 
00437         mRequest = copier;
00438     }
00439     else {
00440         //
00441         // create file input stream
00442         //
00443         rv = EnsureStream();
00444         if (NS_FAILED(rv)) return rv;
00445 
00446         //
00447         // create asynchronous input stream wrapping file input stream.
00448         //
00449         nsCOMPtr<nsITransport> transport;
00450         rv = sts->CreateInputTransport(mStream, nsInt64(-1), nsInt64(-1), PR_TRUE,
00451                                        getter_AddRefs(transport));
00452         if (NS_FAILED(rv)) return rv;
00453 
00454         rv = transport->SetEventSink(this, currentEventQ);
00455         if (NS_FAILED(rv)) return rv;
00456 
00457         nsCOMPtr<nsIInputStream> asyncIn;
00458         rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(asyncIn));
00459         if (NS_FAILED(rv)) return rv;
00460 
00461         //
00462         // create input stream pump
00463         //
00464         nsCOMPtr<nsIInputStreamPump> pump;
00465         rv = NS_NewInputStreamPump(getter_AddRefs(pump), asyncIn);
00466         if (NS_FAILED(rv)) return rv;
00467 
00468         rv = pump->AsyncRead(this, nsnull);
00469         if (NS_FAILED(rv)) return rv;
00470 
00471         mRequest = pump;
00472     }
00473 
00474     if (mLoadGroup)
00475         mLoadGroup->AddRequest(this, nsnull);
00476 
00477     mListener = listener;
00478     mListenerContext = ctx;
00479     return NS_OK;
00480 }
00481 
00482 //-----------------------------------------------------------------------------
00483 // nsIFileChannel
00484 //-----------------------------------------------------------------------------
00485 
00486 NS_IMETHODIMP
00487 nsFileChannel::GetFile(nsIFile **file)
00488 {
00489     return mURL->GetFile(file);
00490 }
00491 
00492 //-----------------------------------------------------------------------------
00493 // nsIUploadChannel
00494 //-----------------------------------------------------------------------------
00495 
00496 NS_IMETHODIMP
00497 nsFileChannel::SetUploadStream(nsIInputStream *stream,
00498                                const nsACString &contentType,
00499                                PRInt32 contentLength)
00500 {
00501     NS_ENSURE_TRUE(!mRequest, NS_ERROR_IN_PROGRESS);
00502 
00503     mStream = stream;
00504 
00505     if (mStream) {
00506         mUploading = PR_TRUE;
00507         mUploadLength = contentLength;
00508 
00509         if (mUploadLength < 0) {
00510             // make sure we know how much data we are uploading.
00511             nsresult rv = mStream->Available((PRUint32 *) &mUploadLength);
00512             if (NS_FAILED(rv)) return rv;
00513         }
00514     }
00515     else {
00516         mUploading = PR_FALSE;
00517         mUploadLength = -1;
00518     }
00519     return NS_OK;
00520 }
00521 
00522 NS_IMETHODIMP
00523 nsFileChannel::GetUploadStream(nsIInputStream **stream)
00524 {
00525     if (mUploading)
00526         NS_IF_ADDREF(*stream = mStream);
00527     else
00528         *stream = nsnull;
00529     return NS_OK;
00530 }
00531 
00532 //-----------------------------------------------------------------------------
00533 // nsIStreamListener
00534 //-----------------------------------------------------------------------------
00535 
00536 NS_IMETHODIMP
00537 nsFileChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
00538 {
00539     return mListener->OnStartRequest(this, mListenerContext);
00540 }
00541 
00542 NS_IMETHODIMP
00543 nsFileChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
00544 {
00545     if (NS_SUCCEEDED(mStatus))
00546         mStatus = status;
00547 
00548     mListener->OnStopRequest(this, mListenerContext, mStatus);
00549     mListener = 0;
00550     mListenerContext = 0;
00551 
00552     if (mLoadGroup)
00553         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
00554 
00555     mRequest = 0;
00556     mStream = 0;
00557 
00558     // Drop notification callbacks to prevent cycles.
00559     mCallbacks = 0;
00560     mProgressSink = 0;
00561 
00562     return NS_OK;
00563 }
00564 
00565 NS_IMETHODIMP
00566 nsFileChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
00567                                nsIInputStream *stream,
00568                                PRUint32 offset, PRUint32 count)
00569 {
00570     return mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
00571 }
00572 
00573 //-----------------------------------------------------------------------------
00574 // nsITransportEventSink
00575 //-----------------------------------------------------------------------------
00576 
00577 NS_IMETHODIMP
00578 nsFileChannel::OnTransportStatus(nsITransport *trans, nsresult status,
00579                                  PRUint64 progress, PRUint64 progressMax)
00580 {
00581     if (!mProgressSink)
00582         NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
00583 
00584     // suppress status notification if channel is no longer pending!
00585     if (mProgressSink && NS_SUCCEEDED(mStatus) && mRequest && !(mLoadFlags & LOAD_BACKGROUND)) {
00586         // file channel does not send OnStatus events!
00587         if (status == nsITransport::STATUS_READING ||
00588             status == nsITransport::STATUS_WRITING) {
00589             mProgressSink->OnProgress(this, nsnull, progress, progressMax);
00590         }
00591     }
00592     return NS_OK;
00593 }
00594 
00595