diff --git a/mozilla/netwerk/base/public/nsNetUtil.h b/mozilla/netwerk/base/public/nsNetUtil.h index d1defd28e76..4ebe7e51608 100644 --- a/mozilla/netwerk/base/public/nsNetUtil.h +++ b/mozilla/netwerk/base/public/nsNetUtil.h @@ -1011,17 +1011,4 @@ NS_NewNotificationCallbacksAggregation(nsIInterfaceRequestor *callbacks, return NS_NewInterfaceRequestorAggregation(callbacks, cbs, result); } -/** - * Helper function for testing online/offline state of the browser. - */ -inline PRBool -NS_IsOffline() -{ - PRBool offline = PR_TRUE; - nsCOMPtr ios = do_GetIOService(); - if (ios) - ios->GetOffline(&offline); - return offline; -} - #endif // !nsNetUtil_h__ diff --git a/mozilla/netwerk/base/src/nsBaseChannel.cpp b/mozilla/netwerk/base/src/nsBaseChannel.cpp index 91458ed2392..c045db1d8d3 100644 --- a/mozilla/netwerk/base/src/nsBaseChannel.cpp +++ b/mozilla/netwerk/base/src/nsBaseChannel.cpp @@ -219,31 +219,6 @@ nsBaseChannel::PushStreamConverter(const char *fromType, return rv; } -nsresult -nsBaseChannel::BeginPumpingData() -{ - nsCOMPtr stream; - nsresult rv = OpenContentStream(PR_TRUE, getter_AddRefs(stream)); - if (NS_FAILED(rv)) - return rv; - - // By assigning mPump, we flag this channel as pending (see IsPending). It's - // important that the pending flag is set when we call into the stream (the - // call to AsyncRead results in the stream's AsyncWait method being called) - // and especially when we call into the loadgroup. Our caller takes care to - // release mPump if we return an error. - - mPump = new nsInputStreamPump(); - if (!mPump) - return NS_ERROR_OUT_OF_MEMORY; - - rv = mPump->Init(stream, -1, -1, 0, 0, PR_TRUE); - if (NS_SUCCEEDED(rv)) - rv = mPump->AsyncRead(this, nsnull); - - return rv; -} - //----------------------------------------------------------------------------- // nsBaseChannel::nsISupports @@ -475,25 +450,28 @@ nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) if (NS_FAILED(rv)) return rv; - // Store the listener and context early so that OpenContentStream and the - // stream's AsyncWait method (called by AsyncRead) can have access to them - // via PushStreamConverter and the StreamListener methods. However, since - // this typically introduces a reference cycle between this and the listener, - // we need to be sure to break the reference if this method does not succeed. - mListener = listener; - mListenerContext = ctxt; + nsCOMPtr stream; + rv = OpenContentStream(PR_TRUE, getter_AddRefs(stream)); + if (NS_FAILED(rv)) + return rv; - // This method assigns mPump as a side-effect. We need to clear mPump if - // this method fails. - rv = BeginPumpingData(); + mPump = new nsInputStreamPump(); + if (!mPump) + return NS_ERROR_OUT_OF_MEMORY; + rv = mPump->Init(stream, -1, -1, 0, 0, PR_TRUE); if (NS_FAILED(rv)) { mPump = nsnull; - mListener = nsnull; - mListenerContext = nsnull; return rv; } - // At this point, we are going to return success no matter what. + rv = mPump->AsyncRead(this, nsnull); + if (NS_FAILED(rv)) { + mPump = nsnull; + return rv; + } + + mListener = listener; + mListenerContext = ctxt; SUSPEND_PUMP_FOR_SCOPE(); @@ -575,8 +553,6 @@ nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) mPump->PeekStream(CallTypeSniffers, NS_STATIC_CAST(nsIChannel*, this)); } - SUSPEND_PUMP_FOR_SCOPE(); - return mListener->OnStartRequest(this, mListenerContext); } @@ -617,8 +593,6 @@ nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *stream, PRUint32 offset, PRUint32 count) { - SUSPEND_PUMP_FOR_SCOPE(); - nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count); if (mSynthProgressEvents && NS_SUCCEEDED(rv)) { diff --git a/mozilla/netwerk/base/src/nsBaseChannel.h b/mozilla/netwerk/base/src/nsBaseChannel.h index 4814a7547e6..4dfdcb01a61 100644 --- a/mozilla/netwerk/base/src/nsBaseChannel.h +++ b/mozilla/netwerk/base/src/nsBaseChannel.h @@ -85,13 +85,11 @@ public: return nsHashPropertyBag::Init(); } -protected: // ----------------------------------------------- // Methods to be implemented by the derived class: virtual ~nsBaseChannel() {} -private: // Implemented by subclass to supply data stream. The parameter, async, is // true when called from nsIChannel::AsyncOpen and false otherwise. When // async is true, the resulting stream will be used with a nsIInputStreamPump @@ -117,11 +115,6 @@ private: return PR_FALSE; } - // Called when the callbacks available to this channel may have changed. - virtual void OnCallbacksChanged() { - } - -public: // ---------------------------------------------- // Methods provided for use by the derived class: @@ -199,15 +192,6 @@ public: mSynthProgressEvents = enable; } - // Some subclasses may wish to manually insert a stream listener between this - // and the channel's listener. The following methods make that possible. - void SetStreamListener(nsIStreamListener *listener) { - mListener = listener; - } - nsIStreamListener *StreamListener() { - return mListener; - } - // Pushes a new stream converter in front of the channel's stream listener. // The fromType and toType values are passed to nsIStreamConverterService's // AsyncConvertData method. If invalidatesContentLength is true, then the @@ -223,14 +207,10 @@ private: NS_DECL_NSISTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER - // Called to setup mPump and call AsyncRead on it. - nsresult BeginPumpingData(); - // Called when the callbacks available to this channel may have changed. void CallbacksChanged() { mProgressSink = nsnull; mQueriedProgressSink = PR_FALSE; - OnCallbacksChanged(); } nsRefPtr mPump; diff --git a/mozilla/netwerk/base/src/nsBaseContentStream.cpp b/mozilla/netwerk/base/src/nsBaseContentStream.cpp index 900c26bf0d5..76e854946f2 100644 --- a/mozilla/netwerk/base/src/nsBaseContentStream.cpp +++ b/mozilla/netwerk/base/src/nsBaseContentStream.cpp @@ -107,10 +107,6 @@ nsBaseContentStream::ReadSegments(nsWriteSegmentFun fun, void *closure, if (mStatus == NS_BASE_STREAM_CLOSED) return NS_OK; - // No data yet - if (!IsClosed() && IsNonBlocking()) - return NS_BASE_STREAM_WOULD_BLOCK; - return mStatus; } @@ -156,15 +152,9 @@ nsBaseContentStream::AsyncWait(nsIInputStreamCallback *callback, PRUint32 flags, mCallback = callback; mCallbackTarget = target; - if (!mCallback) - return NS_OK; - // If we're already closed, then dispatch this callback immediately. - if (IsClosed()) { + if (IsClosed()) DispatchCallback(); - return NS_OK; - } - OnCallbackPending(); return NS_OK; } diff --git a/mozilla/netwerk/base/src/nsBaseContentStream.h b/mozilla/netwerk/base/src/nsBaseContentStream.h index 1660a7e9f35..4837d0f4e7c 100644 --- a/mozilla/netwerk/base/src/nsBaseContentStream.h +++ b/mozilla/netwerk/base/src/nsBaseContentStream.h @@ -96,14 +96,6 @@ public: // Helper function to make code more self-documenting. void DispatchCallbackSync() { DispatchCallback(PR_FALSE); } -protected: - virtual ~nsBaseContentStream() {} - -private: - // Called from the base stream's AsyncWait method when a pending callback - // is installed on the stream. - virtual void OnCallbackPending() {} - private: nsCOMPtr mCallback; nsCOMPtr mCallbackTarget; diff --git a/mozilla/netwerk/base/src/nsInputStreamPump.cpp b/mozilla/netwerk/base/src/nsInputStreamPump.cpp index a918f0bdbf2..c018ecc1d53 100644 --- a/mozilla/netwerk/base/src/nsInputStreamPump.cpp +++ b/mozilla/netwerk/base/src/nsInputStreamPump.cpp @@ -57,17 +57,6 @@ static PRLogModuleInfo *gStreamPumpLog = nsnull; #endif #define LOG(args) PR_LOG(gStreamPumpLog, PR_LOG_DEBUG, args) -//----------------------------------------------------------------------------- - -static NS_METHOD -GetBytesAvailable(nsIInputStream *stream, void *closure, const char *segment, - PRUint32 offset, PRUint32 count, PRUint32 *result) -{ - PRUint32 *avail = NS_STATIC_CAST(PRUint32 *, closure); - *avail = count; - return NS_ERROR_ABORT; // return from ReadSegments call -} - //----------------------------------------------------------------------------- // nsInputStreamPump methods //----------------------------------------------------------------------------- @@ -362,11 +351,11 @@ nsInputStreamPump::OnInputStreamReady(nsIAsyncInputStream *stream) // this function has been called from a PLEvent, so we can safely call // any listener or progress sink methods directly from here. - mWaiting = PR_FALSE; - for (;;) { - if (mSuspendCount || mState == STATE_IDLE) + if (mSuspendCount || mState == STATE_IDLE) { + mWaiting = PR_FALSE; break; + } PRUint32 nextState; switch (mState) { @@ -385,6 +374,7 @@ nsInputStreamPump::OnInputStreamReady(nsIAsyncInputStream *stream) NS_ASSERTION(mState == STATE_TRANSFER, "unexpected state"); NS_ASSERTION(NS_SUCCEEDED(mStatus), "unexpected status"); + mWaiting = PR_FALSE; mStatus = EnsureWaiting(); if (NS_SUCCEEDED(mStatus)) break; @@ -520,20 +510,13 @@ nsInputStreamPump::OnStateTransfer() if (NS_SUCCEEDED(mStatus)) { if (NS_FAILED(rv)) mStatus = rv; - else { - // Check to see if stream will have more data. If so, then stay in - // the STATE_TRANSFER state. Otherwise, advance to STATE_STOP. - // - // NOTE: Calling Available is insufficient since that method could - // return 0 bytes available and NS_OK when it is at end-of- - // file but not closed. We would not be able to distinguish - // that case from a stream that is merely waiting for more - // data to become available. By using ReadSegments we can - // tell if the stream will ever have more data. - // - PRUint32 n; - rv = mAsyncStream->ReadSegments(GetBytesAvailable, &avail, 1, &n); - if (rv == NS_BASE_STREAM_WOULD_BLOCK || (NS_SUCCEEDED(rv) && avail)) + else if (avail) { + // if stream is now closed, advance to STATE_STOP right away. + // Available may return 0 bytes available at the moment; that + // would not mean that we are done. + // XXX async streams should have a GetStatus method! + rv = mAsyncStream->Available(&avail); + if (NS_SUCCEEDED(rv)) return STATE_TRANSFER; } } diff --git a/mozilla/netwerk/base/src/nsSocketTransport2.cpp b/mozilla/netwerk/base/src/nsSocketTransport2.cpp index 555c3782502..3ed29f4d1d0 100644 --- a/mozilla/netwerk/base/src/nsSocketTransport2.cpp +++ b/mozilla/netwerk/base/src/nsSocketTransport2.cpp @@ -417,7 +417,7 @@ nsSocketInputStream::AsyncWait(nsIInputStreamCallback *callback, { nsAutoLock lock(mTransport->mLock); - if (callback && target) { + if (target) { // // build event proxy // @@ -648,7 +648,7 @@ nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback *callback, { nsAutoLock lock(mTransport->mLock); - if (callback && target) { + if (target) { // // build event proxy // diff --git a/mozilla/netwerk/protocol/ftp/public/nsIFTPChannel.idl b/mozilla/netwerk/protocol/ftp/public/nsIFTPChannel.idl index b3fc851781d..6f34274da90 100644 --- a/mozilla/netwerk/protocol/ftp/public/nsIFTPChannel.idl +++ b/mozilla/netwerk/protocol/ftp/public/nsIFTPChannel.idl @@ -35,26 +35,20 @@ * * ***** END LICENSE BLOCK ***** */ -#include "nsISupports.idl" +#include "nsIChannel.idl" +interface nsIRequestObserver; -/** - * This interface may be used to determine if a channel is a FTP channel. - */ -[scriptable, uuid(2315d831-8b40-446a-9138-fe09ebb1b720)] -interface nsIFTPChannel : nsISupports +// this interface provides a way for the FTP connection to +// synchronize with the owning channel. + +[scriptable, uuid(3476df52-1dd2-11b2-b928-925d89b33bc0)] +interface nsIFTPChannel : nsIChannel { }; -/** - * This interface may be defined as a notification callback on the FTP - * channel. It allows a consumer to receive a log of the FTP control - * connection conversation. - */ [scriptable, uuid(455d4234-0330-43d2-bbfb-99afbecbfeb0)] interface nsIFTPEventSink : nsISupports { - /** - * XXX document this method! (see bug 328915) - */ void OnFTPControlLog(in boolean server, in string msg); }; + diff --git a/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp b/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp index 470cbc7a6d6..938b065a5c6 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp @@ -36,9 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ -#include "nsFTPChannel.h" -#include "nsFtpConnectionThread.h" // defines nsFtpState +#include "nsFTPChannel.h" #include "nsIStreamListener.h" #include "nsIServiceManager.h" #include "nsNetUtil.h" @@ -59,7 +58,12 @@ extern PRLogModuleInfo* gFTPLog; static inline PRUint32 PRTimeToSeconds(PRTime t_usec) { - return PRUint32(t_usec / PR_USEC_PER_SEC); + PRTime usec_per_sec; + PRUint32 t_sec; + LL_I2L(usec_per_sec, PR_USEC_PER_SEC); + LL_DIV(t_usec, t_usec, usec_per_sec); + LL_L2I(t_sec, t_usec); + return t_sec; } #define NowInSeconds() PRTimeToSeconds(PR_Now()) @@ -74,53 +78,209 @@ PRTimeToSeconds(PRTime t_usec) // initiated by the server (PORT command) or by the client (PASV command). // Client initiation is the most common case and is attempted first. -//----------------------------------------------------------------------------- +nsFTPChannel::nsFTPChannel() + : mIsPending(0), + mLoadFlags(LOAD_NORMAL), + mSourceOffset(0), + mAmount(0), + mContentLength(-1), + mFTPState(nsnull), + mStatus(NS_OK), + mCanceled(PR_FALSE), + mStartPos(LL_MaxUint()) +{ +} -NS_IMPL_ISUPPORTS_INHERITED3(nsFtpChannel, - nsBaseChannel, - nsIUploadChannel, - nsIResumableChannel, - nsIFTPChannel) +nsFTPChannel::~nsFTPChannel() +{ +#if defined(PR_LOGGING) + nsCAutoString spec; + mURL->GetAsciiSpec(spec); + PR_LOG(gFTPLog, PR_LOG_ALWAYS, ("~nsFTPChannel() for %s", spec.get())); +#endif + NS_IF_RELEASE(mFTPState); +} -//----------------------------------------------------------------------------- +NS_IMPL_ADDREF_INHERITED(nsFTPChannel, nsHashPropertyBag) +NS_IMPL_RELEASE_INHERITED(nsFTPChannel, nsHashPropertyBag) + +NS_INTERFACE_MAP_BEGIN(nsFTPChannel) + NS_INTERFACE_MAP_ENTRY(nsIChannel) + NS_INTERFACE_MAP_ENTRY(nsIFTPChannel) + NS_INTERFACE_MAP_ENTRY(nsIResumableChannel) + NS_INTERFACE_MAP_ENTRY(nsIUploadChannel) + NS_INTERFACE_MAP_ENTRY(nsIRequest) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) + NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink) + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) + NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) + NS_INTERFACE_MAP_ENTRY(nsICacheListener) +NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag) + +nsresult +nsFTPChannel::Init(nsIURI* uri, nsIProxyInfo* proxyInfo, nsICacheSession* session) +{ + nsresult rv = nsHashPropertyBag::Init(); + if (NS_FAILED(rv)) + return rv; + + // setup channel state + mURL = uri; + mProxyInfo = proxyInfo; + + mIOService = do_GetIOService(&rv); + if (NS_FAILED(rv)) return rv; + + mCacheSession = session; + + return NS_OK; +} + + +//////////////////////////////////////////////////////////////////////////////// +// nsIRequest methods: + +// The FTP channel doesn't maintain any connection state. Nor does it +// interpret the protocol. The FTP connection thread is responsible for +// these things and thus, the FTP channel simply calls through to the +// FTP connection thread using an xpcom proxy object to make the +// cross thread call. NS_IMETHODIMP -nsFtpChannel::SetUploadStream(nsIInputStream *stream, - const nsACString &contentType, - PRInt32 contentLength) +nsFTPChannel::GetName(nsACString &result) { - NS_ENSURE_TRUE(!IsPending(), NS_ERROR_IN_PROGRESS); + return mURL->GetSpec(result); +} - mUploadStream = stream; - - // NOTE: contentLength is intentionally ignored here. - +NS_IMETHODIMP +nsFTPChannel::IsPending(PRBool *result) { + *result = mIsPending; return NS_OK; } NS_IMETHODIMP -nsFtpChannel::GetUploadStream(nsIInputStream **stream) +nsFTPChannel::GetStatus(nsresult *status) { - NS_ENSURE_ARG_POINTER(stream); - *stream = mUploadStream; - NS_IF_ADDREF(*stream); + *status = mStatus; return NS_OK; } -//----------------------------------------------------------------------------- +NS_IMETHODIMP +nsFTPChannel::Cancel(nsresult status) { + + PR_LOG(gFTPLog, + PR_LOG_DEBUG, + ("nsFTPChannel::Cancel() called [this=%x, status=%x, mCanceled=%d]\n", + this, status, mCanceled)); + + NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code"); + + if (mCanceled) + return NS_OK; + + mCanceled = PR_TRUE; + + mStatus = status; + + if (mFTPState) + (void)mFTPState->Cancel(status); + + return NS_OK; +} NS_IMETHODIMP -nsFtpChannel::ResumeAt(PRUint64 aStartPos, const nsACString& aEntityID) +nsFTPChannel::Suspend(void) { + + PR_LOG(gFTPLog, PR_LOG_DEBUG, + ("nsFTPChannel::Suspend() called [this=%x]\n", this)); + + if (mFTPState) { + return mFTPState->Suspend(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::Resume(void) { + + PR_LOG(gFTPLog, PR_LOG_DEBUG, + ("nsFTPChannel::Resume() called [this=%x]\n", this)); + + if (mFTPState) { + return mFTPState->Resume(); + } + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIChannel methods: + +NS_IMETHODIMP +nsFTPChannel::GetOriginalURI(nsIURI* *aURL) +{ + *aURL = mOriginalURI ? mOriginalURI : mURL; + NS_ADDREF(*aURL); + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::SetOriginalURI(nsIURI* aURL) +{ + mOriginalURI = aURL; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::GetURI(nsIURI* *aURL) +{ + *aURL = mURL; + NS_ADDREF(*aURL); + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::Open(nsIInputStream **result) +{ + return NS_ImplementChannelOpen(this, result); +} + +nsresult +nsFTPChannel::GenerateCacheKey(nsACString &cacheKey) +{ + cacheKey.SetLength(0); + + nsCAutoString spec; + mURL->GetAsciiSpec(spec); + + // Strip any trailing #ref from the URL before using it as the key + const char *p = strchr(spec.get(), '#'); + if (p) + cacheKey.Append(Substring(spec, 0, p - spec.get())); + else + cacheKey.Append(spec); + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) +{ + nsresult rv = AsyncOpenAt(listener, ctxt, mStartPos, mEntityID); + // mEntityID no longer needed, clear it to avoid returning a wrong entity + // id when someone asks us + mEntityID.Truncate(); + return rv; +} + +NS_IMETHODIMP +nsFTPChannel::ResumeAt(PRUint64 aStartPos, const nsACString& aEntityID) { - NS_ENSURE_TRUE(!IsPending(), NS_ERROR_IN_PROGRESS); mEntityID = aEntityID; mStartPos = aStartPos; - mResumeRequested = (mStartPos || !mEntityID.IsEmpty()); return NS_OK; } NS_IMETHODIMP -nsFtpChannel::GetEntityID(nsACString& entityID) +nsFTPChannel::GetEntityID(nsACString& entityID) { if (mEntityID.IsEmpty()) return NS_ERROR_NOT_RESUMABLE; @@ -129,48 +289,70 @@ nsFtpChannel::GetEntityID(nsACString& entityID) return NS_OK; } -//----------------------------------------------------------------------------- - nsresult -nsFtpChannel::OpenContentStream(PRBool async, nsIInputStream **result) +nsFTPChannel::AsyncOpenAt(nsIStreamListener *listener, nsISupports *ctxt, + PRUint64 startPos, const nsACString& entityID) { - if (!async) - return NS_ERROR_NOT_IMPLEMENTED; - - nsFtpState *state = new nsFtpState(); - if (!state) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(state); - - nsresult rv = state->Init(this); - if (NS_FAILED(rv)) { - NS_RELEASE(state); + PRInt32 port; + nsresult rv = mURL->GetPort(&port); + if (NS_FAILED(rv)) return rv; + + rv = NS_CheckPortSafety(port, "ftp", mIOService); + if (NS_FAILED(rv)) + return rv; + + PR_LOG(gFTPLog, PR_LOG_DEBUG, ("nsFTPChannel::AsyncOpen() called\n")); + + mListener = listener; + mUserContext = ctxt; + + // Add this request to the load group + if (mLoadGroup) + mLoadGroup->AddRequest(this, nsnull); + PRBool offline; + + // If we're starting from the beginning, then its OK to use the cache, + // because the entire file must be there (the cache doesn't support + // partial entries yet) + // Note that ftp doesn't store metadata, so disable caching if there was + // an entityID. Storing this metadata isn't worth it until we can + // get partial data out of the cache anyway... + if (mCacheSession && !mUploadStream && entityID.IsEmpty() && + (startPos==0 || startPos==PRUint32(-1))) { + mIOService->GetOffline(&offline); + + // Set the desired cache access mode accordingly... + nsCacheAccessMode accessRequested; + if (offline) { + // Since we are offline, we can only read from the cache. + accessRequested = nsICache::ACCESS_READ; + } + else if (mLoadFlags & LOAD_BYPASS_CACHE) + accessRequested = nsICache::ACCESS_WRITE; // replace cache entry + else + accessRequested = nsICache::ACCESS_READ_WRITE; // normal browsing + + nsCAutoString cacheKey; + GenerateCacheKey(cacheKey); + + rv = mCacheSession->AsyncOpenCacheEntry(cacheKey, + accessRequested, + this); + if (NS_SUCCEEDED(rv)) + return rv; + + // If we failed to use the cache, try without + PR_LOG(gFTPLog, PR_LOG_DEBUG, + ("Opening cache entry failed [rv=%x]", rv)); } - - *result = state; - return NS_OK; -} - -PRBool -nsFtpChannel::GetStatusArg(nsresult status, nsString &statusArg) -{ - nsCAutoString host; - URI()->GetHost(host); - CopyUTF8toUTF16(host, statusArg); - return PR_TRUE; + + return SetupState(startPos, entityID); + // XXX this function must not fail since we have already called AddRequest! } void -nsFtpChannel::OnCallbacksChanged() -{ - mFTPEventSink = nsnull; -} - -//----------------------------------------------------------------------------- - -void -nsFtpChannel::GetFTPEventSink(nsCOMPtr &aResult) +nsFTPChannel::GetFTPEventSink(nsCOMPtr &aResult) { if (!mFTPEventSink) { nsCOMPtr ftpSink; @@ -184,3 +366,369 @@ nsFtpChannel::GetFTPEventSink(nsCOMPtr &aResult) } aResult = mFTPEventSink; } + +void +nsFTPChannel::InitProgressSink() +{ + // Build a proxy for the progress event sink since we may need to call it + // while we are deep inside some of our state logic, and we wouldn't want + // to worry about some weird re-entrancy scenario. + nsCOMPtr progressSink; + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, progressSink); + if (progressSink) + NS_GetProxyForObject(NS_CURRENT_EVENTQ, + NS_GET_IID(nsIProgressEventSink), + progressSink, + PROXY_ASYNC | PROXY_ALWAYS, + getter_AddRefs(mProgressSink)); +} + +nsresult +nsFTPChannel::SetupState(PRUint64 startPos, const nsACString& entityID) +{ + if (!mFTPState) { + NS_NEWXPCOM(mFTPState, nsFtpState); + if (!mFTPState) return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mFTPState); + } + nsresult rv = mFTPState->Init(this, + mCacheEntry, + mProxyInfo, + startPos, + entityID); + if (NS_FAILED(rv)) return rv; + + (void) mFTPState->SetWriteStream(mUploadStream); + + rv = mFTPState->Connect(); + if (NS_FAILED(rv)) return rv; + + mIsPending = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::GetLoadFlags(PRUint32 *aLoadFlags) +{ + *aLoadFlags = mLoadFlags; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::SetLoadFlags(PRUint32 aLoadFlags) +{ + mLoadFlags = aLoadFlags; + return NS_OK; +} + +// FTP does not provide a file typing mechanism. We fallback to file +// extension mapping. + +NS_IMETHODIMP +nsFTPChannel::GetContentType(nsACString &aContentType) +{ + if (mContentType.IsEmpty()) { + aContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); + } else { + aContentType = mContentType; + } + + PR_LOG(gFTPLog, PR_LOG_DEBUG, ("nsFTPChannel::GetContentType() returned %s\n", PromiseFlatCString(aContentType).get())); + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::SetContentType(const nsACString &aContentType) +{ + PRBool dummy; + net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy); + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::GetContentCharset(nsACString &aContentCharset) +{ + aContentCharset = mContentCharset; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::SetContentCharset(const nsACString &aContentCharset) +{ + mContentCharset = aContentCharset; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::GetContentLength(PRInt32 *aContentLength) +{ + *aContentLength = mContentLength; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::SetContentLength(PRInt32 aContentLength) +{ + mContentLength = aContentLength; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup) +{ + *aLoadGroup = mLoadGroup; + NS_IF_ADDREF(*aLoadGroup); + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) +{ + mLoadGroup = aLoadGroup; + mProgressSink = nsnull; + mFTPEventSink = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::GetOwner(nsISupports* *aOwner) +{ + *aOwner = mOwner.get(); + NS_IF_ADDREF(*aOwner); + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::SetOwner(nsISupports* aOwner) +{ + mOwner = aOwner; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks) +{ + NS_IF_ADDREF(*aNotificationCallbacks = mCallbacks); + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks) +{ + mCallbacks = aNotificationCallbacks; + mProgressSink = nsnull; + mFTPEventSink = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) +{ + *aSecurityInfo = nsnull; + return NS_OK; +} + +// nsIInterfaceRequestor method +NS_IMETHODIMP +nsFTPChannel::GetInterface(const nsIID &aIID, void **aResult) +{ + // capture the progress event sink stuff. pass the rest through. + if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) { + *aResult = NS_STATIC_CAST(nsIProgressEventSink*, this); + NS_ADDREF(this); + return NS_OK; + } + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, aIID, aResult); + return aResult ? NS_OK : NS_ERROR_NO_INTERFACE; +} + +// nsIProgressEventSink methods +NS_IMETHODIMP +nsFTPChannel::OnStatus(nsIRequest *request, nsISupports *aContext, + nsresult aStatus, const PRUnichar* aStatusArg) +{ + if (!mProgressSink) + InitProgressSink(); + + if (aStatus == NS_NET_STATUS_CONNECTED_TO) + { + // The state machine needs to know that the data connection + // was successfully started so that it can issue data commands + // securely. + if (mFTPState) + mFTPState->DataConnectionEstablished(); + else + NS_ERROR("ftp state is null."); + } + + if (!mProgressSink || (mLoadFlags & LOAD_BACKGROUND) || !mIsPending || NS_FAILED(mStatus)) + return NS_OK; + + nsCAutoString host; + mURL->GetHost(host); + return mProgressSink->OnStatus(this, mUserContext, aStatus, + NS_ConvertUTF8toUTF16(host).get()); +} + +NS_IMETHODIMP +nsFTPChannel::OnProgress(nsIRequest *request, nsISupports* aContext, + PRUint64 aProgress, PRUint64 aProgressMax) +{ + if (!mProgressSink) + InitProgressSink(); + + if (!mProgressSink || (mLoadFlags & LOAD_BACKGROUND) || !mIsPending) + return NS_OK; + + return mProgressSink->OnProgress(this, mUserContext, + aProgress, aProgressMax); +} + + +// nsIRequestObserver methods. +NS_IMETHODIMP +nsFTPChannel::OnStopRequest(nsIRequest *request, nsISupports* aContext, + nsresult aStatus) +{ + PR_LOG(gFTPLog, + PR_LOG_DEBUG, + ("nsFTPChannel::OnStopRequest() called [this=%x, request=%x, aStatus=%x]\n", + this, request, aStatus)); + + nsresult rv = NS_OK; + + if (NS_SUCCEEDED(mStatus)) + mStatus = aStatus; + + if (mListener) { + (void) mListener->OnStopRequest(this, mUserContext, mStatus); + } + if (mLoadGroup) { + (void) mLoadGroup->RemoveRequest(this, nsnull, mStatus); + } + + if (mCacheEntry) { + if (NS_SUCCEEDED(mStatus)) { + (void) mCacheEntry->SetExpirationTime( NowInSeconds() + 900 ); // valid for 15 minutes. + (void) mCacheEntry->MarkValid(); + } + else { + (void) mCacheEntry->Doom(); + } + mCacheEntry->Close(); + mCacheEntry = 0; + } + + if (mUploadStream) + mUploadStream->Close(); + + if (mFTPState) { + mFTPState->DataConnectionComplete(); + NS_RELEASE(mFTPState); + } + mIsPending = PR_FALSE; + + // Drop notification callbacks to prevent cycles. + mCallbacks = nsnull; + mProgressSink = nsnull; + mFTPEventSink = nsnull; + + return rv; +} + +NS_IMETHODIMP +nsFTPChannel::OnStartRequest(nsIRequest *request, nsISupports *aContext) +{ + PR_LOG(gFTPLog, + PR_LOG_DEBUG, + ("nsFTPChannel::OnStartRequest() called [this=%x, request=%x]\n", + this, request)); + + if (NS_SUCCEEDED(mStatus)) + request->GetStatus(&mStatus); + + nsCOMPtr resumable = do_QueryInterface(request); + if (resumable) { + resumable->GetEntityID(mEntityID); + } + + nsresult rv = NS_OK; + if (mListener) { + if (mContentType.IsEmpty()) { + // Time to sniff! + nsCOMPtr serv = + do_GetService("@mozilla.org/streamConverters;1", &rv); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr converter; + rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE, + "*/*", + mListener, + mUserContext, + getter_AddRefs(converter)); + if (NS_SUCCEEDED(rv)) { + mListener = converter; + } + } + } + + rv = mListener->OnStartRequest(this, mUserContext); + if (NS_FAILED(rv)) return rv; + } + return rv; +} + + +// nsIStreamListener method +NS_IMETHODIMP +nsFTPChannel::OnDataAvailable(nsIRequest *request, nsISupports* aContext, + nsIInputStream *aInputStream, PRUint32 aSourceOffset, + PRUint32 aLength) { + return mListener->OnDataAvailable(this, mUserContext, aInputStream, aSourceOffset, aLength); +} + +//----------------------------------------------------------------------------- +// nsFTPChannel::nsICacheListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsFTPChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, + nsCacheAccessMode access, + nsresult status) +{ + nsresult rv; + + if (mCanceled) { + NS_ASSERTION(NS_FAILED(mStatus), "Must be canceled with a failure status code"); + OnStartRequest(NS_STATIC_CAST(nsIRequest*, this), nsnull); + OnStopRequest(NS_STATIC_CAST(nsIRequest*, this), nsnull, mStatus); + return mStatus; + } + + if (NS_SUCCEEDED(status)) { + mCacheEntry = entry; + } + + rv = SetupState(PRUint32(-1), EmptyCString()); + + if (NS_FAILED(rv)) { + Cancel(rv); + } + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::SetUploadStream(nsIInputStream *stream, const nsACString &contentType, PRInt32 contentLength) +{ + mUploadStream = stream; + return NS_OK; +} + +NS_IMETHODIMP +nsFTPChannel::GetUploadStream(nsIInputStream **stream) +{ + NS_ENSURE_ARG_POINTER(stream); + *stream = mUploadStream; + NS_IF_ADDREF(*stream); + return NS_OK; +} + diff --git a/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.h b/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.h index 1b39e6ed2bd..9d419ad4002 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.h +++ b/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.h @@ -1,5 +1,4 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* vim:set ts=4 sw=4 sts=4 et cindent: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -41,8 +40,6 @@ #ifndef nsFTPChannel_h___ #define nsFTPChannel_h___ -#include "nsBaseChannel.h" - #include "nsIIOService.h" #include "nsIURI.h" #include "nsString.h" @@ -75,63 +72,94 @@ #define FTP_DATA_CHANNEL_SEG_SIZE (4*1024) #define FTP_DATA_CHANNEL_SEG_COUNT 8 -class nsFtpChannel : public nsBaseChannel, +#define FTP_CACHE_CONTROL_CONNECTION 1 + +class nsFTPChannel : public nsHashPropertyBag, public nsIFTPChannel, public nsIUploadChannel, + public nsIInterfaceRequestor, + public nsIProgressEventSink, + public nsIStreamListener, + public nsICacheListener, public nsIResumableChannel { public: NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIREQUEST + NS_DECL_NSICHANNEL NS_DECL_NSIUPLOADCHANNEL + NS_DECL_NSIFTPCHANNEL + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIPROGRESSEVENTSINK + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSICACHELISTENER NS_DECL_NSIRESUMABLECHANNEL - nsFtpChannel(nsIURI *uri, nsIProxyInfo *pi) - : mProxyInfo(pi) - , mStartPos(0) - , mResumeRequested(PR_FALSE) - { - SetURI(uri); - } + // nsFTPChannel methods: + nsFTPChannel(); + virtual ~nsFTPChannel(); + + // initializes the channel. + nsresult Init(nsIURI* uri, + nsIProxyInfo* proxyInfo, + nsICacheSession* session); - nsIProxyInfo *ProxyInfo() { - return mProxyInfo; - } + nsresult SetupState(PRUint64 startPos, const nsACString& entityID); + nsresult GenerateCacheKey(nsACString &cacheKey); + + nsresult AsyncOpenAt(nsIStreamListener *listener, nsISupports *ctxt, + PRUint64 startPos, const nsACString& entityID); - // Were we asked to resume a download? - PRBool ResumeRequested() { return mResumeRequested; } - - // Download from this byte offset - PRUint64 StartPos() { return mStartPos; } - - // ID of the entity to resume downloading - const nsCString &EntityID() { - return mEntityID; - } - void SetEntityID(const nsCSubstring &entityID) { - mEntityID = entityID; - } - - // Data stream to upload - nsIInputStream *UploadStream() { - return mUploadStream; + // Helper function to simplify getting notification callbacks. + template + void GetCallback(nsCOMPtr &aResult) { + GetInterface(NS_GET_TEMPLATE_IID(T), getter_AddRefs(aResult)); } // Helper function for getting the nsIFTPEventSink. void GetFTPEventSink(nsCOMPtr &aResult); protected: - virtual ~nsFtpChannel() {} - virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result); - virtual PRBool GetStatusArg(nsresult status, nsString &statusArg); - virtual void OnCallbacksChanged(); + void InitProgressSink(); -private: - nsCOMPtr mProxyInfo; - nsCOMPtr mFTPEventSink; - nsCOMPtr mUploadStream; - PRUint64 mStartPos; - nsCString mEntityID; - PRPackedBool mResumeRequested; +protected: + nsCOMPtr mOriginalURI; + nsCOMPtr mURL; + + nsCOMPtr mUploadStream; + + // various callback interfaces + nsCOMPtr mProgressSink; + nsCOMPtr mFTPEventSink; + nsCOMPtr mCallbacks; + + PRBool mIsPending; + PRUint32 mLoadFlags; + + PRUint32 mSourceOffset; + PRInt32 mAmount; + nsCOMPtr mLoadGroup; + nsCString mContentType; + nsCString mContentCharset; + PRInt32 mContentLength; + nsCOMPtr mOwner; + + nsCOMPtr mListener; + + nsFtpState* mFTPState; + + nsCOMPtr mUserContext; + nsresult mStatus; + PRPackedBool mCanceled; + + nsCOMPtr mIOService; + + nsCOMPtr mCacheSession; + nsCOMPtr mCacheEntry; + nsCOMPtr mProxyInfo; + nsCString mEntityID; + PRUint64 mStartPos; }; #endif /* nsFTPChannel_h___ */ diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp b/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp index a89d0e865f1..ed12635bfd5 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp @@ -22,7 +22,6 @@ * * Contributor(s): * Bradley Baetz - * Darin Fisher * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -38,66 +37,402 @@ * * ***** END LICENSE BLOCK ***** */ -#include -#include - -#include "prprf.h" -#include "prlog.h" -#include "prtime.h" -#include "prnetdb.h" - #include "nsFTPChannel.h" #include "nsFtpConnectionThread.h" #include "nsFtpControlConnection.h" #include "nsFtpProtocolHandler.h" -#include "ftpCore.h" -#include "netCore.h" -#include "nsCRT.h" -#include "nsEscape.h" -#include "nsMimeTypes.h" -#include "nsNetUtil.h" -#include "nsEventQueueUtils.h" -#include "nsStreamUtils.h" -#include "nsIURL.h" + +#include +#include + #include "nsISocketTransport.h" +#include "nsIStreamConverterService.h" #include "nsIStreamListenerTee.h" +#include "nsReadableUtils.h" +#include "prprf.h" +#include "prlog.h" +#include "prtime.h" +#include "prnetdb.h" +#include "netCore.h" +#include "ftpCore.h" +#include "nsProxiedService.h" +#include "nsCRT.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIURL.h" +#include "nsEscape.h" +#include "nsNetUtil.h" +#include "nsIDNSService.h" // for host error code +#include "nsCPasswordManager.h" +#include "nsIMemory.h" +#include "nsIStringStream.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" +#include "nsMimeTypes.h" #include "nsIStringBundle.h" -#include "nsCPasswordManager.h" +#include "nsEventQueueUtils.h" +#include "nsChannelProperties.h" + +#include "nsICacheEntryDescriptor.h" +#include "nsICacheListener.h" + +#include "nsIResumableChannel.h" + +static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID); +static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); +static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); + #if defined(PR_LOGGING) extern PRLogModuleInfo* gFTPLog; -#endif #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args) #define LOG_ALWAYS(args) PR_LOG(gFTPLog, PR_LOG_ALWAYS, args) +#else +#define LOG(args) +#define LOG_ALWAYS(args) +#endif -NS_IMPL_ISUPPORTS_INHERITED4(nsFtpState, - nsBaseContentStream, - nsIInputStreamCallback, - nsITransportEventSink, - nsICacheListener, - nsIRequestObserver) + +/** + * This object observes status from a control connection. For control + * connections, we don't worry about reporting transfer progress. + * + * The OnTransportStatus events are delivered to the main thread. + */ +class TransportEventForwarder : public nsITransportEventSink +{ +public: + TransportEventForwarder(nsIProgressEventSink* aSink) : mSink(aSink) {} + + NS_DECL_ISUPPORTS + NS_DECL_NSITRANSPORTEVENTSINK + +private: + nsCOMPtr mSink; +}; + +NS_IMPL_ISUPPORTS1(TransportEventForwarder, nsITransportEventSink) + +NS_IMETHODIMP +TransportEventForwarder::OnTransportStatus(nsITransport *transport, nsresult status, + PRUint64 progress, PRUint64 progressMax) +{ + // We only want to forward the resolving and connecting states of the + // control connection and report the data connection for the rest of the + // transfer, to avoid continuously switching between "sending to" and + // "receiving from" + if (mSink && + (status == NS_NET_STATUS_RESOLVING_HOST || + status == NS_NET_STATUS_CONNECTING_TO || + status == NS_NET_STATUS_CONNECTED_TO)) + mSink->OnStatus(nsnull, nsnull, status, nsnull); + return NS_OK; +} + + +/** + * This object observes events for a data connection. + */ +class DataRequestForwarder : public nsIFTPChannel, + public nsIStreamListener, + public nsIResumableChannel, + public nsITransportEventSink +{ +public: + DataRequestForwarder(); + + nsresult Init(nsIRequest *request); + + nsresult SetStreamListener(nsIStreamListener *listener); + nsresult SetCacheEntry(nsICacheEntryDescriptor *entry, PRBool writing); + nsresult SetEntityID(const nsACString& entity); + void SetFileSize(PRUint64 fileSize) { mFileSize = fileSize; } + + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSIRESUMABLECHANNEL + NS_DECL_NSITRANSPORTEVENTSINK + + NS_FORWARD_NSIREQUEST(mRequest->) + NS_FORWARD_NSICHANNEL(mFTPChannel->) + NS_FORWARD_NSIFTPCHANNEL(mFTPChannel->) + + PRUint64 GetBytesTransfered() {return mBytesTransfered;} + void Uploading(PRBool value, PRUint32 uploadCount); + void SetRetrying(PRBool retry); + +protected: + + nsCOMPtr mRequest; + nsCOMPtr mFTPChannel; + nsCOMPtr mListener; + nsCOMPtr mEventSink; + nsCOMPtr mCacheEntry; + nsCString mEntityID; + + nsUint64 mBytesTransfered; + nsUint64 mBytesToUpload; + PRUint64 mFileSize; + PRPackedBool mDelayedOnStartFired; + PRPackedBool mUploading; + PRPackedBool mRetrying; + + nsresult DelayedOnStartRequest(nsIRequest *request, nsISupports *ctxt); +}; + + +// The whole purpose of this class is to opaque +// the socket transport so that clients only see +// the same nsIChannel/nsIRequest that they started. + +NS_IMPL_THREADSAFE_ISUPPORTS7(DataRequestForwarder, + nsIStreamListener, + nsIRequestObserver, + nsIFTPChannel, + nsIResumableChannel, + nsIChannel, + nsIRequest, + nsITransportEventSink) + + +DataRequestForwarder::DataRequestForwarder() +{ + LOG(("(%x) DataRequestForwarder CREATED\n", this)); + + mBytesTransfered = 0; + mBytesToUpload = 0; + mRetrying = mUploading = mDelayedOnStartFired = PR_FALSE; + mFileSize = LL_MAXUINT; +} + +nsresult +DataRequestForwarder::Init(nsIRequest *request) +{ + LOG(("(%x) DataRequestForwarder Init [request=%x]\n", this, request)); + + NS_ENSURE_ARG(request); + + // for the forwarding declarations. + mRequest = request; + mFTPChannel = do_QueryInterface(request); + mEventSink = do_QueryInterface(request); + mListener = do_QueryInterface(request); + + if (!mRequest || !mFTPChannel) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +void +DataRequestForwarder::Uploading(PRBool value, PRUint32 uploadCount) +{ + mUploading = value; + mBytesToUpload = uploadCount; +} + +nsresult +DataRequestForwarder::SetCacheEntry(nsICacheEntryDescriptor *cacheEntry, PRBool writing) +{ + // if there is a cache entry descriptor, send data to it. + if (!cacheEntry) + return NS_ERROR_FAILURE; + + mCacheEntry = cacheEntry; + if (!writing) + return NS_OK; + + nsresult rv; + nsCOMPtr out; + rv = cacheEntry->OpenOutputStream(0, getter_AddRefs(out)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr tee = + do_CreateInstance(kStreamListenerTeeCID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = tee->Init(mListener, out); + if (NS_FAILED(rv)) return rv; + + mListener = do_QueryInterface(tee, &rv); + + return NS_OK; +} + + +nsresult +DataRequestForwarder::SetStreamListener(nsIStreamListener *listener) +{ + LOG(("(%x) DataRequestForwarder SetStreamListener [listener=%x]\n", this, listener)); + + mListener = listener; + if (!mListener) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +nsresult +DataRequestForwarder::SetEntityID(const nsACString& aEntityID) +{ + mEntityID = aEntityID; + return NS_OK; +} + +NS_IMETHODIMP +DataRequestForwarder::GetEntityID(nsACString& aEntityID) +{ + if (mEntityID.IsEmpty()) + return NS_ERROR_NOT_RESUMABLE; + + aEntityID = mEntityID; + return NS_OK; +} + +NS_IMETHODIMP +DataRequestForwarder::ResumeAt(PRUint64, + const nsACString&) +{ + // We shouldn't get here. This class only exists in the middle of a + // request + NS_NOTREACHED("DataRequestForwarder::ResumeAt"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +DataRequestForwarder::DelayedOnStartRequest(nsIRequest *request, nsISupports *ctxt) +{ + LOG(("(%x) DataRequestForwarder DelayedOnStartRequest \n", this)); + return mListener->OnStartRequest(this, ctxt); +} + +void +DataRequestForwarder::SetRetrying(PRBool retry) +{ + // The problem here is that if we send a second PASV, our listener would + // get an OnStop from the socket transport, and fail. So we temporarily + // suspend notifications + LOG(("(%x) DataRequestForwarder SetRetrying [retry=%d]\n", this, retry)); + + mRetrying = retry; + mDelayedOnStartFired = PR_FALSE; +} + +NS_IMETHODIMP +DataRequestForwarder::OnStartRequest(nsIRequest *request, nsISupports *ctxt) +{ + NS_ASSERTION(mListener, "No Listener Set."); + LOG(("(%x) DataRequestForwarder OnStartRequest \n[mRetrying=%d]", this, mRetrying)); + + if (!mListener) + return NS_ERROR_NOT_INITIALIZED; + + // OnStartRequest is delayed. + return NS_OK; +} + +NS_IMETHODIMP +DataRequestForwarder::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult statusCode) +{ + LOG(("(%x) DataRequestForwarder OnStopRequest [status=%x, mRetrying=%d]\n", this, statusCode, mRetrying)); + + if (mRetrying) { + mRetrying = PR_FALSE; + return NS_OK; + } + + // If there were no calls to ODA, then the onstart won't have been + // fired - bug 122913 + if (!mDelayedOnStartFired) { + mDelayedOnStartFired = PR_TRUE; + nsresult rv = DelayedOnStartRequest(request, ctxt); + if (NS_FAILED(rv)) return rv; + } + + if (!mListener) + return NS_ERROR_NOT_INITIALIZED; + + return mListener->OnStopRequest(this, ctxt, statusCode); +} + +NS_IMETHODIMP +DataRequestForwarder::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *input, PRUint32 offset, PRUint32 count) +{ + nsresult rv; + NS_ASSERTION(mListener, "No Listener Set."); + NS_ASSERTION(!mUploading, "Since we are uploading, we should never get a ODA"); + + if (!mListener) + return NS_ERROR_NOT_INITIALIZED; + + // we want to delay firing the onStartRequest until we know that there is data + if (!mDelayedOnStartFired) { + mDelayedOnStartFired = PR_TRUE; + rv = DelayedOnStartRequest(request, ctxt); + if (NS_FAILED(rv)) return rv; + } + + // XXX mBytesTransfered will get truncated from 64-bit to 32-bit + rv = mListener->OnDataAvailable(this, ctxt, input, mBytesTransfered, count); + if (NS_SUCCEEDED(rv)) + mBytesTransfered += count; + return rv; +} + +// nsITransportEventSink methods +NS_IMETHODIMP +DataRequestForwarder::OnTransportStatus(nsITransport *transport, nsresult status, + PRUint64 progress, PRUint64 progressMax) +{ + if (mEventSink) { + mEventSink->OnStatus(nsnull, nsnull, status, nsnull); + + if (status == nsISocketTransport::STATUS_RECEIVING_FROM || + status == nsISocketTransport::STATUS_SENDING_TO) { + // compute progress based on whether we are uploading or receiving... + PRUint64 count = mUploading ? progress : PRUint64(mBytesTransfered); + PRUint64 max = mUploading ? PRUint64(mBytesToUpload) : mFileSize; + mEventSink->OnProgress(this, nsnull, count, max); + } + } + return NS_OK; +} + + +NS_IMPL_THREADSAFE_ISUPPORTS3(nsFtpState, + nsIStreamListener, + nsIRequestObserver, + nsIRequest) nsFtpState::nsFtpState() - : nsBaseContentStream(PR_TRUE) - , mState(FTP_INIT) - , mNextState(FTP_S_USER) - , mKeepRunning(PR_TRUE) - , mReceivedControlData(PR_FALSE) - , mTryingCachedControl(PR_FALSE) - , mRETRFailed(PR_FALSE) - , mFileSize(LL_MAXUINT) - , mServerType(FTP_GENERIC_TYPE) - , mAction(GET) - , mAnonymous(PR_TRUE) - , mRetryPass(PR_FALSE) - , mInternalError(NS_OK) - , mPort(21) - , mIPv6Checked(PR_FALSE) - , mControlStatus(NS_OK) { - LOG_ALWAYS(("FTP:(%x) nsFtpState created", this)); + LOG_ALWAYS(("(%x) nsFtpState created", this)); + + // bool init + mRETRFailed = PR_FALSE; + mWaitingForDConn = mTryingCachedControl = mRetryPass = PR_FALSE; + mKeepRunning = mAnonymous = PR_TRUE; + + mAction = GET; + mState = FTP_COMMAND_CONNECT; + mNextState = FTP_S_USER; + + mInternalError = NS_OK; + mSuspendCount = 0; + mPort = 21; + + mReceivedControlData = PR_FALSE; + mControlStatus = NS_OK; + + mWriteCount = 0; + + mIPv6Checked = PR_FALSE; + mIPv6ServerAddress = nsnull; + + mControlConnection = nsnull; + mDRequestForwarder = nsnull; + mFileSize = LL_MAXUINT; // make sure handler stays around NS_ADDREF(gFtpHandler); @@ -105,51 +440,71 @@ nsFtpState::nsFtpState() nsFtpState::~nsFtpState() { - LOG_ALWAYS(("FTP:(%x) nsFtpState destroyed", this)); + LOG_ALWAYS(("(%x) nsFtpState destroyed", this)); + + if (mIPv6ServerAddress) nsMemory::Free(mIPv6ServerAddress); + NS_IF_RELEASE(mDRequestForwarder); // release reference to handler nsFtpProtocolHandler *handler = gFtpHandler; NS_RELEASE(handler); } -// nsIInputStreamCallback implementation -NS_IMETHODIMP -nsFtpState::OnInputStreamReady(nsIAsyncInputStream *aInStream) +nsresult +nsFtpState::GetEntityID(nsACString& aEntityID) { - LOG(("FTP:(%p) data stream ready\n", this)); - - // We are receiving a notification from our data stream, so just forward it - // on to our stream callback. - if (HasPendingCallback()) - DispatchCallbackSync(); + if (mEntityID.IsEmpty()) + return NS_ERROR_NOT_RESUMABLE; + aEntityID = mEntityID; return NS_OK; } -void -nsFtpState::OnControlDataAvailable(const char *aData, PRUint32 aDataLen) -{ - LOG(("FTP:(%p) control data available [%u]\n", this, aDataLen)); - mControlConnection->WaitData(this); // queue up another call - +// nsIStreamListener implementation +NS_IMETHODIMP +nsFtpState::OnDataAvailable(nsIRequest *request, + nsISupports *aContext, + nsIInputStream *aInStream, + PRUint32 aOffset, + PRUint32 aCount) +{ + if (aCount == 0) + return NS_OK; /*** should this be an error?? */ + if (!mReceivedControlData) { // parameter can be null cause the channel fills them in. - OnTransportStatus(nsnull, NS_NET_STATUS_BEGIN_FTP_TRANSACTION, 0, 0); + mChannel->OnStatus(nsnull, nsnull, + NS_NET_STATUS_BEGIN_FTP_TRANSACTION, nsnull); + mReceivedControlData = PR_TRUE; } + char* buffer = (char*)nsMemory::Alloc(aCount + 1); + if (!buffer) + return NS_ERROR_OUT_OF_MEMORY; + nsresult rv = aInStream->Read(buffer, aCount, &aCount); + if (NS_FAILED(rv)) + { + LOG(("(%x) nsFtpState - error reading %x\n", this, rv)); + nsMemory::Free(buffer); + return NS_ERROR_FAILURE; + } + buffer[aCount] = '\0'; + + nsXPIDLCString data; + data.Adopt(buffer); + + LOG(("(%x) reading %d bytes: \"%s\"", this, aCount, buffer)); + // Sometimes we can get two responses in the same packet, eg from LIST. // So we need to parse the response line by line - - nsCString buffer = mControlReadCarryOverBuf; - + nsCString lines(mControlReadCarryOverBuf); + lines.Append(data, aCount); // Clear the carryover buf - if we still don't have a line, then it will // be reappended below mControlReadCarryOverBuf.Truncate(); - buffer.Append(aData, aDataLen); - - const char* currLine = buffer.get(); + const char* currLine = lines.get(); while (*currLine) { PRInt32 eolLength = strcspn(currLine, CRLF); PRInt32 currLineLength = strlen(currLine); @@ -173,11 +528,10 @@ nsFtpState::OnControlDataAvailable(const char *aData, PRUint32 aDataLen) if ((currLineLength > eolLength) && (currLine[eolLength] == nsCRT::CR) && - (currLine[eolLength+1] == nsCRT::LF)) { + (currLine[eolLength+1] == nsCRT::LF)) crlfLength = 2; // CR +LF - } else { + else crlfLength = 1; // + LF or CR - } line.Assign(currLine, eolLength + crlfLength); @@ -203,7 +557,7 @@ nsFtpState::OnControlDataAvailable(const char *aData, PRUint32 aDataLen) if (startNum && line[3] == ' ') { // yup. last line, let's move on. if (mState == mNextState) { - NS_ERROR("ftp read state mixup"); + NS_ASSERTION(0, "ftp read state mixup"); mInternalError = NS_ERROR_FAILURE; mState = FTP_ERROR; } else { @@ -215,54 +569,70 @@ nsFtpState::OnControlDataAvailable(const char *aData, PRUint32 aDataLen) if (ftpSink) ftpSink->OnFTPControlLog(PR_TRUE, mResponseMsg.get()); - nsresult rv = Process(); + rv = Process(); mResponseMsg.Truncate(); - if (NS_FAILED(rv)) { - CloseWithStatus(rv); - return; - } + if (NS_FAILED(rv)) return rv; } currLine = currLine + eolLength + crlfLength; } + + return NS_OK; } -void -nsFtpState::OnControlError(nsresult status) + +// nsIRequestObserver implementation +NS_IMETHODIMP +nsFtpState::OnStartRequest(nsIRequest *request, nsISupports *aContext) { - NS_ASSERTION(NS_FAILED(status), "expecting error condition"); +#if defined(PR_LOGGING) + nsCAutoString spec; + (void)mURL->GetAsciiSpec(spec); - LOG(("FTP:(%p) CC(%p) error [%x was-cached=%u]\n", - this, mControlConnection.get(), status, mTryingCachedControl)); + LOG(("(%x) nsFtpState::OnStartRequest() (spec =%s)\n", this, spec.get())); +#endif + return NS_OK; +} - mControlStatus = status; - if (mTryingCachedControl && NS_SUCCEEDED(mInternalError)) { +NS_IMETHODIMP +nsFtpState::OnStopRequest(nsIRequest *request, nsISupports *aContext, + nsresult aStatus) +{ + LOG(("(%x) nsFtpState::OnStopRequest() rv=%x\n", this, aStatus)); + mControlStatus = aStatus; + + // HACK. This may not always work. I am assuming that I can mask the OnStopRequest + // notification since I created it via the control connection. + if (mTryingCachedControl && NS_FAILED(aStatus) && NS_SUCCEEDED(mInternalError)) { + LOG(("(%x) nsFtpState::OnStopRequest() cache connect failed. Reconnecting...\n", this, aStatus)); mTryingCachedControl = PR_FALSE; Connect(); - } else { - CloseWithStatus(status); - } + return NS_OK; + } + + if (NS_FAILED(aStatus)) // aStatus will be NS_OK if we are successfully disconnecing the control connection. + StopProcessing(); + + return NS_OK; } nsresult nsFtpState::EstablishControlConnection() { - NS_ASSERTION(!mControlConnection, "we already have a control connection"); - nsresult rv; - LOG(("FTP:(%x) trying cached control\n", this)); + LOG(("(%x) trying cached control\n", this)); - // Look to see if we can use a cached control connection: - nsFtpControlConnection *connection = nsnull; - gFtpHandler->RemoveConnection(mChannel->URI(), &connection); + nsFtpControlConnection* connection; + (void) gFtpHandler->RemoveConnection(mURL, &connection); + nsRefPtr fwd(new TransportEventForwarder(mChannel)); if (connection) { - mControlConnection.swap(connection); + mControlConnection = connection; if (mControlConnection->IsAlive()) { // set stream listener of the control connection to be us. - mControlConnection->WaitData(this); + (void) mControlConnection->SetStreamListener(NS_STATIC_CAST(nsIStreamListener*, this)); // read cached variables into us. mServerType = mControlConnection->mServerType; @@ -272,54 +642,53 @@ nsFtpState::EstablishControlConnection() // we're already connected to this server, skip login. mState = FTP_S_PASV; - mResponseCode = 530; // assume the control connection was dropped. + mResponseCode = 530; //assume the control connection was dropped. mControlStatus = NS_OK; mReceivedControlData = PR_FALSE; // For this request, we have not. - // if we succeed, return. Otherwise, we need to create a transport - rv = mControlConnection->Connect(mChannel->ProxyInfo(), this); + // if we succeed, return. Otherwise, we need to + // create a transport + rv = mControlConnection->Connect(mProxyInfo, fwd); if (NS_SUCCEEDED(rv)) return rv; } - LOG(("FTP:(%p) cached CC(%p) is unusable\n", this, - mControlConnection.get())); - - mControlConnection->WaitData(nsnull); - mControlConnection = nsnull; + else + { + LOG(("(%x) isAlive return false\n", this)); + NS_RELEASE(mControlConnection); + } } - LOG(("FTP:(%p) creating CC\n", this)); + LOG(("(%x) creating control\n", this)); mState = FTP_READ_BUF; mNextState = FTP_S_USER; nsCAutoString host; - rv = mChannel->URI()->GetAsciiHost(host); - if (NS_FAILED(rv)) - return rv; + rv = mURL->GetAsciiHost(host); + if (NS_FAILED(rv)) return rv; - mControlConnection = new nsFtpControlConnection(host, mPort); - if (!mControlConnection) - return NS_ERROR_OUT_OF_MEMORY; + mControlConnection = new nsFtpControlConnection(host.get(), mPort); + if (!mControlConnection) return NS_ERROR_OUT_OF_MEMORY; - rv = mControlConnection->Connect(mChannel->ProxyInfo(), this); - if (NS_FAILED(rv)) { - LOG(("FTP:(%p) CC(%p) failed to connect [rv=%x]\n", this, - mControlConnection.get(), rv)); - mControlConnection = nsnull; - return rv; - } + NS_ADDREF(mControlConnection); + + // Must do it this way 'cuz the channel intercepts the progress notifications. + (void) mControlConnection->SetStreamListener(NS_STATIC_CAST(nsIStreamListener*, this)); - return mControlConnection->WaitData(this); + return mControlConnection->Connect(mProxyInfo, fwd); } void nsFtpState::MoveToNextState(FTP_STATE nextState) { - if (NS_FAILED(mInternalError)) { + if (NS_FAILED(mInternalError)) + { mState = FTP_ERROR; - LOG(("FTP:(%x) FAILED (%x)\n", this, mInternalError)); - } else { + LOG(("(%x) FAILED (%x)\n", this, mInternalError)); + } + else + { mState = FTP_READ_BUF; mNextState = nextState; } @@ -331,56 +700,58 @@ nsFtpState::Process() nsresult rv = NS_OK; PRBool processingRead = PR_TRUE; - while (mKeepRunning && processingRead) { - switch (mState) { + while (mKeepRunning && processingRead) + { + switch(mState) + { case FTP_COMMAND_CONNECT: - KillControlConnection(); - LOG(("FTP:(%p) establishing CC", this)); - mInternalError = EstablishControlConnection(); // sets mState - if (NS_FAILED(mInternalError)) { - mState = FTP_ERROR; - LOG(("FTP:(%p) FAILED\n", this)); - } else { - LOG(("FTP:(%p) SUCCEEDED\n", this)); - } - break; + KillControlConnection(); + LOG(("(%x) Establishing control connection...", this)); + mInternalError = EstablishControlConnection(); // sets mState + if (NS_FAILED(mInternalError)) { + mState = FTP_ERROR; + LOG(("(%x) FAILED\n", this)); + } + else + LOG(("(%x) SUCCEEDED\n", this)); + break; case FTP_READ_BUF: - LOG(("FTP:(%p) Waiting for CC(%p)\n", this, - mControlConnection.get())); - processingRead = PR_FALSE; - break; + LOG(("(%x) Waiting for control data (%x)...", this, rv)); + processingRead = PR_FALSE; + break; case FTP_ERROR: // xx needs more work to handle dropped control connection cases - if ((mTryingCachedControl && mResponseCode == 530 && - mInternalError == NS_ERROR_FTP_PASV) || - (mResponseCode == 425 && - mInternalError == NS_ERROR_FTP_PASV)) { - // The user was logged out during an pasv operation - // we want to restart this request with a new control - // channel. - mState = FTP_COMMAND_CONNECT; - } else if (mResponseCode == 421 && + if ((mTryingCachedControl && mResponseCode == 530 && + mInternalError == NS_ERROR_FTP_PASV) || + (mResponseCode == 425 && + mInternalError == NS_ERROR_FTP_PASV)) { + // The user was logged out during an pasv operation + // we want to restart this request with a new control + // channel. + mState = FTP_COMMAND_CONNECT; + } + else if (mResponseCode == 421 && mInternalError != NS_ERROR_FTP_LOGIN) { - // The command channel dropped for some reason. - // Fire it back up, unless we were trying to login - // in which case the server might just be telling us - // that the max number of users has been reached... - mState = FTP_COMMAND_CONNECT; - } else { - LOG(("FTP:(%x) FTP_ERROR - calling StopProcessing\n", this)); - rv = StopProcessing(); - NS_ASSERTION(NS_SUCCEEDED(rv), "StopProcessing failed."); - processingRead = PR_FALSE; - } - break; + // The command channel dropped for some reason. + // Fire it back up, unless we were trying to login + // in which case the server might just be telling us + // that the max number of users has been reached... + mState = FTP_COMMAND_CONNECT; + } else { + LOG(("(%x) FTP_ERROR - Calling StopProcessing\n", this)); + rv = StopProcessing(); + NS_ASSERTION(NS_SUCCEEDED(rv), "StopProcessing failed."); + processingRead = PR_FALSE; + } + break; case FTP_COMPLETE: - LOG(("FTP:(%x) COMPLETE\n", this)); - rv = StopProcessing(); - NS_ASSERTION(NS_SUCCEEDED(rv), "StopProcessing failed."); - processingRead = PR_FALSE; - break; + LOG(("(%x) COMPLETE\n", this)); + rv = StopProcessing(); + NS_ASSERTION(NS_SUCCEEDED(rv), "StopProcessing failed."); + processingRead = PR_FALSE; + break; // USER case FTP_S_USER: @@ -490,11 +861,10 @@ nsFtpState::Process() case FTP_S_LIST: rv = S_list(); - if (rv == NS_ERROR_NOT_RESUMABLE) { + if (rv == NS_ERROR_NOT_RESUMABLE) mInternalError = rv; - } else if (NS_FAILED(rv)) { + else if (NS_FAILED(rv)) mInternalError = NS_ERROR_FTP_CWD; - } MoveToNextState(FTP_R_LIST); break; @@ -636,10 +1006,10 @@ nsFtpState::Process() default: ; - } - } + } + } - return rv; +return rv; } /////////////////////////////////// @@ -664,43 +1034,35 @@ nsFtpState::S_user() { return NS_ERROR_NOT_INITIALIZED; nsXPIDLString user, passwd; + PRBool retval; nsCAutoString prePath; - rv = mChannel->URI()->GetPrePath(prePath); - if (NS_FAILED(rv)) - return rv; + rv = mURL->GetPrePath(prePath); + if (NS_FAILED(rv)) return rv; NS_ConvertUTF8toUTF16 prePathU(prePath); - nsCOMPtr bundleService = - do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); - if (NS_FAILED(rv)) - return rv; + nsCOMPtr bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; nsCOMPtr bundle; rv = bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(bundle)); - if (NS_FAILED(rv)) - return rv; + if (NS_FAILED(rv)) return rv; - const PRUnichar *formatStrings[] = { prePathU.get() }; - NS_NAMED_LITERAL_STRING(name, "EnterUserPasswordFor"); - - nsXPIDLString formattedString; - rv = bundle->FormatStringFromName(name.get(), formatStrings, 1, - getter_Copies(formattedString)); - if (NS_FAILED(rv)) - return rv; - - PRBool retval; + nsXPIDLString formatedString; + const PRUnichar *formatStrings[1] = { prePathU.get()}; + rv = bundle->FormatStringFromName(NS_LITERAL_STRING("EnterUserPasswordFor").get(), + formatStrings, 1, + getter_Copies(formatedString)); rv = prompter->PromptUsernameAndPassword(nsnull, - formattedString, + formatedString, prePathU.get(), nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, getter_Copies(user), getter_Copies(passwd), &retval); - // if the user canceled or didn't supply a username we want to fail - if (NS_FAILED(rv) || !retval || (user && !*user) ) - return NS_ERROR_FAILURE; + // if the user canceled or didn't supply a username we want to fail + if (!retval || (user && !*user) ) + return NS_ERROR_FAILURE; mUsername = user; mPassword = passwd; } @@ -717,25 +1079,25 @@ nsFtpState::R_user() { if (mResponseCode/100 == 3) { // send off the password return FTP_S_PASS; - } - if (mResponseCode/100 == 2) { + } else if (mResponseCode/100 == 2) { // no password required, we're already logged in return FTP_S_SYST; - } - if (mResponseCode/100 == 5) { + } else if (mResponseCode/100 == 5) { // problem logging in. typically this means the server // has reached it's user limit. return FTP_ERROR; + } else { + // LOGIN FAILED + if (mAnonymous) { + // we just tried to login anonymously and failed. + // kick back out to S_user() and try again after + // gathering uname/pwd info from the user. + mAnonymous = PR_FALSE; + return FTP_S_USER; + } else { + return FTP_ERROR; + } } - // LOGIN FAILED - if (mAnonymous) { - // we just tried to login anonymously and failed. - // kick back out to S_user() and try again after - // gathering uname/pwd info from the user. - mAnonymous = PR_FALSE; - return FTP_S_USER; - } - return FTP_ERROR; } @@ -753,14 +1115,11 @@ nsFtpState::S_pass() { } else { nsXPIDLCString anonPassword; PRBool useRealEmail = PR_FALSE; - nsCOMPtr prefs = - do_GetService(NS_PREFSERVICE_CONTRACTID); + nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); if (prefs) { rv = prefs->GetBoolPref("advanced.mailftp", &useRealEmail); - if (NS_SUCCEEDED(rv) && useRealEmail) { - prefs->GetCharPref("network.ftp.anonymous_password", - getter_Copies(anonPassword)); - } + if (NS_SUCCEEDED(rv) && useRealEmail) + prefs->GetCharPref("network.ftp.anonymous_password", getter_Copies(anonPassword)); } if (!anonPassword.IsEmpty()) { passwordStr.AppendASCII(anonPassword); @@ -777,42 +1136,36 @@ nsFtpState::S_pass() { if (!prompter) return NS_ERROR_NOT_INITIALIZED; - nsCAutoString prePath; - rv = mChannel->URI()->GetPrePath(prePath); - if (NS_FAILED(rv)) - return rv; - NS_ConvertUTF8toUTF16 prePathU(prePath); - - nsCOMPtr bundleService = - do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); - if (NS_FAILED(rv)) - return rv; - - nsCOMPtr bundle; - rv = bundleService->CreateBundle(NECKO_MSGS_URL, - getter_AddRefs(bundle)); - - const PRUnichar *formatStrings[2] = { - mUsername.get(), prePathU.get() - }; - NS_NAMED_LITERAL_STRING(name, "EnterPasswordFor"); - - nsXPIDLString formattedString; - rv = bundle->FormatStringFromName(name.get(), formatStrings, 2, - getter_Copies(formattedString)); - if (NS_FAILED(rv)) - return rv; - nsXPIDLString passwd; PRBool retval; + + nsCAutoString prePath; + rv = mURL->GetPrePath(prePath); + if (NS_FAILED(rv)) return rv; + NS_ConvertUTF8toUTF16 prePathU(prePath); + + nsCOMPtr bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr bundle; + rv = bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(bundle)); + + nsXPIDLString formatedString; + const PRUnichar *formatStrings[2] = { mUsername.get(), prePathU.get() }; + rv = bundle->FormatStringFromName(NS_LITERAL_STRING("EnterPasswordFor").get(), + formatStrings, 2, + getter_Copies(formatedString)); + + if (NS_FAILED(rv)) return rv; rv = prompter->PromptPassword(nsnull, - formattedString, + formatedString, prePathU.get(), nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, getter_Copies(passwd), &retval); + // we want to fail if the user canceled. Note here that if they want // a blank password, we will pass it along. - if (NS_FAILED(rv) || !retval) + if (!retval) return NS_ERROR_FAILURE; mPassword = passwd; @@ -830,18 +1183,15 @@ nsFtpState::R_pass() { if (mResponseCode/100 == 3) { // send account info return FTP_S_ACCT; - } - if (mResponseCode/100 == 2) { + } else if (mResponseCode/100 == 2) { // logged in return FTP_S_SYST; - } - if (mResponseCode == 503) { + } else if (mResponseCode == 503) { // start over w/ the user command. // note: the password was successful, and it's stored in mPassword mRetryPass = PR_FALSE; return FTP_S_USER; - } - if (mResponseCode/100 == 5 || mResponseCode==421) { + } else if (mResponseCode/100 == 5 || mResponseCode==421) { // There is no difference between a too-many-users error, // a wrong-password error, or any other sort of error // So we need to tell wallet to forget the password if we had one, @@ -849,14 +1199,14 @@ nsFtpState::R_pass() { // user can retry if they want to if (!mPassword.IsEmpty()) { - nsCOMPtr pm = - do_GetService(NS_PASSWORDMANAGER_CONTRACTID); + nsCOMPtr pm = do_GetService(NS_PASSWORDMANAGER_CONTRACTID); if (pm) { nsCAutoString prePath; - nsresult rv = mChannel->URI()->GetPrePath(prePath); + nsresult rv = mURL->GetPrePath(prePath); NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get prepath"); - if (NS_SUCCEEDED(rv)) + if (NS_SUCCEEDED(rv)) { pm->RemoveUser(prePath, EmptyString()); + } } } @@ -875,7 +1225,8 @@ nsFtpState::R_pass() { nsresult nsFtpState::S_pwd() { - return SendFTPCommand(NS_LITERAL_CSTRING("PWD" CRLF)); + nsCString pwdStr("PWD" CRLF); + return SendFTPCommand(pwdStr); } FTP_STATE @@ -885,7 +1236,7 @@ nsFtpState::R_pwd() { nsCAutoString respStr(mResponseMsg); PRInt32 pos = respStr.FindChar('"'); if (pos > -1) { - respStr.Cut(0, pos+1); + respStr.Cut(0,pos+1); pos = respStr.FindChar('"'); if (pos > -1) { respStr.Truncate(pos); @@ -901,7 +1252,8 @@ nsFtpState::R_pwd() { nsresult nsFtpState::S_syst() { - return SendFTPCommand(NS_LITERAL_CSTRING("SYST" CRLF)); + nsCString systString("SYST" CRLF); + return SendFTPCommand( systString ); } FTP_STATE @@ -913,47 +1265,52 @@ nsFtpState::R_syst() { ( mResponseMsg.Find("MACOS Peter's Server") > -1) || ( mResponseMsg.Find("MACOS WebSTAR FTP") > -1) || ( mResponseMsg.Find("MVS") > -1) || - ( mResponseMsg.Find("OS/390") > -1)) { + ( mResponseMsg.Find("OS/390") > -1)) + { mServerType = FTP_UNIX_TYPE; - } else if (( mResponseMsg.Find("WIN32", PR_TRUE) > -1) || - ( mResponseMsg.Find("windows", PR_TRUE) > -1)) { + } + else if ( ( mResponseMsg.Find("WIN32", PR_TRUE) > -1) || + ( mResponseMsg.Find("windows", PR_TRUE) > -1) ) + { mServerType = FTP_NT_TYPE; - } else if (mResponseMsg.Find("OS/2", PR_TRUE) > -1) { + } + else if ( mResponseMsg.Find("OS/2", PR_TRUE) > -1) + { mServerType = FTP_OS2_TYPE; - } else if (mResponseMsg.Find("VMS", PR_TRUE) > -1) { + } + else if ( mResponseMsg.Find("VMS", PR_TRUE) > -1) + { mServerType = FTP_VMS_TYPE; - } else { - NS_ERROR("Server type list format unrecognized."); + } + else + { + NS_ASSERTION(0, "Server type list format unrecognized."); // Guessing causes crashes. // (Of course, the parsing code should be more robust...) nsresult rv; nsCOMPtr bundleService = - do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); - if (NS_FAILED(rv)) - return FTP_ERROR; + do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); + if (NS_FAILED(rv)) return FTP_ERROR; nsCOMPtr bundle; rv = bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(bundle)); - if (NS_FAILED(rv)) - return FTP_ERROR; + if (NS_FAILED(rv)) return FTP_ERROR; + nsXPIDLString formatedString; PRUnichar* ucs2Response = ToNewUnicode(mResponseMsg); const PRUnichar *formatStrings[1] = { ucs2Response }; - NS_NAMED_LITERAL_STRING(name, "UnsupportedFTPServer"); - - nsXPIDLString formattedString; - rv = bundle->FormatStringFromName(name.get(), formatStrings, 1, - getter_Copies(formattedString)); + rv = bundle->FormatStringFromName(NS_LITERAL_STRING("UnsupportedFTPServer").get(), + formatStrings, 1, + getter_Copies(formatedString)); nsMemory::Free(ucs2Response); - if (NS_FAILED(rv)) - return FTP_ERROR; + if (NS_FAILED(rv)) return FTP_ERROR; - // TODO(darin): this code should not be dictating UI like this! + // XXX(darin): this code should not be dictating UI like this! nsCOMPtr prompter; mChannel->GetCallback(prompter); if (prompter) - prompter->Alert(nsnull, formattedString.get()); + prompter->Alert(nsnull, formatedString.get()); // since we just alerted the user, clear mResponseMsg, // which is displayed to the user. @@ -976,20 +1333,22 @@ nsFtpState::R_syst() { nsresult nsFtpState::S_acct() { - return SendFTPCommand(NS_LITERAL_CSTRING("ACCT noaccount" CRLF)); + nsCString acctString("ACCT noaccount" CRLF); + return SendFTPCommand(acctString); } FTP_STATE nsFtpState::R_acct() { if (mResponseCode/100 == 2) return FTP_S_SYST; - - return FTP_ERROR; + else + return FTP_ERROR; } nsresult nsFtpState::S_type() { - return SendFTPCommand(NS_LITERAL_CSTRING("TYPE I" CRLF)); + nsCString typeString("TYPE I" CRLF); + return SendFTPCommand(typeString); } FTP_STATE @@ -1044,7 +1403,15 @@ FTP_STATE nsFtpState::R_size() { if (mResponseCode/100 == 2) { PR_sscanf(mResponseMsg.get() + 4, "%llu", &mFileSize); - mChannel->SetContentLength64(mFileSize); + PRUint32 size32; + LL_L2UI(size32, mFileSize); + if (NS_FAILED(mChannel->SetContentLength(size32))) return FTP_ERROR; + + // Set the 64-bit length too + mChannel->SetPropertyAsUint64(NS_CHANNEL_PROP_CONTENT_LENGTH, + mFileSize); + + mDRequestForwarder->SetFileSize(mFileSize); } // We may want to be able to resume this @@ -1077,82 +1444,105 @@ nsFtpState::R_mdtm() { } } - nsCString entityID; - entityID.Truncate(); - entityID.AppendInt(PRInt64(mFileSize)); - entityID.Append('/'); - entityID.Append(mModTime); - mChannel->SetEntityID(entityID); + mEntityID.Truncate(); + mEntityID.AppendInt(PRInt64(mFileSize)); + mEntityID.Append('/'); + mEntityID.Append(mModTime); + mDRequestForwarder->SetEntityID(mEntityID); + // if we tried downloading this, lets try restarting it... + if (mDRequestForwarder && mDRequestForwarder->GetBytesTransfered() > 0) { + mStartPos = mDRequestForwarder->GetBytesTransfered(); + return FTP_S_REST; + } + // We weren't asked to resume - if (!mChannel->ResumeRequested()) + if (mStartPos == LL_MAXUINT) return FTP_S_RETR; //if (our entityID == supplied one (if any)) - if (mSuppliedEntityID.IsEmpty() || entityID.Equals(mSuppliedEntityID)) + if (mSuppliedEntityID.IsEmpty() || + mEntityID.Equals(mSuppliedEntityID)) + { return FTP_S_REST; - - mInternalError = NS_ERROR_ENTITY_CHANGED; - mResponseMsg.Truncate(); - return FTP_ERROR; + } else { + mInternalError = NS_ERROR_ENTITY_CHANGED; + mResponseMsg.Truncate(); + return FTP_ERROR; + } } nsresult nsFtpState::SetContentType() { - return mChannel->SetContentType( - NS_LITERAL_CSTRING(APPLICATION_HTTP_INDEX_FORMAT)); + return mChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_HTTP_INDEX_FORMAT)); } nsresult nsFtpState::S_list() { - nsresult rv = SetContentType(); + nsresult rv; + + if (!mDRequestForwarder) + return NS_ERROR_FAILURE; + + rv = SetContentType(); + if (NS_FAILED(rv)) return FTP_ERROR; + + // save off the server type if we are caching. + if(mCacheEntry) { + nsCAutoString serverType; + serverType.AppendInt(mServerType); + (void) mCacheEntry->SetMetaDataElement("servertype", serverType.get()); + } - rv = mChannel->PushStreamConverter("text/ftp-dir", - APPLICATION_HTTP_INDEX_FORMAT); + nsCOMPtr converter; + + rv = BuildStreamConverter(getter_AddRefs(converter)); if (NS_FAILED(rv)) { // clear mResponseMsg which is displayed to the user. - // TODO: we should probably set this to something meaningful. + // TODO: we should probably set this to something + // meaningful. mResponseMsg = ""; return rv; } - if (mCacheEntry) { - // save off the server type if we are caching. - nsCAutoString serverType; - serverType.AppendInt(mServerType); - mCacheEntry->SetMetaDataElement("servertype", serverType.get()); - - // open cache entry for writing, and configure it to receive data. - if (NS_FAILED(InstallCacheListener())) { - mCacheEntry->Doom(); - mCacheEntry = nsnull; - } - } - + // the data forwarder defaults to sending notifications + // to the channel. Lets hijack and send the notifications + // to the stream converter. + mDRequestForwarder->SetStreamListener(converter); + mDRequestForwarder->SetCacheEntry(mCacheEntry, PR_TRUE); // dir listings aren't resumable - NS_ENSURE_TRUE(!mChannel->ResumeRequested(), NS_ERROR_NOT_RESUMABLE); - - mChannel->SetEntityID(EmptyCString()); - - const char *listString; - if (mServerType == FTP_VMS_TYPE) { - listString = "LIST *.*;0" CRLF; - } else { - listString = "LIST" CRLF; + NS_ASSERTION(mSuppliedEntityID.IsEmpty(), + "Entity ID given to directory request"); + NS_ASSERTION(mStartPos == LL_MAXUINT || mStartPos == nsUint64(0), + "Non-initial start position given to directory request"); + if (!mSuppliedEntityID.IsEmpty() || (mStartPos != LL_MAXUINT && mStartPos != nsUint64(0))) { + // If we reach this code, then the caller is in error + return NS_ERROR_NOT_RESUMABLE; } - return SendFTPCommand(nsDependentCString(listString)); + mDRequestForwarder->SetEntityID(EmptyCString()); + + nsCAutoString listString; + if (mServerType == FTP_VMS_TYPE) + listString.AssignLiteral("LIST *.*;0" CRLF); + else + listString.AssignLiteral("LIST" CRLF); + + return SendFTPCommand(listString); + } FTP_STATE nsFtpState::R_list() { if (mResponseCode/100 == 1) { - // OK, time to start reading from the data connection. - if (HasPendingCallback()) - mDataStream->AsyncWait(this, 0, 0, CallbackTarget()); + nsresult rv = mDPipeRequest->Resume(); + if (NS_FAILED(rv)) { + LOG(("(%x) dataPipe->Resume (rv=%x)\n", this, rv)); + return FTP_ERROR; + } return FTP_READ_BUF; } @@ -1166,6 +1556,7 @@ nsFtpState::R_list() { nsresult nsFtpState::S_retr() { + nsresult rv = NS_OK; nsCAutoString retrStr(mPath); if (retrStr.IsEmpty() || retrStr.First() != '/') retrStr.Insert(mPwd,0); @@ -1173,7 +1564,12 @@ nsFtpState::S_retr() { ConvertFilespecToVMS(retrStr); retrStr.Insert("RETR ",0); retrStr.Append(CRLF); - return SendFTPCommand(retrStr); + + if (!mDRequestForwarder) + return NS_ERROR_FAILURE; + + rv = SendFTPCommand(retrStr); + return rv; } FTP_STATE @@ -1192,8 +1588,11 @@ nsFtpState::R_retr() { (void)mCacheEntry->Doom(); mCacheEntry = nsnull; } - if (HasPendingCallback()) - mDataStream->AsyncWait(this, 0, 0, CallbackTarget()); + nsresult rv = mDPipeRequest->Resume(); + if (NS_FAILED(rv)) { + LOG(("(%x) dataPipe->Resume (rv=%x)\n", this, rv)); + return FTP_ERROR; + } return FTP_READ_BUF; } @@ -1204,6 +1603,7 @@ nsFtpState::R_retr() { if (mResponseCode/100 == 5) { mRETRFailed = PR_TRUE; + mDRequestForwarder->SetRetrying(PR_TRUE); return FTP_S_PASV; } @@ -1216,7 +1616,7 @@ nsFtpState::S_rest() { nsCAutoString restString("REST "); // The PRInt64 cast is needed to avoid ambiguity - restString.AppendInt(PRInt64(mChannel->StartPos()), 10); + restString.AppendInt(PRInt64(PRUint64(mStartPos)), 10); restString.Append(CRLF); return SendFTPCommand(restString); @@ -1226,7 +1626,7 @@ FTP_STATE nsFtpState::R_rest() { if (mResponseCode/100 == 4) { // If REST fails, then we can't resume - mChannel->SetEntityID(EmptyCString()); + mEntityID.Truncate(); mInternalError = NS_ERROR_NOT_RESUMABLE; mResponseMsg.Truncate(); @@ -1239,24 +1639,30 @@ nsFtpState::R_rest() { nsresult nsFtpState::S_stor() { - NS_ENSURE_STATE(mChannel->UploadStream()); + NS_ASSERTION(mWriteStream, "we're trying to upload without any data"); + + if (!mWriteStream) + return NS_ERROR_FAILURE; NS_ASSERTION(mAction == PUT, "Wrong state to be here"); - nsCOMPtr url = do_QueryInterface(mChannel->URI()); - NS_ASSERTION(url, "I thought you were a nsStandardURL"); - nsCAutoString storStr; - url->GetFilePath(storStr); + nsresult rv; + nsCOMPtr aURL(do_QueryInterface(mURL, &rv)); + if (NS_FAILED(rv)) return rv; + + rv = aURL->GetFilePath(storStr); + if (NS_FAILED(rv)) return rv; NS_ASSERTION(!storStr.IsEmpty(), "What does it mean to store a empty path"); - // kill the first slash since we want to be relative to CWD. if (storStr.First() == '/') + { + // kill the first slash since we want to be relative to CWD. storStr.Cut(0,1); - - if (mServerType == FTP_VMS_TYPE) + } + if (mServerType == FTP_VMS_TYPE) { ConvertFilespecToVMS(storStr); - + } NS_UnescapeURL(storStr); storStr.Insert("STOR ",0); storStr.Append(CRLF); @@ -1273,7 +1679,7 @@ nsFtpState::R_stor() { } if (mResponseCode/100 == 1) { - LOG(("FTP:(%x) writing on DT\n", this)); + LOG(("(%x) writing on Data Transport\n", this)); return FTP_READ_BUF; } @@ -1286,52 +1692,52 @@ nsFtpState::S_pasv() { nsresult rv; if (mIPv6Checked == PR_FALSE) { - NS_ASSERTION(mIPv6ServerAddress.get() == nsnull, "already initialized"); - // Find IPv6 socket address, if server is IPv6 mIPv6Checked = PR_TRUE; + PR_ASSERT(mIPv6ServerAddress == 0); nsITransport *controlSocket = mControlConnection->Transport(); - if (!controlSocket) - return FTP_ERROR; - - nsCOMPtr sTrans = do_QueryInterface(controlSocket); + if (!controlSocket) return FTP_ERROR; + nsCOMPtr sTrans = do_QueryInterface(controlSocket, &rv); + if (sTrans) { PRNetAddr addr; rv = sTrans->GetPeerAddr(&addr); - if (NS_SUCCEEDED(rv) && addr.raw.family == PR_AF_INET6 && - !PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped)) { - const size_t size = 100; - mIPv6ServerAddress = new char[size]; - if (mIPv6ServerAddress && - PR_NetAddrToString(&addr, mIPv6ServerAddress, - size) != PR_SUCCESS) - mIPv6ServerAddress = nsnull; + if (NS_SUCCEEDED(rv)) { + if (addr.raw.family == PR_AF_INET6 && !PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped)) { + mIPv6ServerAddress = (char *) nsMemory::Alloc(100); + if (mIPv6ServerAddress) { + if (PR_NetAddrToString(&addr, mIPv6ServerAddress, 100) != PR_SUCCESS) { + nsMemory::Free(mIPv6ServerAddress); + mIPv6ServerAddress = 0; + } + } + } } } } - const char *string; - if (mIPv6ServerAddress) { + const char * string; + if (mIPv6ServerAddress) string = "EPSV" CRLF; - } else { + else string = "PASV" CRLF; - } - return SendFTPCommand(nsDependentCString(string)); + nsCString pasvString(string); + return SendFTPCommand(pasvString); } FTP_STATE nsFtpState::R_pasv() { - if (mResponseCode/100 != 2) - return FTP_ERROR; - nsresult rv; PRInt32 port; - nsCAutoString responseCopy(mResponseMsg); - char *response = responseCopy.BeginWriting(); + if (mResponseCode/100 != 2) { + return FTP_ERROR; + } + char *response = ToNewCString(mResponseMsg); + if (!response) return FTP_ERROR; char *ptr = response; nsCAutoString host; @@ -1340,21 +1746,22 @@ nsFtpState::R_pasv() { // text (|||ppp|) // Where '|' can be any single character char delim; - while (*ptr && *ptr != '(') - ptr++; - if (*ptr++ != '(') + while (*ptr && *ptr != '(') ptr++; + if (*ptr++ != '(') { return FTP_ERROR; + } delim = *ptr++; - if (!delim || *ptr++ != delim || - *ptr++ != delim || - *ptr < '0' || *ptr > '9') + if (!delim || *ptr++ != delim || *ptr++ != delim || + *ptr < '0' || *ptr > '9') { return FTP_ERROR; + } port = 0; do { port = port * 10 + *ptr++ - '0'; } while (*ptr >= '0' && *ptr <= '9'); - if (*ptr++ != delim || *ptr != ')') + if (*ptr++ != delim || *ptr != ')') { return FTP_ERROR; + } } else { // The returned address string can be of the form // (xxx,xxx,xxx,xxx,ppp,ppp) or @@ -1389,8 +1796,9 @@ nsFtpState::R_pasv() { } NS_ASSERTION(fields == 6, "Can't parse PASV response"); - if (fields < 6) + if (fields < 6) { return FTP_ERROR; + } port = ((PRInt32) (p0<<8)) + p1; host.AppendInt(h0); @@ -1402,91 +1810,105 @@ nsFtpState::R_pasv() { host.AppendInt(h3); } - const char* hostStr = - mIPv6ServerAddress ? mIPv6ServerAddress : host.get(); + nsMemory::Free(response); + + const char* hostStr = mIPv6ServerAddress ? mIPv6ServerAddress : host.get(); PRBool newDataConn = PR_TRUE; - if (mDataTransport) { + if (mDPipeRequest) { // Reuse this connection only if its still alive, and the port // is the same - nsCOMPtr strans = do_QueryInterface(mDataTransport); - if (strans) { + + if (mDPipe) { PRInt32 oldPort; - nsresult rv = strans->GetPort(&oldPort); + nsresult rv = mDPipe->GetPort(&oldPort); if (NS_SUCCEEDED(rv)) { if (oldPort == port) { PRBool isAlive; - if (NS_SUCCEEDED(strans->IsAlive(&isAlive)) && isAlive) + if (NS_SUCCEEDED(mDPipe->IsAlive(&isAlive)) && isAlive) newDataConn = PR_FALSE; } } } if (newDataConn) { - mDataTransport->Close(NS_ERROR_ABORT); - mDataTransport = nsnull; - mDataStream = nsnull; + mDPipeRequest->Cancel(NS_ERROR_ABORT); + mDPipeRequest = 0; + mDPipe = 0; + } else { + mDRequestForwarder->SetRetrying(PR_FALSE); } } if (newDataConn) { // now we know where to connect our data channel - nsCOMPtr sts = - do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + nsCOMPtr sts = do_GetService(kSocketTransportServiceCID, &rv); - nsCOMPtr strans; - rv = sts->CreateTransport(nsnull, 0, nsDependentCString(hostStr), - port, mChannel->ProxyInfo(), - getter_AddRefs(strans)); // the data socket - if (NS_FAILED(rv)) - return FTP_ERROR; - mDataTransport = strans; + rv = sts->CreateTransport(nsnull, 0, + nsDependentCString(hostStr), port, mProxyInfo, + getter_AddRefs(mDPipe)); // the data socket + if (NS_FAILED(rv)) return FTP_ERROR; - LOG(("FTP:(%x) created DT (%s:%x)\n", this, hostStr, port)); + LOG(("(%x) Created Data Transport (%s:%x)\n", this, hostStr, port)); + if (!mDRequestForwarder) { + mDRequestForwarder = new DataRequestForwarder; + if (!mDRequestForwarder) return FTP_ERROR; + NS_ADDREF(mDRequestForwarder); + + rv = mDRequestForwarder->Init(mChannel); + if (NS_FAILED(rv)){ + LOG(("(%x) forwarder->Init failed (rv=%x)\n", this, rv)); + return FTP_ERROR; + } + } + + mWaitingForDConn = PR_TRUE; + // hook ourself up as a proxy for status notifications nsCOMPtr eventQ; rv = NS_GetCurrentEventQ(getter_AddRefs(eventQ)); - NS_ENSURE_SUCCESS(rv, FTP_ERROR); - - rv = mDataTransport->SetEventSink(this, eventQ); - NS_ENSURE_SUCCESS(rv, FTP_ERROR); + if (NS_FAILED(rv)){ + LOG(("(%x) NS_GetCurrentEventQ failed (rv=%x)\n", this, rv)); + return FTP_ERROR; + } + rv = mDPipe->SetEventSink(NS_STATIC_CAST(nsITransportEventSink*, mDRequestForwarder), eventQ); + if (NS_FAILED(rv)){ + LOG(("(%x) forwarder->SetEventSink failed (rv=%x)\n", this, rv)); + return FTP_ERROR; + } if (mAction == PUT) { NS_ASSERTION(!mRETRFailed, "Failed before uploading"); + mDRequestForwarder->Uploading(PR_TRUE, mWriteCount); // nsIUploadChannel requires the upload stream to support ReadSegments. // therefore, we can open an unbuffered socket output stream. nsCOMPtr output; - rv = mDataTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, - 0, 0, getter_AddRefs(output)); - if (NS_FAILED(rv)) - return FTP_ERROR; + rv = mDPipe->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, + getter_AddRefs(output)); + if (NS_FAILED(rv)) return FTP_ERROR; // perform the data copy on the socket transport thread. we do this // because "output" is a socket output stream, so the result is that // all work will be done on the socket transport thread. - nsCOMPtr stEventTarget = - do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); - if (NS_FAILED(rv)) - return FTP_ERROR; + nsCOMPtr stEventTarget = do_GetService(kSocketTransportServiceCID, &rv); + if (NS_FAILED(rv)) return FTP_ERROR; nsCOMPtr copier; rv = NS_NewAsyncStreamCopier(getter_AddRefs(copier), - mChannel->UploadStream(), + mWriteStream, output, stEventTarget, - PR_TRUE, // upload stream is buffered + PR_TRUE, // mWriteStream is buffered PR_FALSE); // output is NOT buffered - if (NS_FAILED(rv)) - return FTP_ERROR; + if (NS_FAILED(rv)) return FTP_ERROR; - rv = copier->AsyncCopy(this, nsnull); - if (NS_FAILED(rv)) - return FTP_ERROR; + rv = copier->AsyncCopy(mDRequestForwarder, nsnull); + if (NS_FAILED(rv)) return FTP_ERROR; // hold a reference to the copier so we can cancel it if necessary. - mUploadRequest = copier; + mDPipeRequest = copier; // update the current working directory before sending the STOR // command. this is needed since we might be reusing a control @@ -1500,12 +1922,42 @@ nsFtpState::R_pasv() { // open a buffered, asynchronous socket input stream nsCOMPtr input; - rv = mDataTransport->OpenInputStream(0, - FTP_DATA_CHANNEL_SEG_SIZE, - FTP_DATA_CHANNEL_SEG_COUNT, - getter_AddRefs(input)); - NS_ENSURE_SUCCESS(rv, FTP_ERROR); - mDataStream = do_QueryInterface(input); + rv = mDPipe->OpenInputStream(0, + FTP_DATA_CHANNEL_SEG_SIZE, + FTP_DATA_CHANNEL_SEG_COUNT, + getter_AddRefs(input)); + if (NS_FAILED(rv)) { + LOG(("(%x) OpenInputStream failed (rv=%x)\n", this, rv)); + return FTP_ERROR; + } + + // pump data to the request forwarder... + nsCOMPtr pump; + rv = NS_NewInputStreamPump(getter_AddRefs(pump), input, nsInt64(-1), + nsInt64(-1), 0, 0, PR_TRUE); + if (NS_FAILED(rv)) { + LOG(("(%x) NS_NewInputStreamPump failed (rv=%x)\n", this, rv)); + return FTP_ERROR; + } + + rv = pump->AsyncRead(mDRequestForwarder, nsnull); + if (NS_FAILED(rv)) { + LOG(("(%x) AsyncRead failed (rv=%x)\n", this, rv)); + return FTP_ERROR; + } + + // hold a reference to the input stream pump so we can cancel it. + mDPipeRequest = pump; + + // Suspend the read + // If we don't do this, then the remote server could close the + // connection before we get the error message, and then we process the + // onstop as if it was from the real data connection + rv = mDPipeRequest->Suspend(); + if (NS_FAILED(rv)){ + LOG(("(%x) dataPipe->Suspend failed (rv=%x)\n", this, rv)); + return FTP_ERROR; + } } if (mRETRFailed) @@ -1513,15 +1965,154 @@ nsFtpState::R_pasv() { return FTP_S_SIZE; } + //////////////////////////////////////////////////////////////////////////////// // nsIRequest methods: -static inline -PRUint32 NowInSeconds() +NS_IMETHODIMP +nsFtpState::GetName(nsACString &result) { - return PRUint32(PR_Now() / PR_USEC_PER_SEC); + return mURL->GetSpec(result); } +NS_IMETHODIMP +nsFtpState::IsPending(PRBool *result) +{ + nsresult rv = NS_OK; + *result = PR_FALSE; + + nsIRequest *request = mControlConnection->ReadRequest(); + if (request) + rv = request->IsPending(result); + + return rv; +} + +NS_IMETHODIMP +nsFtpState::GetStatus(nsresult *status) +{ + *status = mInternalError; + return NS_OK; +} + +NS_IMETHODIMP +nsFtpState::Cancel(nsresult status) +{ + NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code"); + LOG(("(%x) nsFtpState::Cancel() rv=%x\n", this, status)); + + // we should try to recover the control connection.... + if (NS_SUCCEEDED(mControlStatus)) + mControlStatus = status; + + if (mKeepRunning) + (void) StopProcessing(); + return NS_OK; +} + +NS_IMETHODIMP +nsFtpState::Suspend(void) +{ + nsresult rv = NS_OK; + + if (!mControlConnection) + return NS_ERROR_FAILURE; + + // suspending the underlying socket transport will + // cause the FTP state machine to "suspend" when it + // tries to use the transport. May not be granular + // enough. + if (mSuspendCount < 1) { + mSuspendCount++; + + // only worry about the read request. + nsIRequest *request = mControlConnection->ReadRequest(); + if (request) { + rv = request->Suspend(); + if (NS_FAILED(rv)) return rv; + } + + if (mDPipeRequest) + rv = mDPipeRequest->Suspend(); + } + + return rv; +} + +NS_IMETHODIMP +nsFtpState::Resume(void) +{ + nsresult rv = NS_ERROR_FAILURE; + + // resuming the underlying socket transports will + // cause the FTP state machine to unblock and + // go on about it's business. + if (mSuspendCount) { + + PRBool dataAlive = PR_FALSE; + + if (mDPipe) + mDPipe->IsAlive(&dataAlive); + + if (mDPipe && dataAlive && mControlConnection->IsAlive()) + { + nsIRequest *controlRequest = mControlConnection->ReadRequest(); + NS_ASSERTION(controlRequest, "where did my request go!"); + + controlRequest->Resume(); + rv = mDPipeRequest->Resume(); + } + else + { + // control or data connection went down. need to reconnect. + // if we were downloading, we need to perform REST. + rv = EstablishControlConnection(); + } + } + mSuspendCount--; + return rv; +} + +NS_IMETHODIMP +nsFtpState::GetLoadGroup(nsILoadGroup **aLoadGroup) +{ + *aLoadGroup = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsFtpState::SetLoadGroup(nsILoadGroup *aLoadGroup) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsFtpState::GetLoadFlags(nsLoadFlags *aLoadFlags) +{ + *aLoadFlags = nsIRequest::LOAD_NORMAL; + return NS_OK; +} + +NS_IMETHODIMP +nsFtpState::SetLoadFlags(nsLoadFlags aLoadFlags) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +// This really really needs to be somewhere else +static inline PRUint32 +PRTimeToSeconds(PRTime t_usec) +{ + PRTime usec_per_sec; + PRUint32 t_sec; + LL_I2L(usec_per_sec, PR_USEC_PER_SEC); + LL_DIV(t_usec, t_usec, usec_per_sec); + LL_L2I(t_sec, t_usec); + return t_sec; +} + +#define NowInSeconds() PRTimeToSeconds(PR_Now()) + PRUint32 nsFtpState::mSessionStartTime = NowInSeconds(); /* Is this cache entry valid to use for reading? @@ -1543,131 +2134,120 @@ PRUint32 nsFtpState::mSessionStartTime = NowInSeconds(); * caching files - bbaetz */ PRBool -nsFtpState::CanReadCacheEntry() +nsFtpState::CanReadEntry() { - NS_ASSERTION(mCacheEntry, "must have a cache entry"); - nsCacheAccessMode access; nsresult rv = mCacheEntry->GetAccessGranted(&access); - if (NS_FAILED(rv)) - return PR_FALSE; + if (NS_FAILED(rv)) return PR_FALSE; // If I'm not granted read access, then I can't reuse it... if (!(access & nsICache::ACCESS_READ)) return PR_FALSE; - if (mChannel->HasLoadFlag(nsIRequest::LOAD_FROM_CACHE)) + nsLoadFlags flags; + rv = mChannel->GetLoadFlags(&flags); + if (NS_FAILED(rv)) return PR_FALSE; + + if (flags & LOAD_FROM_CACHE) return PR_TRUE; - if (mChannel->HasLoadFlag(nsIRequest::LOAD_BYPASS_CACHE)) + if (flags & LOAD_BYPASS_CACHE) return PR_FALSE; - if (mChannel->HasLoadFlag(nsIRequest::VALIDATE_ALWAYS)) + if (flags & VALIDATE_ALWAYS) return PR_FALSE; PRUint32 time; - if (mChannel->HasLoadFlag(nsIRequest::VALIDATE_ONCE_PER_SESSION)) { + if (flags & VALIDATE_ONCE_PER_SESSION) { rv = mCacheEntry->GetLastModified(&time); - if (NS_FAILED(rv)) - return PR_FALSE; + if (NS_FAILED(rv)) return PR_FALSE; return (mSessionStartTime > time); } - if (mChannel->HasLoadFlag(nsIRequest::VALIDATE_NEVER)) + if (flags & VALIDATE_NEVER) return PR_TRUE; // OK, now we just check the expiration time as usual rv = mCacheEntry->GetExpirationTime(&time); - if (NS_FAILED(rv)) - return PR_FALSE; - + if (NS_FAILED(rv)) return rv; return (NowInSeconds() <= time); } nsresult -nsFtpState::InstallCacheListener() +nsFtpState::Init(nsFTPChannel* aChannel, + nsICacheEntryDescriptor* cacheEntry, + nsIProxyInfo* proxyInfo, + PRUint64 startPos, + const nsACString& entity) { - NS_ASSERTION(mCacheEntry, "must have a cache entry"); - - nsCOMPtr out; - mCacheEntry->OpenOutputStream(0, getter_AddRefs(out)); - NS_ENSURE_STATE(out); - - nsCOMPtr tee = - do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID); - NS_ENSURE_STATE(tee); - - nsresult rv = tee->Init(mChannel->StreamListener(), out); - NS_ENSURE_SUCCESS(rv, rv); - - mChannel->SetStreamListener(tee); - return NS_OK; -} - -nsresult -nsFtpState::OpenCacheDataStream() -{ - NS_ASSERTION(mCacheEntry, "must have a cache entry"); - - // Get a transport to the cached data... - nsCOMPtr input; - mCacheEntry->OpenInputStream(0, getter_AddRefs(input)); - NS_ENSURE_STATE(input); - - nsCOMPtr sts = - do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); - NS_ENSURE_STATE(sts); - - nsCOMPtr transport; - sts->CreateInputTransport(input, -1, -1, PR_TRUE, - getter_AddRefs(transport)); - NS_ENSURE_STATE(transport); - - nsCOMPtr eventQ; - NS_GetCurrentEventQ(getter_AddRefs(eventQ)); - NS_ENSURE_STATE(eventQ); - - nsresult rv = transport->SetEventSink(this, eventQ); - NS_ENSURE_SUCCESS(rv, rv); - - // Open a non-blocking, buffered input stream... - nsCOMPtr transportInput; - transport->OpenInputStream(0, FTP_DATA_CHANNEL_SEG_SIZE, - FTP_DATA_CHANNEL_SEG_COUNT, - getter_AddRefs(transportInput)); - NS_ENSURE_STATE(transportInput); - - mDataStream = do_QueryInterface(transportInput); - NS_ENSURE_STATE(mDataStream); - - mDataTransport = transport; - return NS_OK; -} - -nsresult -nsFtpState::Init(nsFtpChannel *channel) -{ - // parameter validation - NS_ASSERTION(channel, "FTP: needs a channel"); - - mChannel = channel; // a straight ref ptr to the channel - mKeepRunning = PR_TRUE; - mSuppliedEntityID = channel->EntityID(); - - if (channel->UploadStream()) - mAction = PUT; + mCacheEntry = cacheEntry; + mProxyInfo = proxyInfo; + mStartPos = startPos; + mSuppliedEntityID = entity; + + // parameter validation + NS_ASSERTION(aChannel, "FTP: needs a channel"); - nsresult rv; - nsCAutoString path; - nsCOMPtr url = do_QueryInterface(mChannel->URI()); - if (url) { - rv = url->GetFilePath(path); - } else { - rv = mChannel->URI()->GetPath(path); - } + mChannel = aChannel; // a straight ref ptr to the channel + + nsresult rv = aChannel->GetURI(getter_AddRefs(mURL)); if (NS_FAILED(rv)) return rv; + + if (mCacheEntry) { + if (CanReadEntry()) { + // XXX - all this code assumes that we only cache directories + // If we start caching files, this needs to be updated + + // make sure the channel knows wassup + SetContentType(); + + NS_ASSERTION(!mDRequestForwarder, "there should not be a data forwarder"); + mDRequestForwarder = new DataRequestForwarder; + if (!mDRequestForwarder) return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mDRequestForwarder); + + rv = mDRequestForwarder->Init(mChannel); + + nsXPIDLCString serverType; + (void) mCacheEntry->GetMetaDataElement("servertype", getter_Copies(serverType)); + nsCAutoString serverNum(serverType.get()); + PRInt32 err; + mServerType = serverNum.ToInteger(&err); + + nsCOMPtr converter; + rv = BuildStreamConverter(getter_AddRefs(converter)); + if (NS_FAILED(rv)) return rv; + + mDRequestForwarder->SetStreamListener(converter); + mDRequestForwarder->SetCacheEntry(mCacheEntry, PR_FALSE); + mDRequestForwarder->SetEntityID(EmptyCString()); + + // Get a transport to the cached data... + nsCOMPtr input; + rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(input)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr pump; + rv = NS_NewInputStreamPump(getter_AddRefs(pump), input); + if (NS_FAILED(rv)) return rv; + + // Pump the cache data downstream + rv = pump->AsyncRead(mDRequestForwarder, nsnull); + if (NS_FAILED(rv)) return rv; + + mDPipeRequest = pump; + } + } + + nsCAutoString path; + nsCOMPtr aURL(do_QueryInterface(mURL)); + if (aURL) + rv = aURL->GetFilePath(path); + else + rv = mURL->GetPath(path); + if (NS_FAILED(rv)) return rv; // Skip leading slash char *fwdPtr = path.BeginWriting(); @@ -1686,7 +2266,7 @@ nsFtpState::Init(nsFtpChannel *channel) // pull any username and/or password out of the uri nsCAutoString uname; - rv = mChannel->URI()->GetUsername(uname); + rv = mURL->GetUsername(uname); if (NS_FAILED(rv)) return rv; @@ -1700,7 +2280,7 @@ nsFtpState::Init(nsFtpChannel *channel) } nsCAutoString password; - rv = mChannel->URI()->GetPassword(password); + rv = mURL->GetPassword(password); if (NS_FAILED(rv)) return rv; @@ -1713,9 +2293,8 @@ nsFtpState::Init(nsFtpChannel *channel) // setup the connection cache key PRInt32 port; - rv = mChannel->URI()->GetPort(&port); - if (NS_FAILED(rv)) - return rv; + rv = mURL->GetPort(&port); + if (NS_FAILED(rv)) return rv; if (port > 0) mPort = port; @@ -1723,21 +2302,39 @@ nsFtpState::Init(nsFtpChannel *channel) return NS_OK; } -void +nsresult nsFtpState::Connect() { + if (mDRequestForwarder) + return NS_OK; // we are already connected. + + nsresult rv; + mState = FTP_COMMAND_CONNECT; mNextState = FTP_S_USER; - nsresult rv = Process(); + rv = Process(); // check for errors. if (NS_FAILED(rv)) { - LOG(("FTP:Process() failed: %x\n", rv)); + LOG(("-- Connect() on Control Connect FAILED with rv = %x\n", rv)); mInternalError = NS_ERROR_FAILURE; mState = FTP_ERROR; - CloseWithStatus(mInternalError); } + + return rv; +} + +nsresult +nsFtpState::SetWriteStream(nsIInputStream* aInStream) +{ + if (!aInStream) + return NS_OK; + + mAction = PUT; + mWriteStream = aInStream; + mWriteStream->Available(&mWriteCount); + return NS_OK; } void @@ -1745,9 +2342,13 @@ nsFtpState::KillControlConnection() { mControlReadCarryOverBuf.Truncate(0); - mIPv6Checked = PR_FALSE; - mIPv6ServerAddress = nsnull; + NS_IF_RELEASE(mDRequestForwarder); + mIPv6Checked = PR_FALSE; + if (mIPv6ServerAddress) { + nsMemory::Free(mIPv6ServerAddress); + mIPv6ServerAddress = 0; + } // if everything went okay, save the connection. // FIX: need a better way to determine if we can cache the connections. // there are some errors which do not mean that we need to kill the connection @@ -1757,44 +2358,42 @@ nsFtpState::KillControlConnection() return; // kill the reference to ourselves in the control connection. - mControlConnection->WaitData(nsnull); + (void) mControlConnection->SetStreamListener(nsnull); - if (NS_SUCCEEDED(mInternalError) && + if (FTP_CACHE_CONTROL_CONNECTION && + NS_SUCCEEDED(mInternalError) && NS_SUCCEEDED(mControlStatus) && mControlConnection->IsAlive()) { - LOG_ALWAYS(("FTP:(%p) caching CC(%p)", this, mControlConnection.get())); + LOG_ALWAYS(("(%x) nsFtpState caching control connection", this)); // Store connection persistent data mControlConnection->mServerType = mServerType; mControlConnection->mPassword = mPassword; mControlConnection->mPwd = mPwd; - nsresult rv = gFtpHandler->InsertConnection(mChannel->URI(), - mControlConnection); + nsresult rv = gFtpHandler->InsertConnection(mURL, mControlConnection); // Can't cache it? Kill it then. mControlConnection->Disconnect(rv); - } else { + } + else mControlConnection->Disconnect(NS_BINDING_ABORTED); - } - mControlConnection = nsnull; + NS_RELEASE(mControlConnection); + + return; } nsresult nsFtpState::StopProcessing() { - // Only do this function once. - if (!mKeepRunning) - return NS_OK; - mKeepRunning = PR_FALSE; - - LOG_ALWAYS(("FTP:(%x) nsFtpState stopping", this)); + LOG_ALWAYS(("(%x) nsFtpState stopping", this)); #ifdef DEBUG_dougt printf("FTP Stopped: [response code %d] [response msg follows:]\n%s\n", mResponseCode, mResponseMsg.get()); #endif - if (NS_FAILED(mInternalError) && !mResponseMsg.IsEmpty()) { + if (NS_FAILED(mInternalError) && !mResponseMsg.IsEmpty()) + { // check to see if the control status is bad. // web shell wont throw an alert. we better: @@ -1806,24 +2405,112 @@ nsFtpState::StopProcessing() } nsresult broadcastErrorCode = mControlStatus; - if (NS_SUCCEEDED(broadcastErrorCode)) + if ( NS_SUCCEEDED(broadcastErrorCode)) broadcastErrorCode = mInternalError; mInternalError = broadcastErrorCode; + if (mDPipeRequest && NS_FAILED(broadcastErrorCode)) + mDPipeRequest->Cancel(broadcastErrorCode); + + if (mDRequestForwarder) { + NS_RELEASE(mDRequestForwarder); + } + else + { + // The forwarding object was never created which means that we never sent our notifications. + + nsCOMPtr asyncObserver; + + NS_NewRequestObserverProxy(getter_AddRefs(asyncObserver), + mChannel, + NS_CURRENT_EVENTQ); + if(asyncObserver) { + (void) asyncObserver->OnStartRequest(this, nsnull); + (void) asyncObserver->OnStopRequest(this, nsnull, broadcastErrorCode); + } + + + } + + // Clean up the event loop + mKeepRunning = PR_FALSE; + KillControlConnection(); - // XXX This can fire before we are done loading data. Is that a problem? - OnTransportStatus(nsnull, NS_NET_STATUS_END_FTP_TRANSACTION, 0, 0); + mChannel->OnStatus(nsnull, nsnull, NS_NET_STATUS_END_FTP_TRANSACTION, nsnull); - if (NS_FAILED(broadcastErrorCode)) - CloseWithStatus(broadcastErrorCode); + // Release the Observers + mWriteStream = 0; // should this call close before setting to null? + + mChannel = 0; + mProxyInfo = 0; return NS_OK; } + +nsresult +nsFtpState::BuildStreamConverter(nsIStreamListener** convertStreamListener) +{ + nsresult rv; + // setup a listener to push the data into. This listener sits inbetween the + // unconverted data of fromType, and the final listener in the chain (in this case + // the mListener). + nsCOMPtr converterListener; + + nsCOMPtr scs = + do_GetService(kStreamConverterServiceCID, &rv); + + if (NS_FAILED(rv)) + return rv; + + rv = scs->AsyncConvertData("text/ftp-dir", + APPLICATION_HTTP_INDEX_FORMAT, + mChannel, + mURL, + getter_AddRefs(converterListener)); + if (NS_FAILED(rv)) { + LOG(("(%x) scs->AsyncConvertData failed (rv=%x)\n", this, rv)); + return rv; + } + + NS_ADDREF(*convertStreamListener = converterListener); + return rv; +} + +void +nsFtpState::DataConnectionEstablished() +{ + LOG(("(%x) Data Connection established.", this)); + mWaitingForDConn = PR_FALSE; + + // If we no longer have a channel, we don't need this connection + if (!mChannel) { + LOG((" Ignoring data connection")); + return; + } + + // sending empty string with (mWaitingForDConn == PR_FALSE) will cause the + // control socket to write out its buffer. + nsCString a(""); + SendFTPCommand(a); +} + +void +nsFtpState::DataConnectionComplete() +{ + LOG(("(%x) Data Connection complete.", this)); + + if (mDPipe) { + mDPipe->SetEventSink(nsnull, nsnull); + mDPipe->Close(NS_ERROR_ABORT); + mDPipe = 0; + } +} + nsresult -nsFtpState::SendFTPCommand(const nsCSubstring& command) +nsFtpState::SendFTPCommand(nsCString& command) { NS_ASSERTION(mControlConnection, "null control connection"); @@ -1832,16 +2519,16 @@ nsFtpState::SendFTPCommand(const nsCSubstring& command) if (StringBeginsWith(command, NS_LITERAL_CSTRING("PASS "))) logcmd = "PASS xxxxx"; - LOG(("FTP:(%x) writing \"%s\"\n", this, logcmd.get())); + LOG(("(%x)(dwait=%d) Writing \"%s\"\n", this, mWaitingForDConn, logcmd.get())); nsCOMPtr ftpSink; mChannel->GetFTPEventSink(ftpSink); if (ftpSink) ftpSink->OnFTPControlLog(PR_FALSE, logcmd.get()); - if (mControlConnection) - return mControlConnection->Write(command); - + if (mControlConnection) { + return mControlConnection->Write(command, mWaitingForDConn); + } return NS_ERROR_FAILURE; } @@ -1858,11 +2545,9 @@ nsFtpState::ConvertFilespecToVMS(nsCString& fileString) // Get a writeable copy we can strtok with. fileStringCopy = fileString; t = nsCRT::strtok(fileStringCopy.BeginWriting(), "/", &nextToken); - if (t) - while (nsCRT::strtok(nextToken, "/", &nextToken)) - ntok++; // count number of terms (tokens) - LOG(("FTP:(%x) ConvertFilespecToVMS ntok: %d\n", this, ntok)); - LOG(("FTP:(%x) ConvertFilespecToVMS from: \"%s\"\n", this, fileString.get())); + if (t) while (nsCRT::strtok(nextToken, "/", &nextToken)) ntok++; // count number of terms (tokens) + LOG(("(%x) ConvertFilespecToVMS ntok: %d\n", this, ntok)); + LOG(("(%x) ConvertFilespecToVMS from: \"%s\"\n", this, fileString.get())); if (fileString.First() == '/') { // absolute filespec @@ -1876,13 +2561,15 @@ nsFtpState::ConvertFilespecToVMS(nsCString& fileString) // Just a slash fileString.Truncate(); fileString.AppendLiteral("[]"); - } else { + } + else { // just copy the name part (drop the leading slash) fileStringCopy = fileString; fileString = Substring(fileStringCopy, 1, fileStringCopy.Length()-1); } - } else { + } + else { // Get another copy since the last one was written to. fileStringCopy = fileString; fileString.Truncate(); @@ -1895,7 +2582,8 @@ nsFtpState::ConvertFilespecToVMS(nsCString& fileString) fileString.Append(nsCRT::strtok(nextToken, "/", &nextToken)); } - } else { + } + else { fileString.AppendLiteral("000000"); } fileString.Append(']'); @@ -1908,7 +2596,8 @@ nsFtpState::ConvertFilespecToVMS(nsCString& fileString) // a/b/c -> [.a.b]c if (ntok == 1) { // no slashes, just use the name as is - } else { + } + else { // Get another copy since the last one was written to. fileStringCopy = fileString; fileString.Truncate(); @@ -1926,7 +2615,7 @@ nsFtpState::ConvertFilespecToVMS(nsCString& fileString) fileString.Append(nsCRT::strtok(nextToken, "/", &nextToken)); } } - LOG(("FTP:(%x) ConvertFilespecToVMS to: \"%s\"\n", this, fileString.get())); + LOG(("(%x) ConvertFilespecToVMS to: \"%s\"\n", this, fileString.get())); } // Convert a unix-style dirspec to VMS format @@ -1937,7 +2626,7 @@ nsFtpState::ConvertFilespecToVMS(nsCString& fileString) void nsFtpState::ConvertDirspecToVMS(nsCString& dirSpec) { - LOG(("FTP:(%x) ConvertDirspecToVMS from: \"%s\"\n", this, dirSpec.get())); + LOG(("(%x) ConvertDirspecToVMS from: \"%s\"\n", this, dirSpec.get())); if (!dirSpec.IsEmpty()) { if (dirSpec.Last() != '/') dirSpec.Append('/'); @@ -1946,255 +2635,22 @@ nsFtpState::ConvertDirspecToVMS(nsCString& dirSpec) ConvertFilespecToVMS(dirSpec); dirSpec.Truncate(dirSpec.Length()-1); } - LOG(("FTP:(%x) ConvertDirspecToVMS to: \"%s\"\n", this, dirSpec.get())); + LOG(("(%x) ConvertDirspecToVMS to: \"%s\"\n", this, dirSpec.get())); } // Convert an absolute VMS style dirspec to UNIX format void nsFtpState::ConvertDirspecFromVMS(nsCString& dirSpec) { - LOG(("FTP:(%x) ConvertDirspecFromVMS from: \"%s\"\n", this, dirSpec.get())); + LOG(("(%x) ConvertDirspecFromVMS from: \"%s\"\n", this, dirSpec.get())); if (dirSpec.IsEmpty()) { dirSpec.Insert('.', 0); - } else { + } + else { dirSpec.Insert('/', 0); dirSpec.ReplaceSubstring(":[", "/"); dirSpec.ReplaceChar('.', '/'); dirSpec.ReplaceChar(']', '/'); } - LOG(("FTP:(%x) ConvertDirspecFromVMS to: \"%s\"\n", this, dirSpec.get())); -} - -//----------------------------------------------------------------------------- - -NS_IMETHODIMP -nsFtpState::OnTransportStatus(nsITransport *transport, nsresult status, - PRUint64 progress, PRUint64 progressMax) -{ - // Mix signals from both the control and data connections. - - // Ignore data transfer events on the control connection. - if (mControlConnection && transport == mControlConnection->Transport()) { - switch (status) { - case NS_NET_STATUS_RESOLVING_HOST: - case NS_NET_STATUS_CONNECTING_TO: - case NS_NET_STATUS_CONNECTED_TO: - break; - default: - return NS_OK; - } - } - - // Ignore the progressMax value from the socket. We know the true size of - // the file based on the response from our SIZE request. - mChannel->OnTransportStatus(nsnull, status, progress, mFileSize); - return NS_OK; -} - -//----------------------------------------------------------------------------- - - -NS_IMETHODIMP -nsFtpState::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, - nsCacheAccessMode access, - nsresult status) -{ - // We may have been closed while we were waiting for this cache entry. - if (IsClosed()) - return NS_OK; - - mCacheEntry = entry; - if (CanReadCacheEntry() && ReadCacheEntry()) { - mState = FTP_READ_CACHE; - } else { - Connect(); - } - return NS_OK; -} - -//----------------------------------------------------------------------------- - -NS_IMETHODIMP -nsFtpState::OnStartRequest(nsIRequest *request, nsISupports *context) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsFtpState::OnStopRequest(nsIRequest *request, nsISupports *context, - nsresult status) -{ - mUploadRequest = nsnull; - - // We're done uploading. Let our consumer know that we're done. - Close(); - return NS_OK; -} - -//----------------------------------------------------------------------------- - -NS_IMETHODIMP -nsFtpState::Available(PRUint32 *result) -{ - if (mDataStream) - return mDataStream->Available(result); - - return nsBaseContentStream::Available(result); -} - -NS_IMETHODIMP -nsFtpState::ReadSegments(nsWriteSegmentFun writer, void *closure, - PRUint32 count, PRUint32 *result) -{ - // Insert a thunk here so that the input stream passed to the writer is this - // input stream instead of mDataStream. - - if (mDataStream) { - nsWriteSegmentThunk thunk = { this, writer, closure }; - return mDataStream->ReadSegments(NS_WriteSegmentThunk, &thunk, count, - result); - } - - return nsBaseContentStream::ReadSegments(writer, closure, count, result); -} - -NS_IMETHODIMP -nsFtpState::CloseWithStatus(nsresult status) -{ - LOG(("FTP:(%p) close [%x]\n", this, status)); - - // Shutdown the control connection processing if we are being closed with an - // error. Note: This method may be called several times. - if (!IsClosed() && status != NS_BASE_STREAM_CLOSED && NS_FAILED(status)) { - if (NS_SUCCEEDED(mInternalError)) - mInternalError = status; - StopProcessing(); - } - - if (mUploadRequest) { - mUploadRequest->Cancel(NS_ERROR_ABORT); - mUploadRequest = nsnull; - } - - if (mDataTransport) { - // Shutdown the data transport. - mDataTransport->Close(NS_ERROR_ABORT); - mDataTransport = nsnull; - } - - mDataStream = nsnull; - mCacheEntry = nsnull; - - return nsBaseContentStream::CloseWithStatus(status); -} - -void -nsFtpState::OnCallbackPending() -{ - // If this is the first call, then see if we could use the cache. If we - // aren't going to read from (or write to) the cache, then just proceed to - // connect to the server. - - if (mState == FTP_INIT) { - if (CheckCache()) { - mState = FTP_WAIT_CACHE; - return; - } - if (mCacheEntry && CanReadCacheEntry() && ReadCacheEntry()) { - mState = FTP_READ_CACHE; - return; - } - Connect(); - } else if (mDataStream) { - mDataStream->AsyncWait(this, 0, 0, CallbackTarget()); - } -} - -PRBool -nsFtpState::ReadCacheEntry() -{ - NS_ASSERTION(mCacheEntry, "should have a cache entry"); - - // make sure the channel knows wassup - SetContentType(); - - nsXPIDLCString serverType; - mCacheEntry->GetMetaDataElement("servertype", getter_Copies(serverType)); - nsCAutoString serverNum(serverType.get()); - PRInt32 err; - mServerType = serverNum.ToInteger(&err); - - mChannel->PushStreamConverter("text/ftp-dir", - APPLICATION_HTTP_INDEX_FORMAT); - - mChannel->SetEntityID(EmptyCString()); - - if (NS_FAILED(OpenCacheDataStream())) - return PR_FALSE; - - if (HasPendingCallback()) - mDataStream->AsyncWait(this, 0, 0, CallbackTarget()); - - return PR_TRUE; -} - -PRBool -nsFtpState::CheckCache() -{ - // This function is responsible for setting mCacheEntry if there is a cache - // entry that we can use. It returns true if we end up waiting for access - // to the cache. - - // In some cases, we don't want to use the cache: - if (mChannel->UploadStream() || mChannel->ResumeRequested()) - return PR_FALSE; - - nsCOMPtr cache = do_GetService(NS_CACHESERVICE_CONTRACTID); - if (!cache) - return PR_FALSE; - - nsCOMPtr session; - cache->CreateSession("FTP", - nsICache::STORE_ANYWHERE, - nsICache::STREAM_BASED, - getter_AddRefs(session)); - if (!session) - return PR_FALSE; - session->SetDoomEntriesIfExpired(PR_FALSE); - - // Set cache access requested: - nsCacheAccessMode accessReq; - if (NS_IsOffline()) { - accessReq = nsICache::ACCESS_READ; // can only read - } else if (mChannel->HasLoadFlag(nsIRequest::LOAD_BYPASS_CACHE)) { - accessReq = nsICache::ACCESS_WRITE; // replace cache entry - } else { - accessReq = nsICache::ACCESS_READ_WRITE; // normal browsing - } - - // Check to see if we are not allowed to write to the cache: - if (mChannel->HasLoadFlag(nsIRequest::INHIBIT_CACHING)) { - accessReq &= ~nsICache::ACCESS_WRITE; - if (accessReq == nsICache::ACCESS_NONE) - return PR_FALSE; - } - - // Generate cache key (remove trailing #ref if any): - nsCAutoString key; - mChannel->URI()->GetAsciiSpec(key); - PRInt32 pos = key.RFindChar('#'); - if (pos != kNotFound) - key.Truncate(pos); - NS_ENSURE_FALSE(key.IsEmpty(), PR_FALSE); - - // Try to open a cache entry immediately, but if the cache entry is busy, - // then wait for it to be available. - - session->OpenCacheEntry(key, accessReq, PR_FALSE, - getter_AddRefs(mCacheEntry)); - if (mCacheEntry) - return PR_FALSE; // great, we're ready to proceed! - - nsresult rv = session->AsyncOpenCacheEntry(key, accessReq, this); - return NS_SUCCEEDED(rv); + LOG(("(%x) ConvertDirspecFromVMS to: \"%s\"\n", this, dirSpec.get())); } diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.h b/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.h index 31c20318b53..735970b8272 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.h +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.h @@ -40,9 +40,6 @@ #define __nsFtpState__h_ #include "ftpCore.h" -#include "nsFTPChannel.h" -#include "nsBaseContentStream.h" - #include "nsInt64.h" #include "nsIThread.h" #include "nsIRunnable.h" @@ -50,14 +47,13 @@ #include "nsISocketTransport.h" #include "nsIServiceManager.h" #include "nsIStreamListener.h" -#include "nsICacheListener.h" #include "nsIURI.h" #include "prtime.h" #include "nsString.h" #include "nsIFTPChannel.h" #include "nsIProtocolHandler.h" #include "nsCOMPtr.h" -#include "nsIAsyncInputStream.h" +#include "nsIInputStream.h" #include "nsIOutputStream.h" #include "nsAutoLock.h" #include "nsAutoPtr.h" @@ -70,7 +66,6 @@ #include "nsFtpControlConnection.h" #include "nsICacheEntryDescriptor.h" -#include "nsICacheListener.h" // ftp server types #define FTP_GENERIC_TYPE 0 @@ -83,9 +78,6 @@ typedef enum _FTP_STATE { /////////////////////// //// Internal states - FTP_INIT, - FTP_WAIT_CACHE, - FTP_READ_CACHE, FTP_COMMAND_CONNECT, FTP_READ_BUF, FTP_ERROR, @@ -112,46 +104,38 @@ typedef enum _FTP_STATE { // higher level ftp actions typedef enum _FTP_ACTION {GET, PUT} FTP_ACTION; -class nsFtpChannel; +class DataRequestForwarder; +class nsFTPChannel; -// The nsFtpState object is the content stream for the channel. It implements -// nsIInputStreamCallback, so it can read data from the control connection. It -// implements nsITransportEventSink so it can mix status events from both the -// control connection and the data connection. - -class nsFtpState : public nsBaseContentStream, - public nsIInputStreamCallback, - public nsITransportEventSink, - public nsICacheListener, - public nsIRequestObserver, - public nsFtpControlConnectionListener { +class nsFtpState : public nsIStreamListener, + public nsIRequest { public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIINPUTSTREAMCALLBACK - NS_DECL_NSITRANSPORTEVENTSINK - NS_DECL_NSICACHELISTENER + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER - - // Override input stream methods: - NS_IMETHOD CloseWithStatus(nsresult status); - NS_IMETHOD Available(PRUint32 *result); - NS_IMETHOD ReadSegments(nsWriteSegmentFun fun, void *closure, - PRUint32 count, PRUint32 *result); - - // nsFtpControlConnectionListener methods: - virtual void OnControlDataAvailable(const char *data, PRUint32 dataLen); - virtual void OnControlError(nsresult status); + NS_DECL_NSIREQUEST nsFtpState(); - nsresult Init(nsFtpChannel *channel); - -protected: - // Notification from nsBaseContentStream::AsyncWait - virtual void OnCallbackPending(); - -private: virtual ~nsFtpState(); + nsresult Init(nsFTPChannel *aChannel, + nsICacheEntryDescriptor* cacheEntry, + nsIProxyInfo* proxyInfo, + PRUint64 startPos, + const nsACString& entity); + + // use this to provide a stream to be written to the server. + nsresult SetWriteStream(nsIInputStream* aInStream); + + nsresult GetEntityID(nsACString& aEntityID); + + nsresult Connect(); + + // lets the data forwarder tell us when the the data pipe has been created + // and when the data pipe has finished. + void DataConnectionEstablished(); + void DataConnectionComplete(); +private: /////////////////////////////////// // BEGIN: STATE METHODS nsresult S_user(); FTP_STATE R_user(); @@ -181,60 +165,13 @@ private: void KillControlConnection(); nsresult StopProcessing(); nsresult EstablishControlConnection(); - nsresult SendFTPCommand(const nsCSubstring& command); + nsresult SendFTPCommand(nsCString& command); void ConvertFilespecToVMS(nsCString& fileSpec); void ConvertDirspecToVMS(nsCString& fileSpec); void ConvertDirspecFromVMS(nsCString& fileSpec); nsresult BuildStreamConverter(nsIStreamListener** convertStreamListener); nsresult SetContentType(); - - /** - * This method is called to kick-off the FTP state machine. mState is - * reset to FTP_COMMAND_CONNECT, and the FTP state machine progresses from - * there. This method is initially called (indirectly) from the channel's - * AsyncOpen implementation. - */ - void Connect(); - - /** - * This method opens a cache entry for reading or writing depending on the - * state of the channel and of the system (e.g., opened for reading if we - * are offline). This method is responsible for setting mCacheEntry if - * there is a cache entry that can be used. It returns true if it ends up - * waiting (asynchronously) for access to the cache entry. In that case, - * the nsFtpState's OnCacheEntryAvailable method will be called once the - * cache entry is available or if an error occurs. - */ - PRBool CheckCache(); - - /** - * This method returns true if the data for this URL can be read from the - * cache. This method assumes that mCacheEntry is non-null. - */ - PRBool CanReadCacheEntry(); - - /** - * This method causes the cache entry to be read. Data from the cache - * entry will be fed to the channel's listener. This method returns true - * if successfully reading from the cache. This method assumes that - * mCacheEntry is non-null and opened with read access. - */ - PRBool ReadCacheEntry(); - - /** - * This method configures mDataStream with an asynchronous input stream to - * the cache entry. The cache entry is read on a background thread. This - * method assumes that mCacheEntry is non-null and opened with read access. - */ - nsresult OpenCacheDataStream(); - - /** - * This method inserts the cache entry's output stream into the stream - * listener chain for the FTP channel. As a result, the cache entry - * receives data as data is pushed to the channel's listener. This method - * assumes that mCacheEntry is non-null and opened with write access. - */ - nsresult InstallCacheListener(); + PRBool CanReadEntry(); /////////////////////////////////// // Private members @@ -244,22 +181,27 @@ private: FTP_STATE mNextState; // the next state PRPackedBool mKeepRunning; // thread event loop boolean PRInt32 mResponseCode; // the last command response code - nsCString mResponseMsg; // the last command response text + nsCAutoString mResponseMsg; // the last command response text // ****** channel/transport/stream vars - nsRefPtr mControlConnection; // cacheable control connection (owns mCPipe) + nsFtpControlConnection* mControlConnection; // cacheable control connection (owns mCPipe) PRPackedBool mReceivedControlData; PRPackedBool mTryingCachedControl; // retrying the password + PRPackedBool mWaitingForDConn; // Are we wait for a data connection PRPackedBool mRETRFailed; // Did we already try a RETR and it failed? + nsCOMPtr mDPipe; // the data transport + nsCOMPtr mDPipeRequest; + DataRequestForwarder* mDRequestForwarder; PRUint64 mFileSize; nsCString mModTime; // ****** consumer vars - nsRefPtr mChannel; // our owning FTP channel we pass through our events + nsRefPtr mChannel; // our owning FTP channel we pass through our events nsCOMPtr mProxyInfo; // ****** connection cache vars PRInt32 mServerType; // What kind of server are we talking to + PRPackedBool mList; // Use LIST instead of NLST // ****** protocol interpretation related state vars nsString mUsername; // username @@ -270,28 +212,35 @@ private: nsresult mInternalError; // represents internal state errors // ****** URI vars + nsCOMPtr mURL; // the uri we're connecting to PRInt32 mPort; // the port to connect to nsString mFilename; // url filename (if any) nsCString mPath; // the url's path nsCString mPwd; // login Path // ****** other vars - nsCOMPtr mDataTransport; - nsCOMPtr mDataStream; - nsCOMPtr mUploadRequest; - PRPackedBool mIPv6Checked; + PRUint8 mSuspendCount;// number of times we've been suspended. + PRUint32 mBufferSegmentSize; + PRUint32 mBufferMaxSize; + PRLock *mLock; + nsCOMPtr mWriteStream; // This stream is written to the server. + PRUint32 mWriteCount; + PRPackedBool mIPv6Checked; static PRUint32 mSessionStartTime; - nsAutoArrayPtr mIPv6ServerAddress; // Server IPv6 address; null if server not IPv6 + char *mIPv6ServerAddress; // Server IPv6 address; null if server not IPv6 // ***** control read gvars nsresult mControlStatus; - nsCString mControlReadCarryOverBuf; + nsCAutoString mControlReadCarryOverBuf; nsCOMPtr mCacheEntry; + nsUint64 mStartPos; nsCString mSuppliedEntityID; + nsCString mEntityID; }; + #endif //__nsFtpState__h_ diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp b/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp index cf16e12367c..b00db3cc746 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp @@ -47,172 +47,130 @@ #include "nsEventQueueUtils.h" #include "nsCRT.h" + #if defined(PR_LOGGING) extern PRLogModuleInfo* gFTPLog; -#endif #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args) #define LOG_ALWAYS(args) PR_LOG(gFTPLog, PR_LOG_ALWAYS, args) +#else +#define LOG(args) +#define LOG_ALWAYS(args) +#endif + + +static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); // // nsFtpControlConnection implementation ... // -NS_IMPL_ISUPPORTS1(nsFtpControlConnection, nsIInputStreamCallback) +NS_IMPL_THREADSAFE_ISUPPORTS2(nsFtpControlConnection, + nsIStreamListener, + nsIRequestObserver) -NS_IMETHODIMP -nsFtpControlConnection::OnInputStreamReady(nsIAsyncInputStream *stream) +nsFtpControlConnection::nsFtpControlConnection(const char* host, PRUint32 port) + : mServerType(0), mPort(port) { - char data[4096]; + LOG_ALWAYS(("(%x) nsFtpControlConnection created", this)); - // Consume data whether we have a listener or not. - PRUint32 avail; - nsresult rv = stream->Available(&avail); - if (NS_SUCCEEDED(rv)) { - if (avail > sizeof(data)) - avail = sizeof(data); - - PRUint32 n; - rv = stream->Read(data, avail, &n); - if (NS_SUCCEEDED(rv) && n != avail) - avail = n; - } - - // It's important that we null out mListener before calling one of its - // methods as it may call WaitData, which would queue up another read. - - nsRefPtr listener; - listener.swap(mListener); - - if (!listener) - return NS_OK; - - if (NS_FAILED(rv)) { - listener->OnControlError(rv); - } else { - listener->OnControlDataAvailable(data, avail); - } - - return NS_OK; -} - -nsFtpControlConnection::nsFtpControlConnection(const nsCSubstring& host, PRUint32 port) - : mServerType(0), mHost(host), mPort(port) -{ - LOG_ALWAYS(("FTP:CC created @%p", this)); + mHost.Assign(host); } nsFtpControlConnection::~nsFtpControlConnection() { - LOG_ALWAYS(("FTP:CC destroyed @%p", this)); + LOG_ALWAYS(("(%x) nsFtpControlConnection destroyed", this)); } PRBool nsFtpControlConnection::IsAlive() { - if (!mSocket) + if (!mCPipe) return PR_FALSE; PRBool isAlive = PR_FALSE; - mSocket->IsAlive(&isAlive); + mCPipe->IsAlive(&isAlive); return isAlive; } nsresult nsFtpControlConnection::Connect(nsIProxyInfo* proxyInfo, nsITransportEventSink* eventSink) { - if (mSocket) - return NS_OK; - - // build our own nsresult rv; - nsCOMPtr sts = - do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); - if (NS_FAILED(rv)) - return rv; - rv = sts->CreateTransport(nsnull, 0, mHost, mPort, proxyInfo, - getter_AddRefs(mSocket)); // the command transport - if (NS_FAILED(rv)) - return rv; + if (!mCPipe) { + // build our own + nsCOMPtr sts = + do_GetService(kSocketTransportServiceCID, &rv); - // proxy transport events back to current thread - if (eventSink) { - nsCOMPtr eventQ; - rv = NS_GetCurrentEventQ(getter_AddRefs(eventQ)); - if (NS_SUCCEEDED(rv)) - mSocket->SetEventSink(eventSink, eventQ); + rv = sts->CreateTransport(nsnull, 0, mHost, mPort, proxyInfo, + getter_AddRefs(mCPipe)); // the command transport + if (NS_FAILED(rv)) return rv; + + // proxy transport events back to current thread + if (eventSink) { + nsCOMPtr eventQ; + rv = NS_GetCurrentEventQ(getter_AddRefs(eventQ)); + if (NS_SUCCEEDED(rv)) + mCPipe->SetEventSink(eventSink, eventQ); + } + + // open buffered, blocking output stream to socket. so long as commands + // do not exceed 1024 bytes in length, the writing thread (the main thread) + // will not block. this should be OK. + rv = mCPipe->OpenOutputStream(nsITransport::OPEN_BLOCKING, 1024, 1, + getter_AddRefs(mOutStream)); + if (NS_FAILED(rv)) return rv; + + // open buffered, non-blocking/asynchronous input stream to socket. + nsCOMPtr inStream; + rv = mCPipe->OpenInputStream(0, + FTP_COMMAND_CHANNEL_SEG_SIZE, + FTP_COMMAND_CHANNEL_SEG_COUNT, + getter_AddRefs(inStream)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr pump; + rv = NS_NewInputStreamPump(getter_AddRefs(pump), inStream); + if (NS_FAILED(rv)) return rv; + + // get the ball rolling by reading on the control socket. + rv = pump->AsyncRead(NS_STATIC_CAST(nsIStreamListener*, this), nsnull); + if (NS_FAILED(rv)) return rv; + + // cyclic reference! + mReadRequest = pump; } - - // open buffered, blocking output stream to socket. so long as commands - // do not exceed 1024 bytes in length, the writing thread (the main thread) - // will not block. this should be OK. - rv = mSocket->OpenOutputStream(nsITransport::OPEN_BLOCKING, 1024, 1, - getter_AddRefs(mSocketOutput)); - if (NS_FAILED(rv)) - return rv; - - // open buffered, non-blocking/asynchronous input stream to socket. - nsCOMPtr inStream; - rv = mSocket->OpenInputStream(0, - FTP_COMMAND_CHANNEL_SEG_SIZE, - FTP_COMMAND_CHANNEL_SEG_COUNT, - getter_AddRefs(inStream)); - if (NS_SUCCEEDED(rv)) - mSocketInput = do_QueryInterface(inStream); - - return rv; -} - -nsresult -nsFtpControlConnection::WaitData(nsFtpControlConnectionListener *listener) -{ - LOG(("FTP:(%p) wait data [listener=%p]\n", this, listener)); - - // If listener is null, then simply disconnect the listener. Otherwise, - // ensure that we are listening. - if (!listener) { - mListener = nsnull; - return NS_OK; - } - - NS_ENSURE_STATE(mSocketInput); - - nsCOMPtr eventQ; - NS_GetCurrentEventQ(getter_AddRefs(eventQ)); - NS_ENSURE_STATE(eventQ); - - mListener = listener; - return mSocketInput->AsyncWait(this, 0, 0, eventQ); + return NS_OK; } nsresult nsFtpControlConnection::Disconnect(nsresult status) { - if (!mSocket) - return NS_OK; // already disconnected + if (!mCPipe) return NS_ERROR_FAILURE; - LOG_ALWAYS(("FTP:(%p) CC disconnecting (%x)", this, status)); + LOG_ALWAYS(("(%x) nsFtpControlConnection disconnecting (%x)", this, status)); if (NS_FAILED(status)) { // break cyclic reference! - mSocket->Close(status); - mSocket = 0; - mSocketInput->AsyncWait(nsnull, 0, 0, nsnull); // clear any observer - mSocketInput = nsnull; - mSocketOutput = nsnull; + mOutStream = 0; + mReadRequest->Cancel(status); + mReadRequest = 0; + mCPipe->Close(status); + mCPipe = 0; } return NS_OK; } nsresult -nsFtpControlConnection::Write(const nsCSubstring& command) +nsFtpControlConnection::Write(nsCString& command, PRBool suspend) { - NS_ENSURE_STATE(mSocketOutput); + if (!mCPipe) + return NS_ERROR_FAILURE; PRUint32 len = command.Length(); PRUint32 cnt; - nsresult rv = mSocketOutput->Write(command.Data(), len, &cnt); + nsresult rv = mOutStream->Write(command.get(), len, &cnt); if (NS_FAILED(rv)) return rv; @@ -220,5 +178,59 @@ nsFtpControlConnection::Write(const nsCSubstring& command) if (len != cnt) return NS_ERROR_FAILURE; + if (suspend) + return NS_OK; + return NS_OK; } + +NS_IMETHODIMP +nsFtpControlConnection::OnStartRequest(nsIRequest *request, nsISupports *aContext) +{ + if (!mCPipe) + return NS_OK; + + if (!mListener) + return NS_OK; + + // In case our listener tries to remove itself via SetStreamListener(nsnull), + // we need to keep an extra reference to it on the stack. + nsCOMPtr deathGrip = mListener; + return mListener->OnStartRequest(request, aContext); +} + +NS_IMETHODIMP +nsFtpControlConnection::OnStopRequest(nsIRequest *request, nsISupports *aContext, + nsresult aStatus) +{ + if (!mCPipe) + return NS_OK; + + if (!mListener) + return NS_OK; + + // In case our listener tries to remove itself via SetStreamListener(nsnull), + // we need to keep an extra reference to it on the stack. + nsCOMPtr deathGrip = mListener; + return mListener->OnStopRequest(request, aContext, aStatus); +} + +NS_IMETHODIMP +nsFtpControlConnection::OnDataAvailable(nsIRequest *request, + nsISupports *aContext, + nsIInputStream *aInStream, + PRUint32 aOffset, + PRUint32 aCount) +{ + if (!mCPipe) + return NS_OK; + + if (!mListener) + return NS_OK; + + // In case our listener tries to remove itself via SetStreamListener(nsnull), + // we need to keep an extra reference to it on the stack. + nsCOMPtr deathGrip = mListener; + return mListener->OnDataAvailable(request, aContext, aInStream, + aOffset, aCount); +} diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.h b/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.h index d89c82f7a76..c601906ac83 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.h +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.h @@ -46,57 +46,33 @@ #include "nsIRequest.h" #include "nsISocketTransport.h" #include "nsIOutputStream.h" -#include "nsIAsyncInputStream.h" +#include "nsIInputStream.h" #include "nsAutoLock.h" -#include "nsAutoPtr.h" #include "nsString.h" class nsIProxyInfo; class nsITransportEventSink; -class nsFtpControlConnectionListener : public nsISupports { -public: - /** - * Called when a chunk of data arrives on the control connection. - * @param data - * The new data or null if an error occured. - * @param dataLen - * The data length in bytes. - */ - virtual void OnControlDataAvailable(const char *data, PRUint32 dataLen) = 0; - - /** - * Called when an error occurs on the control connection. - * @param status - * A failure code providing more info about the error. - */ - virtual void OnControlError(nsresult status) = 0; -}; - -class nsFtpControlConnection : public nsIInputStreamCallback +class nsFtpControlConnection : public nsIStreamListener { public: NS_DECL_ISUPPORTS - NS_DECL_NSIINPUTSTREAMCALLBACK + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIREQUESTOBSERVER - nsFtpControlConnection(const nsCSubstring& host, PRUint32 port); + nsFtpControlConnection(const char* host, PRUint32 port); ~nsFtpControlConnection(); nsresult Connect(nsIProxyInfo* proxyInfo, nsITransportEventSink* eventSink); nsresult Disconnect(nsresult status); - nsresult Write(const nsCSubstring& command); + nsresult Write(nsCString& command, PRBool suspend); PRBool IsAlive(); - nsITransport *Transport() { return mSocket; } + nsIRequest *ReadRequest() { return mReadRequest; } + nsITransport *Transport() { return mCPipe; } - /** - * Call this function to be notified asynchronously when there is data - * available for the socket. The listener passed to this method replaces - * any existing listener, and the listener can be null to disconnect the - * previous listener. - */ - nsresult WaitData(nsFtpControlConnectionListener *listener); + void SetStreamListener(nsIStreamListener *l) { mListener = l; } PRUint32 mServerType; // what kind of server is it. nsString mPassword; @@ -107,11 +83,10 @@ private: nsCString mHost; PRUint32 mPort; - nsCOMPtr mSocket; - nsCOMPtr mSocketOutput; - nsCOMPtr mSocketInput; - - nsRefPtr mListener; + nsCOMPtr mReadRequest; + nsCOMPtr mCPipe; + nsCOMPtr mOutStream; + nsCOMPtr mListener; }; #endif diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.cpp b/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.cpp index c64b55f8340..efa771e4c58 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.cpp @@ -73,7 +73,7 @@ // // To enable logging (see prlog.h for full details): // -// set NSPR_LOG_MODULES=nsFtp:5 +// set NSPR_LOG_MODULES=nsFTPProtocol:5 // set NSPR_LOG_FILE=nspr.log // // this enables PR_LOG_DEBUG level information and places all output in @@ -86,7 +86,7 @@ PRLogModuleInfo* gFTPLog = nsnull; //----------------------------------------------------------------------------- #define IDLE_TIMEOUT_PREF "network.ftp.idleConnectionTimeout" -#define IDLE_CONNECTION_LIMIT 8 /* TODO pref me */ +#define IDLE_CONNECTION_LIMIT 8 /* XXX pref me */ static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID); static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID); @@ -99,17 +99,16 @@ nsFtpProtocolHandler::nsFtpProtocolHandler() : mIdleTimeout(-1) { #if defined(PR_LOGGING) - if (!gFTPLog) - gFTPLog = PR_NewLogModule("nsFtp"); + if (!gFTPLog) gFTPLog = PR_NewLogModule("nsFTPProtocol"); #endif - LOG(("FTP:creating handler @%x\n", this)); + LOG(("Creating nsFtpProtocolHandler @%x\n", this)); gFtpHandler = this; } nsFtpProtocolHandler::~nsFtpProtocolHandler() { - LOG(("FTP:destroying handler @%x\n", this)); + LOG(("Destroying nsFtpProtocolHandler @%x\n", this)); NS_ASSERTION(mRootConnectionList.Count() == 0, "why wasn't Observe called?"); @@ -144,7 +143,7 @@ nsFtpProtocolHandler::Init() if (observerService) observerService->AddObserver(this, "network:offline-about-to-go-offline", - PR_TRUE); + PR_FALSE); return NS_OK; } @@ -212,17 +211,27 @@ nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result) } NS_IMETHODIMP -nsFtpProtocolHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* proxyInfo, - nsIChannel* *result) +nsFtpProtocolHandler::NewProxiedChannel(nsIURI* url, nsIProxyInfo* proxyInfo, nsIChannel* *result) { - NS_ENSURE_ARG_POINTER(uri); - nsFtpChannel *channel = new nsFtpChannel(uri, proxyInfo); + NS_ENSURE_ARG_POINTER(url); + nsFTPChannel *channel = new nsFTPChannel(); if (!channel) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(channel); - nsresult rv = channel->Init(); + nsCOMPtr cache = do_GetService(kCacheServiceCID); + if (cache) { + cache->CreateSession("FTP", + nsICache::STORE_ANYWHERE, + nsICache::STREAM_BASED, + getter_AddRefs(mCacheSession)); + if (mCacheSession) + mCacheSession->SetDoomEntriesIfExpired(PR_FALSE); + } + + nsresult rv = channel->Init(url, proxyInfo, mCacheSession); if (NS_FAILED(rv)) { + LOG(("nsFtpProtocolHandler::NewChannel() FAILED\n")); NS_RELEASE(channel); return rv; } @@ -243,7 +252,7 @@ nsFtpProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retva void nsFtpProtocolHandler::Timeout(nsITimer *aTimer, void *aClosure) { - LOG(("FTP:timeout reached for %p\n", aClosure)); + LOG(("Timeout reached for %0x\n", aClosure)); PRBool found = gFtpHandler->mRootConnectionList.RemoveElement(aClosure); if (!found) { @@ -266,7 +275,7 @@ nsFtpProtocolHandler::RemoveConnection(nsIURI *aKey, nsFtpControlConnection* *_r nsCAutoString spec; aKey->GetPrePath(spec); - LOG(("FTP:removing connection for %s\n", spec.get())); + LOG(("Removing connection for %s\n", spec.get())); timerStruct* ts = nsnull; PRInt32 i; @@ -301,7 +310,7 @@ nsFtpProtocolHandler::InsertConnection(nsIURI *aKey, nsFtpControlConnection *aCo nsCAutoString spec; aKey->GetPrePath(spec); - LOG(("FTP:inserting connection for %s\n", spec.get())); + LOG(("Inserting connection for %s\n", spec.get())); nsresult rv; nsCOMPtr timer = do_CreateInstance("@mozilla.org/timer;1", &rv); @@ -364,7 +373,7 @@ nsFtpProtocolHandler::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData) { - LOG(("FTP:observing [%s]\n", aTopic)); + LOG(("nsFtpProtocolHandler::Observe [topic=%s]\n", aTopic)); if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { nsCOMPtr branch = do_QueryInterface(aSubject); @@ -376,12 +385,14 @@ nsFtpProtocolHandler::Observe(nsISupports *aSubject, nsresult rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &timeout); if (NS_SUCCEEDED(rv)) mIdleTimeout = timeout; - } else if (!strcmp(aTopic, "network:offline-about-to-go-offline")) { + } + else if (!strcmp(aTopic, "network:offline-about-to-go-offline")) { PRInt32 i; for (i=0;i mChannel; nsCOMPtr mSocket; @@ -121,15 +119,14 @@ NS_IMETHODIMP nsGopherContentStream::ReadSegments(nsWriteSegmentFun writer, void *closure, PRUint32 count, PRUint32 *result) { - // Insert a thunk here so that the input stream passed to the writer is - // this input stream instead of mSocketInput. - if (mSocketInput) { - nsWriteSegmentThunk thunk = { this, writer, closure }; - return mSocketInput->ReadSegments(NS_WriteSegmentThunk, &thunk, count, - result); - } + // TODO: Insert a thunk here so that the input stream passed to the writer + // is this input stream instead of mSocketInput. This thunk should be + // some generic thunk available from nsStreamUtils. + if (mSocketInput) + return mSocketInput->ReadSegments(writer, closure, count, result); - return nsBaseContentStream::ReadSegments(writer, closure, count, result); + // No data yet + return NS_BASE_STREAM_WOULD_BLOCK; } NS_IMETHODIMP @@ -144,6 +141,26 @@ nsGopherContentStream::CloseWithStatus(nsresult status) return nsBaseContentStream::CloseWithStatus(status); } +NS_IMETHODIMP +nsGopherContentStream::AsyncWait(nsIInputStreamCallback *callback, PRUint32 flags, + PRUint32 count, nsIEventTarget *target) +{ + nsresult rv = nsBaseContentStream::AsyncWait(callback, flags, count, target); + if (NS_FAILED(rv) && IsClosed()) + return rv; + + // We have a callback, so failure means we should close the stream. + if (!mSocket) { + rv = OpenSocket(target); + } else if (mSocketInput) { + rv = mSocketInput->AsyncWait(this, 0, 0, target); + } + if (NS_FAILED(rv)) + CloseWithStatus(rv); + + return NS_OK; +} + NS_IMETHODIMP nsGopherContentStream::OnInputStreamReady(nsIAsyncInputStream *stream) { @@ -166,22 +183,6 @@ nsGopherContentStream::OnOutputStreamReady(nsIAsyncOutputStream *stream) return NS_OK; } -void -nsGopherContentStream::OnCallbackPending() -{ - nsresult rv; - - // We have a callback, so failure means we should close the stream. - if (!mSocket) { - rv = OpenSocket(CallbackTarget()); - } else if (mSocketInput) { - rv = mSocketInput->AsyncWait(this, 0, 0, CallbackTarget()); - } - - if (NS_FAILED(rv)) - CloseWithStatus(rv); -} - nsresult nsGopherContentStream::OpenSocket(nsIEventTarget *target) { diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp index 7884848cb41..f9a196b30da 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -3646,7 +3646,7 @@ nsHttpChannel::SetUploadStream(nsIInputStream *stream, const nsACString &content mUploadStreamHasHeaders = PR_FALSE; mRequestHead.SetMethod(nsHttp::Get); // revert to GET request } - mUploadStream = stream; + mUploadStream = stream; return NS_OK; } diff --git a/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.cpp b/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.cpp index e0e15883225..67640355f0e 100644 --- a/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.cpp +++ b/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.cpp @@ -97,12 +97,28 @@ NS_IMETHODIMP nsFTPDirListingConv::AsyncConvertData(const char *aFromType, const char *aToType, nsIStreamListener *aListener, nsISupports *aCtxt) { NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into FTP dir listing converter"); + nsresult rv; // hook up our final listener. this guy gets the various On*() calls we want to throw // at him. mFinalListener = aListener; NS_ADDREF(mFinalListener); + // we need our own channel that represents the content-type of the + // converted data. + NS_ASSERTION(aCtxt, "FTP dir listing needs a context (the uri)"); + nsIURI *uri; + rv = aCtxt->QueryInterface(NS_GET_IID(nsIURI), (void**)&uri); + if (NS_FAILED(rv)) return rv; + + // XXX this seems really wrong!! + rv = NS_NewInputStreamChannel(&mPartChannel, + uri, + nsnull, + NS_LITERAL_CSTRING(APPLICATION_HTTP_INDEX_FORMAT)); + NS_RELEASE(uri); + if (NS_FAILED(rv)) return rv; + PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("nsFTPDirListingConv::AsyncConvertData() converting FROM raw, TO application/http-index-format\n")); @@ -196,7 +212,7 @@ nsFTPDirListingConv::OnDataAvailable(nsIRequest* request, nsISupports *ctxt, rv = NS_NewCStringInputStream(getter_AddRefs(inputData), indexFormat); NS_ENSURE_SUCCESS(rv, rv); - rv = mFinalListener->OnDataAvailable(request, ctxt, inputData, 0, indexFormat.Length()); + rv = mFinalListener->OnDataAvailable(mPartChannel, ctxt, inputData, 0, indexFormat.Length()); return rv; } @@ -207,7 +223,7 @@ NS_IMETHODIMP nsFTPDirListingConv::OnStartRequest(nsIRequest* request, nsISupports *ctxt) { // we don't care about start. move along... but start masqeurading // as the http-index channel now. - return mFinalListener->OnStartRequest(request, ctxt); + return mFinalListener->OnStartRequest(mPartChannel, ctxt); } NS_IMETHODIMP @@ -215,18 +231,33 @@ nsFTPDirListingConv::OnStopRequest(nsIRequest* request, nsISupports *ctxt, nsresult aStatus) { // we don't care about stop. move along... - return mFinalListener->OnStopRequest(request, ctxt, aStatus); + nsresult rv; + + nsCOMPtr channel = do_QueryInterface(request, &rv); + if (NS_FAILED(rv)) return rv; + + + nsCOMPtr loadgroup; + rv = channel->GetLoadGroup(getter_AddRefs(loadgroup)); + if (NS_FAILED(rv)) return rv; + + if (loadgroup) + (void)loadgroup->RemoveRequest(mPartChannel, nsnull, aStatus); + + return mFinalListener->OnStopRequest(mPartChannel, ctxt, aStatus); } // nsFTPDirListingConv methods nsFTPDirListingConv::nsFTPDirListingConv() { mFinalListener = nsnull; + mPartChannel = nsnull; mSentHeading = PR_FALSE; } nsFTPDirListingConv::~nsFTPDirListingConv() { NS_IF_RELEASE(mFinalListener); + NS_IF_RELEASE(mPartChannel); } nsresult diff --git a/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.h b/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.h index 257bf287caa..1c6cc3f0b0c 100644 --- a/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.h +++ b/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.h @@ -81,6 +81,8 @@ private: PRBool mSentHeading; // have we sent 100, 101, 200, and 300 lines yet? nsIStreamListener *mFinalListener; // this guy gets the converted data via his OnDataAvailable() + nsIChannel *mPartChannel; // the channel for the given part we're processing. + // one channel per part. }; #endif /* __nsftpdirlistingdconv__h__ */ diff --git a/mozilla/xpcom/build/dlldeps.cpp b/mozilla/xpcom/build/dlldeps.cpp index 812b7ec8af2..476d5c098f5 100644 --- a/mozilla/xpcom/build/dlldeps.cpp +++ b/mozilla/xpcom/build/dlldeps.cpp @@ -129,7 +129,6 @@ void XXXNeverCalled() NS_CopySegmentToStream(nsnull, nsnull, nsnull, 0, 0, nsnull); NS_CopySegmentToBuffer(nsnull, nsnull, nsnull, 0, 0, nsnull); NS_DiscardSegment(nsnull, nsnull, nsnull, 0, 0, nsnull); - NS_WriteSegmentThunk(nsnull, nsnull, nsnull, 0, 0, 0); NS_NewByteInputStream(nsnull, nsnull, 0, NS_ASSIGNMENT_COPY); NS_NewCStringInputStream(nsnull, nsCString()); NS_NewStringInputStream(nsnull, nsString()); diff --git a/mozilla/xpcom/io/nsIAsyncInputStream.idl b/mozilla/xpcom/io/nsIAsyncInputStream.idl index bb9a46c3cdd..6ce0bb30fff 100644 --- a/mozilla/xpcom/io/nsIAsyncInputStream.idl +++ b/mozilla/xpcom/io/nsIAsyncInputStream.idl @@ -91,8 +91,7 @@ interface nsIAsyncInputStream : nsIInputStream * event will be dispatched when the stream becomes readable or closed. * * @param aCallback - * This object is notified when the stream becomes ready. This - * parameter may be null to clear an existing callback. + * This object is notified when the stream becomes ready. * @param aFlags * This parameter specifies optional flags passed in to configure * the behavior of this method. Pass zero to specify no flags. diff --git a/mozilla/xpcom/io/nsIAsyncOutputStream.idl b/mozilla/xpcom/io/nsIAsyncOutputStream.idl index f8fbded2f62..b0dd9ec0323 100644 --- a/mozilla/xpcom/io/nsIAsyncOutputStream.idl +++ b/mozilla/xpcom/io/nsIAsyncOutputStream.idl @@ -91,8 +91,7 @@ interface nsIAsyncOutputStream : nsIOutputStream * event will be dispatched when the stream becomes writable or closed. * * @param aCallback - * This object is notified when the stream becomes ready. This - * parameter may be null to clear an existing callback. + * This object is notified when the stream becomes ready. * @param aFlags * This parameter specifies optional flags passed in to configure * the behavior of this method. Pass zero to specify no flags. diff --git a/mozilla/xpcom/io/nsPipe3.cpp b/mozilla/xpcom/io/nsPipe3.cpp index dca0cccb32d..4d33cb0166e 100644 --- a/mozilla/xpcom/io/nsPipe3.cpp +++ b/mozilla/xpcom/io/nsPipe3.cpp @@ -813,9 +813,6 @@ nsPipeInputStream::AsyncWait(nsIInputStreamCallback *callback, mCallback = 0; mCallbackFlags = 0; - if (!callback) - return NS_OK; - nsCOMPtr proxy; if (target) { nsresult rv = NS_NewInputStreamReadyEvent(getter_AddRefs(proxy), @@ -1187,9 +1184,6 @@ nsPipeOutputStream::AsyncWait(nsIOutputStreamCallback *callback, mCallback = 0; mCallbackFlags = 0; - if (!callback) - return NS_OK; - nsCOMPtr proxy; if (target) { nsresult rv = NS_NewOutputStreamReadyEvent(getter_AddRefs(proxy), diff --git a/mozilla/xpcom/io/nsStreamUtils.cpp b/mozilla/xpcom/io/nsStreamUtils.cpp index fb7fbce7153..b0f396a1b59 100644 --- a/mozilla/xpcom/io/nsStreamUtils.cpp +++ b/mozilla/xpcom/io/nsStreamUtils.cpp @@ -721,18 +721,3 @@ NS_DiscardSegment(nsIInputStream *inStr, *countWritten = count; return NS_OK; } - -//----------------------------------------------------------------------------- - -NS_COM NS_METHOD -NS_WriteSegmentThunk(nsIInputStream *inStr, - void *closure, - const char *buffer, - PRUint32 offset, - PRUint32 count, - PRUint32 *countWritten) -{ - nsWriteSegmentThunk *thunk = NS_STATIC_CAST(nsWriteSegmentThunk *, closure); - return thunk->mFun(thunk->mStream, thunk->mClosure, buffer, offset, count, - countWritten); -} diff --git a/mozilla/xpcom/io/nsStreamUtils.h b/mozilla/xpcom/io/nsStreamUtils.h index 9040403ace8..0db75b12d69 100644 --- a/mozilla/xpcom/io/nsStreamUtils.h +++ b/mozilla/xpcom/io/nsStreamUtils.h @@ -39,8 +39,8 @@ #define nsStreamUtils_h__ #include "nsStringFwd.h" -#include "nsIInputStream.h" +class nsIInputStream; class nsIOutputStream; class nsIInputStreamCallback; class nsIOutputStreamCallback; @@ -204,26 +204,4 @@ NS_DiscardSegment(nsIInputStream *aInputStream, void *aClosure, const char *aFromSegment, PRUint32 aToOffset, PRUint32 aCount, PRUint32 *aWriteCount); -/** - * This function is intended to be passed to nsIInputStream::ReadSegments to - * adjust the aInputStream parameter passed to a consumer's WriteSegmentFun. - * The aClosure parameter must be a pointer to a nsWriteSegmentThunk object. - * The mStream and mClosure members of that object will be passed to the mFun - * function, with the remainder of the parameters being what are passed to - * NS_WriteSegmentThunk. - * - * This function comes in handy when implementing ReadSegments in terms of an - * inner stream's ReadSegments. - */ -extern NS_COM NS_METHOD -NS_WriteSegmentThunk(nsIInputStream *aInputStream, void *aClosure, - const char *aFromSegment, PRUint32 aToOffset, - PRUint32 aCount, PRUint32 *aWriteCount); - -struct nsWriteSegmentThunk { - nsIInputStream *mStream; - nsWriteSegmentFun mFun; - void *mClosure; -}; - #endif // !nsStreamUtils_h__