mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-04-15 20:41:02 +00:00
1031 lines
29 KiB
C++
1031 lines
29 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
#include "nsFileChannel.h"
|
|
#include "nscore.h"
|
|
#include "nsIEventSinkGetter.h"
|
|
#include "nsIURI.h"
|
|
#include "nsIEventQueue.h"
|
|
#include "nsIStreamListener.h"
|
|
#include "nsIIOService.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsFileProtocolHandler.h"
|
|
#include "nsIBufferInputStream.h"
|
|
#include "nsIBufferOutputStream.h"
|
|
#include "nsAutoLock.h"
|
|
#include "netCore.h"
|
|
#include "nsIFileStream.h"
|
|
#include "nsISimpleEnumerator.h"
|
|
#include "nsIURL.h"
|
|
#include "prio.h"
|
|
#include "prmem.h" // XXX can be removed when we start doing real content-type discovery
|
|
#include "nsCOMPtr.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsSpecialSystemDirectory.h"
|
|
#include "nsEscape.h"
|
|
#include "nsIMIMEService.h"
|
|
#include "prlog.h"
|
|
|
|
static NS_DEFINE_CID(kMIMEServiceCID, NS_MIMESERVICE_CID);
|
|
|
|
static NS_DEFINE_CID(kEventQueueService, NS_EVENTQUEUESERVICE_CID);
|
|
NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
|
|
|
|
#ifdef STREAM_CONVERTER_HACK
|
|
#include "nsIAllocator.h"
|
|
static NS_DEFINE_CID(kStreamConverterCID, NS_STREAM_CONVERTER_CID);
|
|
#endif
|
|
|
|
#if defined(PR_LOGGING)
|
|
//
|
|
// Log module for SocketTransport logging...
|
|
//
|
|
// To enable logging (see prlog.h for full details):
|
|
//
|
|
// set NSPR_LOG_MODULES=nsFileTransport:5
|
|
// set NSPR_LOG_FILE=nspr.log
|
|
//
|
|
// this enables PR_LOG_DEBUG level information and places all output in
|
|
// the file nspr.log
|
|
//
|
|
PRLogModuleInfo* gFileTransportLog = nsnull;
|
|
|
|
#endif /* PR_LOGGING */
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
nsFileChannel::nsFileChannel()
|
|
: mURI(nsnull), mGetter(nsnull), mListener(nsnull), mEventQueue(nsnull),
|
|
mContext(nsnull), mState(QUIESCENT),
|
|
mSuspended(PR_FALSE), mFileStream(nsnull),
|
|
mBufferInputStream(nsnull), mBufferOutputStream(nsnull),
|
|
mStatus(NS_OK), mHandler(nsnull), mSourceOffset(0),
|
|
mLoadAttributes(LOAD_NORMAL),
|
|
mReadFixedAmount(PR_FALSE), mLoadGroup(nsnull)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
#if defined(PR_LOGGING)
|
|
//
|
|
// Initialize the global PRLogModule for socket transport logging
|
|
// if necessary...
|
|
//
|
|
if (nsnull == gFileTransportLog) {
|
|
gFileTransportLog = PR_NewLogModule("nsFileTransport");
|
|
}
|
|
#endif /* PR_LOGGING */
|
|
}
|
|
|
|
nsresult
|
|
nsFileChannel::Init(nsFileProtocolHandler* handler,
|
|
const char* verb, nsIURI* uri, nsIEventSinkGetter* getter,
|
|
nsIEventQueue* queue)
|
|
{
|
|
nsresult rv;
|
|
|
|
mHandler = handler;
|
|
NS_ADDREF(mHandler);
|
|
|
|
mGetter = getter;
|
|
NS_IF_ADDREF(mGetter);
|
|
|
|
mMonitor = PR_NewMonitor();
|
|
if (mMonitor == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
if (getter) {
|
|
rv = getter->GetEventSink(verb, nsCOMTypeInfo<nsIStreamListener>::GetIID(), (nsISupports**)&mListener);
|
|
// ignore the failure -- we can live without having an event sink
|
|
}
|
|
|
|
mURI = uri;
|
|
NS_ADDREF(mURI);
|
|
|
|
// if we support the nsIURL interface then use it to get just
|
|
// the file path with no other garbage!
|
|
nsCOMPtr<nsIURL> aUrl = do_QueryInterface(mURI, &rv);
|
|
if (NS_SUCCEEDED(rv) && aUrl) // does it support the url interface?
|
|
{
|
|
nsXPIDLCString fileString;
|
|
aUrl->DirFile(getter_Copies(fileString));
|
|
// to be mac friendly you need to convert a file path to a nsFilePath before
|
|
// passing it to a nsFileSpec...
|
|
#ifdef XP_MAC
|
|
nsFilePath filePath(nsUnescape((char*)(const char*)fileString));
|
|
mSpec = filePath;
|
|
|
|
// Don't assume we actually created a good file spec
|
|
FSSpec theSpec = mSpec.GetFSSpec();
|
|
if (!theSpec.name[0])
|
|
{
|
|
NS_ERROR("failed to create a file spec");
|
|
|
|
// Since we didn't actually create the file spec
|
|
// we return an error
|
|
return NS_ERROR_MALFORMED_URI;
|
|
}
|
|
#else
|
|
nsFilePath filePath(nsUnescape((char*)(const char*)fileString));
|
|
mSpec = filePath;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// otherwise do the best we can by using the spec for the uri....
|
|
// XXX temporary, until we integrate more thoroughly with nsFileSpec
|
|
char* url;
|
|
rv = mURI->GetSpec(&url);
|
|
if (NS_FAILED(rv)) return rv;
|
|
nsFileURL fileURL(url);
|
|
nsCRT::free(url);
|
|
mSpec = fileURL;
|
|
}
|
|
|
|
mEventQueue = queue;
|
|
NS_IF_ADDREF(mEventQueue);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsFileChannel::~nsFileChannel()
|
|
{
|
|
NS_IF_RELEASE(mURI);
|
|
NS_IF_RELEASE(mGetter);
|
|
NS_IF_RELEASE(mListener);
|
|
NS_IF_RELEASE(mEventQueue);
|
|
NS_IF_RELEASE(mContext);
|
|
NS_IF_RELEASE(mHandler);
|
|
NS_ASSERTION(mFileStream == nsnull, "channel not closed");
|
|
NS_ASSERTION(mBufferInputStream == nsnull, "channel not closed");
|
|
NS_ASSERTION(mBufferOutputStream == nsnull, "channel not closed");
|
|
if (mMonitor)
|
|
PR_DestroyMonitor(mMonitor);
|
|
NS_IF_RELEASE(mLoadGroup);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
NS_ASSERTION(aInstancePtr, "no instance pointer");
|
|
if (aIID.Equals(nsCOMTypeInfo<nsIFileChannel>::GetIID()) ||
|
|
aIID.Equals(nsCOMTypeInfo<nsIChannel>::GetIID()) ||
|
|
aIID.Equals(nsCOMTypeInfo<nsISupports>::GetIID())) {
|
|
*aInstancePtr = NS_STATIC_CAST(nsIFileChannel*, this);
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
return NS_NOINTERFACE;
|
|
}
|
|
|
|
NS_IMPL_ADDREF(nsFileChannel);
|
|
NS_IMPL_RELEASE(nsFileChannel);
|
|
|
|
NS_METHOD
|
|
nsFileChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
|
|
{
|
|
nsFileChannel* fc = new nsFileChannel();
|
|
if (fc == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
NS_ADDREF(fc);
|
|
nsresult rv = fc->QueryInterface(aIID, aResult);
|
|
NS_RELEASE(fc);
|
|
return rv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// From nsIRequest
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::IsPending(PRBool *result)
|
|
{
|
|
*result = mState != QUIESCENT;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::Cancel()
|
|
{
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
nsresult rv = NS_OK;
|
|
mStatus = NS_BINDING_ABORTED;
|
|
if (mSuspended) {
|
|
Resume();
|
|
}
|
|
mState = ENDING;
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: Cancel [this=%x %s]",
|
|
this, (const char*)mSpec));
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::Suspend()
|
|
{
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
nsresult rv = NS_OK;
|
|
if (!mSuspended) {
|
|
// XXX close the stream here?
|
|
mStatus = mHandler->Suspend(this);
|
|
mSuspended = PR_TRUE;
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: Suspend [this=%x %s]",
|
|
this, (const char*)mSpec));
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::Resume()
|
|
{
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
nsresult rv = NS_OK;
|
|
if (mSuspended) {
|
|
// XXX re-open the stream and seek here?
|
|
mSuspended = PR_FALSE; // set this first before resuming!
|
|
mStatus = mHandler->Resume(this);
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: Resume [this=%x %s] status=%x",
|
|
this, (const char*)mSpec, mStatus));
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// From nsIChannel
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::GetURI(nsIURI * *aURI)
|
|
{
|
|
*aURI = mURI;
|
|
NS_ADDREF(mURI);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::OpenInputStream(PRUint32 startPosition, PRInt32 readCount,
|
|
nsIInputStream **result)
|
|
{
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
nsresult rv;
|
|
|
|
if (mState != QUIESCENT)
|
|
return NS_ERROR_IN_PROGRESS;
|
|
|
|
PRBool exists;
|
|
rv = Exists(&exists);
|
|
if (NS_FAILED(rv)) return rv;
|
|
if (!exists)
|
|
return NS_ERROR_FAILURE; // XXX probably need NS_BASE_STREAM_FILE_NOT_FOUND or something
|
|
|
|
rv = NS_NewPipe(&mBufferInputStream, &mBufferOutputStream,
|
|
NS_FILE_TRANSPORT_SEGMENT_SIZE,
|
|
NS_FILE_TRANSPORT_BUFFER_SIZE, PR_TRUE, this);
|
|
if (NS_FAILED(rv)) return rv;
|
|
#if 0
|
|
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = serv->NewSyncStreamListener(&mBufferInputStream, &mBufferOutputStream, &mListener);
|
|
if (NS_FAILED(rv)) return rv;
|
|
#endif
|
|
|
|
rv = mBufferOutputStream->SetNonBlocking(PR_TRUE);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
mState = START_READ;
|
|
mSourceOffset = startPosition;
|
|
mAmount = readCount;
|
|
mListener = nsnull;
|
|
|
|
rv = mHandler->DispatchRequest(this);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*result = mBufferInputStream;
|
|
NS_ADDREF(*result);
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: OpenInputStream [this=%x %s]",
|
|
this, (const char*)mSpec));
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::OpenOutputStream(PRUint32 startPosition, nsIOutputStream **result)
|
|
{
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
nsresult rv;
|
|
|
|
if (mState != QUIESCENT)
|
|
return NS_ERROR_IN_PROGRESS;
|
|
|
|
NS_ASSERTION(startPosition == 0, "implement startPosition");
|
|
nsISupports* str;
|
|
rv = NS_NewTypicalOutputFileStream(&str, mSpec);
|
|
if (NS_FAILED(rv)) return rv;
|
|
rv = str->QueryInterface(nsCOMTypeInfo<nsIOutputStream>::GetIID(), (void**)result);
|
|
NS_RELEASE(str);
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: OpenOutputStream [this=%x %s]",
|
|
this, (const char*)mSpec));
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::AsyncRead(PRUint32 startPosition, PRInt32 readCount,
|
|
nsISupports *ctxt,
|
|
nsIStreamListener *listener)
|
|
{
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!mEventQueue) {
|
|
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueService, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
rv = eventQService->GetThreadEventQueue(PR_CurrentThread(), &mEventQueue);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
|
|
// mscott -- this is just one temporary hack until we have a legit stream converter
|
|
// story going....if the file we are opening is an rfc822 file then we want to
|
|
// go out and convert the data into html before we try to load it. so I'm inserting
|
|
// code which if we are rfc-822 will cause us to literally insert a converter between
|
|
// the file channel stream of incoming data and the consumer at the other end of the
|
|
// AsyncRead call...
|
|
|
|
#ifdef STREAM_CONVERTER_HACK
|
|
nsXPIDLCString aContentType;
|
|
rv = GetContentType(getter_Copies(aContentType));
|
|
if (NS_SUCCEEDED(rv) && PL_strcasecmp("message/rfc822", aContentType) == 0)
|
|
{
|
|
// okay we are an rfc822 message...
|
|
// (0) Create an instance of an RFC-822 stream converter...
|
|
// because I need this converter to be around for the lifetime of the channel,
|
|
// I'm making it a member variable.
|
|
// (1) create a proxied stream listener for the caller of this method
|
|
// (2) set this proxied listener as the listener on the output stream
|
|
// (3) create a proxied stream listener for the converter
|
|
// (4) set mListener to be the stream converter's listener.
|
|
|
|
// (0) create a stream converter
|
|
nsCOMPtr<nsIStreamConverter2> mimeParser;
|
|
// mscott - we could generalize this hack to work with other stream converters by simply
|
|
// using the content type of the file to generate a progid for a stream converter and use
|
|
// that instead of a class id...
|
|
if (!mStreamConverter)
|
|
rv = nsComponentManager::CreateInstance(kStreamConverterCID,
|
|
NULL, nsCOMTypeInfo<nsIStreamConverter2>::GetIID(),
|
|
(void **) getter_AddRefs(mStreamConverter));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// (1) and (2)
|
|
nsCOMPtr<nsIStreamListener> proxiedConsumerListener;
|
|
rv = serv->NewAsyncStreamListener(listener, mEventQueue, getter_AddRefs(proxiedConsumerListener));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// (3) set the stream converter as the listener on the channel
|
|
mListener = mStreamConverter;
|
|
NS_IF_ADDREF(mListener); // mListener is NOT a com ptr...
|
|
|
|
// now set the output stream correctly
|
|
mStreamConverter->Init(mURI, proxiedConsumerListener, this);
|
|
mStreamConverter->GetContentType(getter_Copies(mStreamConverterOutType));
|
|
}
|
|
else
|
|
rv = serv->NewAsyncStreamListener(listener, mEventQueue, &mListener);
|
|
#else
|
|
rv = serv->NewAsyncStreamListener(listener, mEventQueue, &mListener);
|
|
#endif
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = NS_NewPipe(&mBufferInputStream, &mBufferOutputStream,
|
|
NS_FILE_TRANSPORT_SEGMENT_SIZE,
|
|
NS_FILE_TRANSPORT_BUFFER_SIZE, PR_TRUE, this);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = mBufferOutputStream->SetNonBlocking(PR_TRUE);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
NS_ASSERTION(mContext == nsnull, "context not released");
|
|
mContext = ctxt;
|
|
NS_IF_ADDREF(mContext);
|
|
|
|
mState = START_READ;
|
|
mSourceOffset = startPosition;
|
|
|
|
// did the user request a specific number of bytes to read?
|
|
// if they passed in -1 then they want all bytes to be read.f
|
|
if (readCount > 0) // did the user pass in
|
|
{
|
|
mReadFixedAmount = PR_TRUE;
|
|
mAmount = (PRUint32) readCount; // mscott - this is a safe cast!
|
|
}
|
|
else
|
|
mAmount = 0; // don't worry we'll ignore this parameter from here on out because mReadFixedAmount is false
|
|
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: AsyncRead [this=%x %s]",
|
|
this, (const char*)mSpec));
|
|
rv = mHandler->DispatchRequest(this);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::AsyncWrite(nsIInputStream *fromStream,
|
|
PRUint32 startPosition, PRInt32 writeCount,
|
|
nsISupports *ctxt,
|
|
nsIStreamObserver *observer)
|
|
{
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: AsyncWrite [this=%x %s]",
|
|
this, (const char*)mSpec));
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::GetLoadAttributes(PRUint32 *aLoadAttributes)
|
|
{
|
|
*aLoadAttributes = mLoadAttributes;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::SetLoadAttributes(PRUint32 aLoadAttributes)
|
|
{
|
|
mLoadAttributes = aLoadAttributes;
|
|
return NS_OK;
|
|
}
|
|
|
|
#define DUMMY_TYPE "text/html"
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::GetContentType(char * *aContentType)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
#ifdef STREAM_CONVERTER_HACK
|
|
// okay, if we already have a stream converter hooked up to the channel
|
|
// then we want to LIE about the content type...the content type is really
|
|
// the stream converter out type...
|
|
if (mStreamConverter)
|
|
{
|
|
*aContentType = (char *) nsAllocator::Clone(mStreamConverterOutType, nsCRT::strlen(mStreamConverterOutType) + 1);
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
NS_WITH_SERVICE(nsIMIMEService, MIMEService, kMIMEServiceCID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = MIMEService->GetTypeFromURI(mURI, aContentType);
|
|
if (NS_SUCCEEDED(rv)) return rv;
|
|
|
|
// if all else fails treat it as text/html?
|
|
*aContentType = nsCRT::strdup(DUMMY_TYPE);
|
|
if (!*aContentType) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
} else {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::GetLoadGroup(nsILoadGroup * *aLoadGroup)
|
|
{
|
|
*aLoadGroup = mLoadGroup;
|
|
NS_IF_ADDREF(*aLoadGroup);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::SetLoadGroup(nsILoadGroup * aLoadGroup)
|
|
{
|
|
NS_IF_RELEASE(mLoadGroup);
|
|
mLoadGroup = aLoadGroup;
|
|
NS_IF_ADDREF(mLoadGroup);
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// nsIRunnable methods:
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::Run(void)
|
|
{
|
|
while (mState != QUIESCENT && !mSuspended) {
|
|
Process();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
static NS_METHOD
|
|
nsWriteToFile(void* closure,
|
|
const char* fromRawSegment,
|
|
PRUint32 toOffset,
|
|
PRUint32 count,
|
|
PRUint32 *writeCount)
|
|
{
|
|
nsIOutputStream* outStr = (nsIOutputStream*)closure;
|
|
nsresult rv = outStr->Write(fromRawSegment, count, writeCount);
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsFileChannel::Process(void)
|
|
{
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
switch (mState) {
|
|
case START_READ: {
|
|
nsISupports* fs;
|
|
|
|
if (mListener) {
|
|
mStatus = mListener->OnStartRequest(this, mContext); // always send the start notification
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
}
|
|
|
|
mStatus = NS_NewTypicalInputFileStream(&fs, mSpec);
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
|
|
if (mSourceOffset > 0) // if we need to set a starting offset, QI for the nsIRandomAccessStore and set it
|
|
{
|
|
nsCOMPtr<nsIRandomAccessStore> inputStream;
|
|
inputStream = do_QueryInterface(fs, &mStatus);
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
// for now, assume the offset is always relative to the start of the file (position 0)
|
|
// so use PR_SEEK_SET
|
|
inputStream->Seek(PR_SEEK_SET, mSourceOffset);
|
|
}
|
|
|
|
|
|
mStatus = fs->QueryInterface(nsCOMTypeInfo<nsIInputStream>::GetIID(), (void**)&mFileStream);
|
|
NS_RELEASE(fs);
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: START_READ [this=%x %s]",
|
|
this, (const char*)mSpec));
|
|
mState = READING;
|
|
break;
|
|
}
|
|
case READING: {
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
|
|
nsIInputStream* fileStr = NS_STATIC_CAST(nsIInputStream*, mFileStream);
|
|
|
|
PRUint32 inLen;
|
|
mStatus = fileStr->GetLength(&inLen);
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
|
|
// mscott --> if the user wanted to only read a fixed number of bytes
|
|
// we need to honor that...
|
|
if (mReadFixedAmount)
|
|
inLen = inLen < mAmount ? inLen : mAmount; // take the min(inLen, mAmount)
|
|
|
|
PRUint32 amt;
|
|
mStatus = mBufferOutputStream->WriteFrom(fileStr, inLen, &amt);
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: READING [this=%x %s] amt=%d status=%x",
|
|
this, (const char*)mSpec, amt, mStatus));
|
|
if (mReadFixedAmount)
|
|
mAmount -= amt; // subtract off the amount we just read from mAmount.
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
if (mStatus == NS_BASE_STREAM_WOULD_BLOCK || amt == 0) {
|
|
// Our nsIBufferObserver will have been called from WriteFrom
|
|
// which in turn calls Suspend, so we should end up suspending
|
|
// this file channel.
|
|
Suspend();
|
|
return;
|
|
}
|
|
|
|
// and feed the buffer to the application via the buffer stream:
|
|
if (mListener) {
|
|
mStatus = mListener->OnDataAvailable(this, mContext, mBufferInputStream, mSourceOffset, amt);
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
}
|
|
|
|
if (mReadFixedAmount && mAmount == 0)
|
|
{
|
|
Cancel(); // stop reading data...we are done
|
|
return;
|
|
}
|
|
|
|
mSourceOffset += amt;
|
|
|
|
// stay in the READING state
|
|
break;
|
|
}
|
|
case START_WRITE: {
|
|
nsISupports* fs;
|
|
|
|
if (mListener) {
|
|
mStatus = mListener->OnStartRequest(this, mContext); // always send the start notification
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
}
|
|
|
|
mStatus = NS_NewTypicalOutputFileStream(&fs, mSpec);
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
|
|
mStatus = fs->QueryInterface(nsCOMTypeInfo<nsIOutputStream>::GetIID(), (void**)&mFileStream);
|
|
NS_RELEASE(fs);
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: START_WRITE [this=%x %s]",
|
|
this, (const char*)mSpec));
|
|
mState = WRITING;
|
|
break;
|
|
}
|
|
case WRITING: {
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
#if 0
|
|
PRUint32 amt;
|
|
mStatus = mBuffer->ReadSegments(nsWriteToFile, mFileStream, (PRUint32)-1, &amt);
|
|
if (mStatus == NS_BASE_STREAM_EOF) goto error;
|
|
if (NS_FAILED(mStatus)) goto error;
|
|
|
|
nsAutoCMonitor mon(mBuffer);
|
|
mon.Notify();
|
|
|
|
mSourceOffset += amt;
|
|
#endif
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: WRITING [this=%x %s]",
|
|
this, (const char*)mSpec));
|
|
// stay in the WRITING state
|
|
break;
|
|
}
|
|
case ENDING: {
|
|
PR_LOG(gFileTransportLog, PR_LOG_DEBUG,
|
|
("nsFileTransport: ENDING [this=%x %s] status=%x",
|
|
this, (const char*)mSpec, mStatus));
|
|
mBufferOutputStream->Flush();
|
|
if (mListener) {
|
|
// XXX where do we get the error message?
|
|
(void)mListener->OnStopRequest(this, mContext, mStatus, nsnull);
|
|
NS_RELEASE(mListener);
|
|
}
|
|
|
|
NS_IF_RELEASE(mBufferOutputStream);
|
|
mBufferOutputStream = nsnull;
|
|
NS_IF_RELEASE(mBufferInputStream);
|
|
mBufferInputStream = nsnull;
|
|
NS_IF_RELEASE(mFileStream);
|
|
mFileStream = nsnull;
|
|
NS_IF_RELEASE(mContext);
|
|
mContext = nsnull;
|
|
|
|
mState = QUIESCENT;
|
|
break;
|
|
}
|
|
case QUIESCENT: {
|
|
NS_NOTREACHED("trying to continue a quiescent file transfer");
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
|
|
error:
|
|
// Map EOF to NS_OK for the OnStopBinding(...) notification...
|
|
if (NS_BASE_STREAM_EOF == mStatus) {
|
|
mStatus = NS_OK;
|
|
}
|
|
mState = ENDING;
|
|
return;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// nsIBufferObserver methods:
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::OnFull(nsIBuffer* buffer)
|
|
{
|
|
return Suspend();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::OnWrite(nsIBuffer* aBuffer, PRUint32 aCount)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::OnEmpty(nsIBuffer* buffer)
|
|
{
|
|
return Resume();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// From nsIFileChannel
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::GetCreationDate(PRTime *aCreationDate)
|
|
{
|
|
// XXX no GetCreationDate in nsFileSpec yet
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::GetModDate(PRTime *aModDate)
|
|
{
|
|
nsFileSpec::TimeStamp date;
|
|
mSpec.GetModDate(date);
|
|
LL_I2L(*aModDate, date);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::GetFileSize(PRUint32 *aFileSize)
|
|
{
|
|
*aFileSize = mSpec.GetFileSize();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::GetParent(nsIFileChannel * *aParent)
|
|
{
|
|
nsresult rv;
|
|
|
|
nsFileSpec parentSpec;
|
|
mSpec.GetParent(parentSpec);
|
|
nsFileURL parentURL(parentSpec);
|
|
const char* urlStr = parentURL.GetURLString();
|
|
|
|
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
nsIChannel* channel;
|
|
rv = serv->NewChannel("load", // XXX what should this be?
|
|
urlStr, nsnull,
|
|
mGetter, &channel);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// this cast is safe because nsFileURL::GetURLString aways
|
|
// returns file: strings, and consequently we'll make nsIFileChannel
|
|
// objects from them:
|
|
*aParent = NS_STATIC_CAST(nsIFileChannel*, channel);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
class nsDirEnumerator : public nsISimpleEnumerator
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
nsDirEnumerator() : mHandler(nsnull), mDir(nsnull), mNext(nsnull) {
|
|
NS_INIT_REFCNT();
|
|
}
|
|
|
|
nsresult Init(nsFileProtocolHandler* handler, nsFileSpec& spec) {
|
|
const char* path = spec.GetNativePathCString();
|
|
mDir = PR_OpenDir(path);
|
|
if (mDir == nsnull) // not a directory?
|
|
return NS_ERROR_FAILURE;
|
|
|
|
mHandler = handler;
|
|
NS_ADDREF(mHandler);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD HasMoreElements(PRBool *result) {
|
|
nsresult rv;
|
|
if (mNext == nsnull && mDir) {
|
|
PRDirEntry* entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
|
|
if (entry == nsnull) {
|
|
// end of dir entries
|
|
|
|
PRStatus status = PR_CloseDir(mDir);
|
|
if (status != PR_SUCCESS)
|
|
return NS_ERROR_FAILURE;
|
|
mDir = nsnull;
|
|
|
|
*result = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
const char* path = entry->name;
|
|
rv = mHandler->NewChannelFromNativePath(path, &mNext);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
NS_ASSERTION(mNext, "NewChannel failed");
|
|
}
|
|
*result = mNext != nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetNext(nsISupports **result) {
|
|
nsresult rv;
|
|
PRBool hasMore;
|
|
rv = HasMoreElements(&hasMore);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*result = mNext; // might return nsnull
|
|
mNext = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
virtual ~nsDirEnumerator() {
|
|
if (mDir) {
|
|
PRStatus status = PR_CloseDir(mDir);
|
|
NS_ASSERTION(status == PR_SUCCESS, "close failed");
|
|
}
|
|
NS_IF_RELEASE(mHandler);
|
|
NS_IF_RELEASE(mNext);
|
|
}
|
|
|
|
protected:
|
|
nsFileProtocolHandler* mHandler;
|
|
PRDir* mDir;
|
|
nsIFileChannel* mNext;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(nsDirEnumerator, nsCOMTypeInfo<nsISimpleEnumerator>::GetIID());
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::GetChildren(nsISimpleEnumerator * *aChildren)
|
|
{
|
|
nsresult rv;
|
|
|
|
PRBool isDir;
|
|
rv = IsDirectory(&isDir);
|
|
if (NS_FAILED(rv)) return rv;
|
|
if (!isDir)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsDirEnumerator* dirEnum = new nsDirEnumerator();
|
|
if (dirEnum == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
NS_ADDREF(dirEnum);
|
|
rv = dirEnum->Init(mHandler, mSpec);
|
|
if (NS_FAILED(rv)) {
|
|
NS_RELEASE(dirEnum);
|
|
return rv;
|
|
}
|
|
*aChildren = dirEnum;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::GetNativePath(char * *aNativePath)
|
|
{
|
|
char* nativePath = nsCRT::strdup(mSpec.GetNativePathCString());
|
|
if (nativePath == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
*aNativePath = nativePath;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::Exists(PRBool *result)
|
|
{
|
|
*result = mSpec.Exists();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::Create()
|
|
{
|
|
// XXX no Create in nsFileSpec -- creates non-existent file
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::Delete()
|
|
{
|
|
// XXX no Delete in nsFileSpec -- deletes file or dir
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::MoveFrom(nsIURI *src)
|
|
{
|
|
#if 0
|
|
nsresult rv;
|
|
nsIFileChannel* fc;
|
|
rv = src->QueryInterface(nsCOMTypeInfo<nsIFileChannel>::GetIID(), (void**)&fc);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = fc->moveToDir(this);
|
|
NS_RELEASE(fc);
|
|
return rv;
|
|
}
|
|
else {
|
|
// Do it the hard way -- fetch the URL and store the bits locally.
|
|
// Delete the src when done.
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
#else
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
#endif
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::CopyFrom(nsIURI *src)
|
|
{
|
|
#if 0
|
|
nsresult rv;
|
|
nsIFileChannel* fc;
|
|
rv = src->QueryInterface(nsCOMTypeInfo<nsIFileChannel>::GetIID(), (void**)&fc);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = fc->copyToDir(this);
|
|
NS_RELEASE(fc);
|
|
return rv;
|
|
}
|
|
else {
|
|
// Do it the hard way -- fetch the URL and store the bits locally.
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
#else
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
#endif
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::IsDirectory(PRBool *result)
|
|
{
|
|
*result = mSpec.IsDirectory();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::IsFile(PRBool *result)
|
|
{
|
|
*result = mSpec.IsFile();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::IsLink(PRBool *_retval)
|
|
{
|
|
// XXX no IsLink in nsFileSpec (for alias/shortcut/symlink)
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::ResolveLink(nsIFileChannel **_retval)
|
|
{
|
|
// XXX no ResolveLink in nsFileSpec yet -- returns what link points to
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::MakeUniqueFileName(const char* baseName, char **_retval)
|
|
{
|
|
// XXX makeUnique needs to return the name or file spec to the newly create
|
|
// file!
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFileChannel::Execute(const char *args)
|
|
{
|
|
nsresult rv;
|
|
char* queryArgs = nsnull;
|
|
|
|
if (args == nsnull) {
|
|
nsIURL* url;
|
|
rv = mURI->QueryInterface(nsCOMTypeInfo<nsIURL>::GetIID(), (void**)&url);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = url->GetQuery(&queryArgs);
|
|
NS_RELEASE(url);
|
|
if (NS_FAILED(rv)) return rv;
|
|
args = queryArgs;
|
|
}
|
|
}
|
|
|
|
rv = mSpec.Execute(args);
|
|
if (queryArgs)
|
|
nsCRT::free(queryArgs);
|
|
return rv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|