nsresult
nsIncrementalDownload::ProcessTimeout()
{
  NS_ASSERTION(!mChannel, "how can we have a channel?");

  // Handle existing error conditions
  if (NS_FAILED(mStatus)) {
    CallOnStopRequest();
    return NS_OK;
  }

  // Fetch next chunk
  
  nsCOMPtr<nsIChannel> channel;
  nsresult rv = NS_NewChannel(getter_AddRefs(channel), mFinalURI, nsnull,
                              nsnull, this, mLoadFlags);
  if (NS_FAILED(rv))
    return rv;

  nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(channel, &rv);
  if (NS_FAILED(rv))
    return rv;

  NS_ASSERTION(mCurrentSize != nsInt64(-1),
      "we should know the current file size by now");

  rv = ClearRequestHeader(http);
  if (NS_FAILED(rv))
    return rv;

  // Don't bother making a range request if we are just going to fetch the
  // entire document.
  if (mInterval || mCurrentSize != nsInt64(0)) {
    nsCAutoString range;
    MakeRangeSpec(mCurrentSize, mTotalSize, mChunkSize, mInterval == 0, range);

    rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Range"), range, PR_FALSE);
    if (NS_FAILED(rv))
      return rv;
  }

  rv = channel->AsyncOpen(this, nsnull);
  if (NS_FAILED(rv))
    return rv;

  // Wait to assign mChannel when we know we are going to succeed.  This is
  // important because we don't want to introduce a reference cycle between
  // mChannel and this until we know for a fact that AsyncOpen has succeeded,
  // thus ensuring that our stream listener methods will be invoked.
  mChannel = channel;
  return NS_OK;
}
nsresult nsMailboxProtocol::OpenMultipleMsgTransport(PRUint32 offset, PRInt32 size)
{
  nsresult rv;

  nsCOMPtr<nsIStreamTransportService> serv =
      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  // XXX 64-bit
  rv = serv->CreateInputTransport(m_multipleMsgMoveCopyStream, nsInt64(offset),
                                  nsInt64(size), PR_FALSE,
                                  getter_AddRefs(m_transport));

  return rv;
}
void
nsTopProgressManager::UpdateProgressBar(AggregateTransferInfo& info)
{
    if (info.MSecRemaining == 0)
        return;

    if (info.SuspendedCount > 0) {
        // turn on the strobe...
        FE_SetProgressBarPercent(fContext, 0);
        return;
    }

    nsInt64 dt = nsTime(PR_Now()) - fProgressBarStart;
    PRUint32 elapsed = dt / nsInt64((PRUint32) PR_USEC_PER_MSEC);
                             
    // Compute the percent complete, that is, elapsed / (elapsed + remaining)
    double p = ((double) elapsed) / ((double) (elapsed + info.MSecRemaining));
    PRUint32 pctComplete = (PRUint32) (100.0 * p);

#define MONOTONIC_PROGRESS_BAR
#if defined(MONOTONIC_PROGRESS_BAR)
    // This hackery is a kludge to make the progress bar
    // monotonically increase rather than slipping backwards as we
    // discover that there's more content to download. It works by
    // adjusting the progress manager's start time backwards to
    // make the elapsed time (time we've waited so far) seem
    // larger in proportion to the amount of time that appears to
    // be left.
    if (pctComplete < fProgress) {
        PRUint32 newElapsed =
            (PRUint32) ((p * ((double) info.MSecRemaining))
                        / (1.0 - p));

        PRInt32 dMSec = newElapsed - elapsed;
        if (dMSec > 0)
            fProgressBarStart -= nsInt64(dMSec * ((PRUint32) PR_USEC_PER_MSEC));

        // Progress bar hasn't changed -- don't bother updating it.
        return;
    }
#endif

    fProgress = pctComplete;
    FE_SetProgressBarPercent(fContext, fProgress);
}
// maxSize may be -1 if unknown
static void
MakeRangeSpec(const nsInt64 &size, const nsInt64 &maxSize, PRInt32 chunkSize,
              PRBool fetchRemaining, nsCString &rangeSpec)
{
  rangeSpec.AssignLiteral("bytes=");
  rangeSpec.AppendInt(PRInt64(size));
  rangeSpec.Append('-');

  if (fetchRemaining)
    return;

  nsInt64 end = size + nsInt64(chunkSize);
  if (maxSize != nsInt64(-1) && end > maxSize)
    end = maxSize;
  end -= 1;

  rangeSpec.AppendInt(PRInt64(end));
}
NS_IMETHODIMP
nsInputStreamPump::Init(nsIInputStream *stream,
                        PRInt64 streamPos, PRInt64 streamLen,
                        PRUint32 segsize, PRUint32 segcount,
                        PRBool closeWhenDone)
{
    NS_ENSURE_TRUE(mState == STATE_IDLE, NS_ERROR_IN_PROGRESS);

    mStreamOffset = PRUint64(streamPos);
    if (nsInt64(streamLen) >= nsInt64(0))
        mStreamLength = PRUint64(streamLen);
    mStream = stream;
    mSegSize = segsize;
    mSegCount = segcount;
    mCloseWhenDone = closeWhenDone;

    return NS_OK;
}
Beispiel #6
0
/**
 * asynchronously copy file.
 */
static nsresult
RunTest(nsIFile *srcFile, nsIFile *destFile)
{
    nsresult rv;

    LOG(("RunTest\n"));

    nsCOMPtr<nsIStreamTransportService> sts =
        do_GetService(kStreamTransportServiceCID, &rv);
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIInputStream> srcStr;
    rv = NS_NewLocalFileInputStream(getter_AddRefs(srcStr), srcFile);
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIOutputStream> destStr;
    rv = NS_NewLocalFileOutputStream(getter_AddRefs(destStr), destFile);
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsITransport> srcTransport;
    rv = sts->CreateInputTransport(srcStr, nsInt64(-1), nsInt64(-1), PR_TRUE,
                                   getter_AddRefs(srcTransport));
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsITransport> destTransport;
    rv = sts->CreateOutputTransport(destStr, nsInt64(-1), nsInt64(-1), PR_TRUE,
                                    getter_AddRefs(destTransport));
    if (NS_FAILED(rv)) return rv;

    MyCopier *copier = new MyCopier();
    if (copier == nsnull)
        return NS_ERROR_OUT_OF_MEMORY;
    NS_ADDREF(copier);

    rv = copier->AsyncCopy(srcTransport, destTransport);
    if (NS_FAILED(rv)) return rv;

    PumpEvents();

    NS_RELEASE(copier);
    return NS_OK;
}
NS_IMETHODIMP
nsMsgFileStream::Seek(PRInt32 whence, PRInt64 offset)
{
    if (mFileDesc == nsnull)
        return NS_BASE_STREAM_CLOSED;

    nsInt64 cnt = PR_Seek64(mFileDesc, offset, (PRSeekWhence)whence);
    if (cnt == nsInt64(-1)) {
        return NS_ErrorAccordingToNSPR();
    }
    return NS_OK;
}
nsresult
nsIncrementalDownload::FlushChunk()
{
  NS_ASSERTION(mTotalSize != nsInt64(-1), "total size should be known");

  if (mChunkLen == 0)
    return NS_OK;

  nsresult rv = AppendToFile(mDest, mChunk, mChunkLen);
  if (NS_FAILED(rv))
    return rv;

  mCurrentSize += nsInt64(mChunkLen);
  mChunkLen = 0;

  if (mProgressSink)
    mProgressSink->OnProgress(this, mObserverContext,
                              PRUint64(PRInt64(mCurrentSize)),
                              PRUint64(PRInt64(mTotalSize)));
  return NS_OK;
}
Beispiel #9
0
static nsresult
RunBlockingTest(nsIFile *srcFile, nsIFile *destFile)
{
    nsresult rv;

    LOG(("RunBlockingTest\n"));

    nsCOMPtr<nsIStreamTransportService> sts =
        do_GetService(kStreamTransportServiceCID, &rv);
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIInputStream> srcIn;
    rv = NS_NewLocalFileInputStream(getter_AddRefs(srcIn), srcFile);
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIOutputStream> fileOut;
    rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOut), destFile);
    if (NS_FAILED(rv)) return rv;
    
    nsCOMPtr<nsITransport> destTransport;
    rv = sts->CreateOutputTransport(fileOut, nsInt64(-1), nsInt64(-1),
                                    PR_TRUE, getter_AddRefs(destTransport));
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIOutputStream> destOut;
    rv = destTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING, 100, 10, getter_AddRefs(destOut));
    if (NS_FAILED(rv)) return rv;

    char buf[120];
    PRUint32 n;
    for (;;) {
        rv = srcIn->Read(buf, sizeof(buf), &n);
        if (NS_FAILED(rv) || (n == 0)) return rv;

        rv = destOut->Write(buf, n, &n);
        if (NS_FAILED(rv)) return rv;
    }

    return NS_OK;
}
NS_IMETHODIMP
nsMsgFileStream::Tell(PRInt64 *result)
{
    if (mFileDesc == nsnull)
        return NS_BASE_STREAM_CLOSED;

    nsInt64 cnt = PR_Seek64(mFileDesc, 0, PR_SEEK_CUR);
    if (cnt == nsInt64(-1)) {
        return NS_ErrorAccordingToNSPR();
    }
    *result = cnt;
    return NS_OK;
}
NS_IMETHODIMP nsIconChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
{
  nsCOMPtr<nsIInputStream> inStream;
  nsresult rv = MakeInputStream(getter_AddRefs(inStream), PR_TRUE);
  if (NS_FAILED(rv))
    return rv;

  // Init our streampump
  rv = mPump->Init(inStream, nsInt64(-1), nsInt64(-1), 0, 0, PR_FALSE);
  if (NS_FAILED(rv))
    return rv;

  rv = mPump->AsyncRead(this, ctxt);
  if (NS_SUCCEEDED(rv)) {
    // Store our real listener
    mListener = aListener;
    // Add ourself to the load group, if available
    if (mLoadGroup)
      mLoadGroup->AddRequest(this, nsnull);
  }
  return rv;
}
static char* 
ReadManifestIntoMemory(xptiInterfaceInfoManager* aMgr,
                       PRUint32* pLength)
{
    PRFileDesc* fd = nsnull;
    PRInt32 flen;
    PRInt64 fileSize;
    char* whole = nsnull;
    PRBool success = PR_FALSE;

    nsCOMPtr<nsILocalFile> aFile;
    if(!aMgr->GetCloneOfManifestLocation(getter_AddRefs(aFile)) || !aFile)
        return nsnull;

    if(NS_FAILED(aFile->GetFileSize(&fileSize)) || !(flen = nsInt64(fileSize)))
        return nsnull;

    whole = new char[flen];
    if (!whole)
        return nsnull;

    // All exits from on here should be via 'goto out' 

    if(NS_FAILED(aFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd)) || !fd)
        goto out;

    if(flen > PR_Read(fd, whole, flen))
        goto out;

    success = PR_TRUE;

 out:
    if(fd)
        PR_Close(fd);

    if(!success)     
    {
        delete [] whole;
        return nsnull;
    }

    *pLength = flen;
    return whole;    
}
void
nsTopProgressManager::UpdateStatusMessage(AggregateTransferInfo& info)
{
    // Compute how much time has elapsed
    nsInt64 dt = nsTime(PR_Now()) - fActualStart;
    PRUint32 elapsed = dt / nsInt64((PRUint32) PR_USEC_PER_SEC);

    char buf[256];
    *buf = 0;

    if (info.ObjectCount == 1 || info.CompleteCount == 0) {
        // If we only have one object that we're transferring, or if
        // nothing has completed yet, show the default status message
        PL_strncpy(buf, fDefaultStatus, sizeof(buf));
    }

    if (elapsed > TIME_UNTIL_DETAILS) {
        char details[256];
        *details = 0;

        if (!info.UnknownLengthCount && info.ContentLength > 0) {
            formatKnownContentLength(details, sizeof(details),
                                     info.BytesReceived,
                                     info.ContentLength,
                                     elapsed);
        }
        else if (info.BytesReceived > 0) {
            formatUnknownContentLength(details, sizeof(details),
                                       info.BytesReceived,
                                       elapsed);
        }

        if (*details) {
            // XXX needs to go to allxpstr.h
            if (*buf)
                PL_strcatn(buf, sizeof(buf), ", ");

            PL_strcatn(buf, sizeof(buf), details);
        }
    }

    FE_Progress(fContext, buf);
}
Beispiel #14
0
// called on the socket thread
nsresult
nsHttpTransaction::HandleContent(char *buf,
                                 PRUint32 count,
                                 PRUint32 *contentRead,
                                 PRUint32 *contentRemaining)
{
    nsresult rv;

    LOG(("nsHttpTransaction::HandleContent [this=%x count=%u]\n", this, count));

    *contentRead = 0;
    *contentRemaining = 0;

    NS_ASSERTION(mConnection, "no connection");

    if (!mDidContentStart) {
        rv = HandleContentStart();
        if (NS_FAILED(rv)) return rv;
        // Do not write content to the pipe if we haven't started streaming yet
        if (!mDidContentStart)
            return NS_OK;
    }

    if (mChunkedDecoder) {
        // give the buf over to the chunked decoder so it can reformat the
        // data and tell us how much is really there.
        rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
        if (NS_FAILED(rv)) return rv;
    }
    else if (mContentLength >= nsInt64(0)) {
        // HTTP/1.0 servers have been known to send erroneous Content-Length
        // headers. So, unless the connection is persistent, we must make
        // allowances for a possibly invalid Content-Length header. Thus, if
        // NOT persistent, we simply accept everything in |buf|.
        if (mConnection->IsPersistent()) {
            nsInt64 remaining = mContentLength - mContentRead;
            nsInt64 count64 = count;
            *contentRead = PR_MIN(count64, remaining);
            *contentRemaining = count - *contentRead;
        }
        else {
            *contentRead = count;
            // mContentLength might need to be increased...
            nsInt64 position = mContentRead + nsInt64(count);
            if (position > mContentLength) {
                mContentLength = position;
                //mResponseHead->SetContentLength(mContentLength);
            }
        }
    }
    else {
        // when we are just waiting for the server to close the connection...
        // (no explicit content-length given)
        *contentRead = count;
    }

    if (*contentRead) {
        // update count of content bytes read and report progress...
        mContentRead += *contentRead;
        /* when uncommenting, take care of 64-bit integers w/ PR_MAX...
        if (mProgressSink)
            mProgressSink->OnProgress(nsnull, nsnull, mContentRead, PR_MAX(0, mContentLength));
        */
    }

    LOG(("nsHttpTransaction::HandleContent [this=%x count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
        this, count, *contentRead, mContentRead.mValue, mContentLength.mValue));

    // check for end-of-file
    if ((mContentRead == mContentLength) ||
        (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
        // the transaction is done with a complete response.
        mTransactionDone = PR_TRUE;
        mResponseIsComplete = PR_TRUE;

        // report the entire response has arrived
        if (mActivityDistributor)
            mActivityDistributor->ObserveActivity(
                mChannel,
                NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
                NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
                PR_Now(),
                static_cast<PRUint64>(mContentRead.mValue),
                EmptyCString());
    }

    return NS_OK;
}
Beispiel #15
0
// called on the socket thread
nsresult
nsHttpTransaction::HandleContentStart()
{
    LOG(("nsHttpTransaction::HandleContentStart [this=%x]\n", this));

    if (mResponseHead) {
#if defined(PR_LOGGING)
        if (LOG3_ENABLED()) {
            LOG3(("http response [\n"));
            nsCAutoString headers;
            mResponseHead->Flatten(headers, PR_FALSE);
            LogHeaders(headers.get());
            LOG3(("]\n"));
        }
#endif
        // notify the connection, give it a chance to cause a reset.
        PRBool reset = PR_FALSE;
        mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset);

        // looks like we should ignore this response, resetting...
        if (reset) {
            LOG(("resetting transaction's response head\n"));
            mHaveAllHeaders = PR_FALSE;
            mHaveStatusLine = PR_FALSE;
            mReceivedData = PR_FALSE;
            mSentData = PR_FALSE;
            mResponseHead->Reset();
            // wait to be called again...
            return NS_OK;
        }

        // check if this is a no-content response
        switch (mResponseHead->Status()) {
        case 204:
        case 205:
        case 304:
            mNoContent = PR_TRUE;
            LOG(("this response should not contain a body.\n"));
            break;
        }

        if (mNoContent)
            mContentLength = 0;
        else {
            // grab the content-length from the response headers
            mContentLength = mResponseHead->ContentLength();

            // handle chunked encoding here, so we'll know immediately when
            // we're done with the socket.  please note that _all_ other
            // decoding is done when the channel receives the content data
            // so as not to block the socket transport thread too much.
            // ignore chunked responses from HTTP/1.0 servers and proxies.
            if (mResponseHead->Version() >= NS_HTTP_VERSION_1_1 &&
                mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) {
                // we only support the "chunked" transfer encoding right now.
                mChunkedDecoder = new nsHttpChunkedDecoder();
                if (!mChunkedDecoder)
                    return NS_ERROR_OUT_OF_MEMORY;
                LOG(("chunked decoder created\n"));
                // Ignore server specified Content-Length.
                mContentLength = -1;
            }
#if defined(PR_LOGGING)
            else if (mContentLength == nsInt64(-1))
                LOG(("waiting for the server to close the connection.\n"));
#endif
        }
    }

    mDidContentStart = PR_TRUE;
    return NS_OK;
}
NS_IMETHODIMP 
nsCookiePermission::CanSetCookie(nsIURI     *aURI,
                                 nsIChannel *aChannel,
                                 nsICookie2 *aCookie,
                                 PRBool     *aIsSession,
                                 PRInt64    *aExpiry,
                                 PRBool     *aResult)
{
  NS_ASSERTION(aURI, "null uri");

  *aResult = kDefaultPolicy;

  PRUint32 perm;
  mPermMgr->TestPermission(aURI, kPermissionType, &perm);
  switch (perm) {
  case nsICookiePermission::ACCESS_SESSION:
    *aIsSession = PR_TRUE;

  case nsIPermissionManager::ALLOW_ACTION: // ACCESS_ALLOW
    *aResult = PR_TRUE;
    break;

  case nsIPermissionManager::DENY_ACTION:  // ACCESS_DENY
    *aResult = PR_FALSE;
    break;

  default:
    // the permission manager has nothing to say about this cookie -
    // so, we apply the default prefs to it.
    NS_ASSERTION(perm == nsIPermissionManager::UNKNOWN_ACTION, "unknown permission");
    
    // now we need to figure out what type of accept policy we're dealing with
    // if we accept cookies normally, just bail and return
    if (mCookiesLifetimePolicy == ACCEPT_NORMALLY) {
      *aResult = PR_TRUE;
      return NS_OK;
    }
    
    // declare this here since it'll be used in all of the remaining cases
    nsInt64 currentTime = NOW_IN_SECONDS;
    nsInt64 delta = nsInt64(*aExpiry) - currentTime;
    
    // check whether the user wants to be prompted
    if (mCookiesLifetimePolicy == ASK_BEFORE_ACCEPT) {
      // if it's a session cookie and the user wants to accept these 
      // without asking, just accept the cookie and return
      if (*aIsSession && mCookiesAlwaysAcceptSession) {
        *aResult = PR_TRUE;
        return NS_OK;
      }
      
      // default to rejecting, in case the prompting process fails
      *aResult = PR_FALSE;

      nsCAutoString hostPort;
      aURI->GetHostPort(hostPort);

      if (!aCookie) {
         return NS_ERROR_UNEXPECTED;
      }
      // If there is no host, use the scheme, and append "://",
      // to make sure it isn't a host or something.
      // This is done to make the dialog appear for javascript cookies from
      // file:// urls, and make the text on it not too weird. (bug 209689)
      if (hostPort.IsEmpty()) {
        aURI->GetScheme(hostPort);
        if (hostPort.IsEmpty()) {
          // still empty. Just return the default.
          return NS_OK;
        }
        hostPort = hostPort + NS_LITERAL_CSTRING("://");
      }

      // we don't cache the cookiePromptService - it's not used often, so not
      // worth the memory.
      nsresult rv;
      nsCOMPtr<nsICookiePromptService> cookiePromptService =
          do_GetService(NS_COOKIEPROMPTSERVICE_CONTRACTID, &rv);
      if (NS_FAILED(rv)) return rv;

      // try to get a nsIDOMWindow from the channel...
      nsCOMPtr<nsIDOMWindow> parent;
      if (aChannel)
        NS_QueryNotificationCallbacks(aChannel, parent);

      // get some useful information to present to the user:
      // whether a previous cookie already exists, and how many cookies this host
      // has set
      PRBool foundCookie;
      PRUint32 countFromHost;
      nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv);
      if (NS_SUCCEEDED(rv))
        rv = cookieManager->FindMatchingCookie(aCookie, &countFromHost, &foundCookie);
      if (NS_FAILED(rv)) return rv;

      // check if the cookie we're trying to set is already expired, and return;
      // but only if there's no previous cookie, because then we need to delete the previous
      // cookie. we need this check to avoid prompting the user for already-expired cookies.
      if (!foundCookie && !*aIsSession && delta <= nsInt64(0)) {
        // the cookie has already expired. accept it, and let the backend figure
        // out it's expired, so that we get correct logging & notifications.
        *aResult = PR_TRUE;
        return rv;
      }

      PRBool rememberDecision = PR_FALSE;
      rv = cookiePromptService->CookieDialog(parent, aCookie, hostPort, 
                                             countFromHost, foundCookie,
                                             &rememberDecision, aResult);
      if (NS_FAILED(rv)) return rv;
      
      if (*aResult == nsICookiePromptService::ACCEPT_SESSION_COOKIE)
        *aIsSession = PR_TRUE;

      if (rememberDecision) {
        switch (*aResult) {
          case nsICookiePromptService::DENY_COOKIE:
            mPermMgr->Add(aURI, kPermissionType, (PRUint32) nsIPermissionManager::DENY_ACTION);
            break;
          case nsICookiePromptService::ACCEPT_COOKIE:
            mPermMgr->Add(aURI, kPermissionType, (PRUint32) nsIPermissionManager::ALLOW_ACTION);
            break;
          case nsICookiePromptService::ACCEPT_SESSION_COOKIE:
            mPermMgr->Add(aURI, kPermissionType, nsICookiePermission::ACCESS_SESSION);
            break;
          default:
            break;
        }
      }
    } else {
      // we're not prompting, so we must be limiting the lifetime somehow
      // if it's a session cookie, we do nothing
      if (!*aIsSession && delta > nsInt64(0)) {
        if (mCookiesLifetimePolicy == ACCEPT_SESSION) {
          // limit lifetime to session
          *aIsSession = PR_TRUE;
        } else if (delta > mCookiesLifetimeSec) {
          // limit lifetime to specified time
          *aExpiry = currentTime + mCookiesLifetimeSec;
        }
      }
    }
  }

  return NS_OK;
}
NS_IMETHODIMP
nsIncrementalDownload::OnStartRequest(nsIRequest *request,
                                      nsISupports *context)
{
  nsresult rv;

  nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(request, &rv);
  if (NS_FAILED(rv))
    return rv;

  // Ensure that we are receiving a 206 response.
  PRUint32 code;
  rv = http->GetResponseStatus(&code);
  if (NS_FAILED(rv))
    return rv;
  if (code != 206) {
    // We may already have the entire file downloaded, in which case
    // our request for a range beyond the end of the file would have
    // been met with an error response code.
    if (code == 416 && mTotalSize == nsInt64(-1)) {
      mTotalSize = mCurrentSize;
      // Return an error code here to suppress OnDataAvailable.
      return NS_ERROR_DOWNLOAD_COMPLETE;
    }
    // The server may have decided to give us all of the data in one chunk.  If
    // we requested a partial range, then we don't want to download all of the
    // data at once.  So, we'll just try again, but if this keeps happening then
    // we'll eventually give up.
    if (code == 200) {
      if (mInterval) {
        mChannel = nsnull;
        if (++mNonPartialCount > MAX_RETRY_COUNT) {
          NS_WARNING("unable to fetch a byte range; giving up");
          return NS_ERROR_FAILURE;
        }
        // Increase delay with each failure.
        StartTimer(mInterval * mNonPartialCount);
        return NS_ERROR_DOWNLOAD_NOT_PARTIAL;
      }
      // Since we have been asked to download the rest of the file, we can deal
      // with a 200 response.  This may result in downloading the beginning of
      // the file again, but that can't really be helped.
    } else {
      NS_WARNING("server response was unexpected");
      return NS_ERROR_UNEXPECTED;
    }
  } else {
    // We got a partial response, so clear this counter in case the next chunk
    // results in a 200 response.
    mNonPartialCount = 0;
  }

  // Do special processing after the first response.
  if (mTotalSize == nsInt64(-1)) {
    // Update knowledge of mFinalURI
    rv = http->GetURI(getter_AddRefs(mFinalURI));
    if (NS_FAILED(rv))
      return rv;

    if (code == 206) {
      // OK, read the Content-Range header to determine the total size of this
      // download file.
      nsCAutoString buf;
      rv = http->GetResponseHeader(NS_LITERAL_CSTRING("Content-Range"), buf);
      if (NS_FAILED(rv))
        return rv;
      PRInt32 slash = buf.FindChar('/');
      if (slash == kNotFound) {
        NS_WARNING("server returned invalid Content-Range header!");
        return NS_ERROR_UNEXPECTED;
      }
      if (PR_sscanf(buf.get() + slash + 1, "%lld", (PRInt64 *) &mTotalSize) != 1)
        return NS_ERROR_UNEXPECTED;
    } else {
      // Use nsIPropertyBag2 to fetch the content length as it exposes the
      // value as a 64-bit number.
      nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(request, &rv);
      if (NS_FAILED(rv))
        return rv;
      rv = props->GetPropertyAsInt64(NS_CHANNEL_PROP_CONTENT_LENGTH,
                                     &mTotalSize.mValue);
      // We need to know the total size of the thing we're trying to download.
      if (mTotalSize == nsInt64(-1)) {
        NS_WARNING("server returned no content-length header!");
        return NS_ERROR_UNEXPECTED;
      }
      // Need to truncate (or create, if it doesn't exist) the file since we
      // are downloading the whole thing.
      WriteToFile(mDest, nsnull, 0, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE);
      mCurrentSize = 0;
    }

    // Notify observer that we are starting...
    rv = CallOnStartRequest();
    if (NS_FAILED(rv))
      return rv;
  }

  // Adjust mChunkSize accordingly if mCurrentSize is close to mTotalSize.
  nsInt64 diff = mTotalSize - mCurrentSize;
  if (diff <= nsInt64(0)) {
    NS_WARNING("about to set a bogus chunk size; giving up");
    return NS_ERROR_UNEXPECTED;
  }

  if (diff < nsInt64(mChunkSize))
    mChunkSize = PRUint32(diff);

  mChunk = new char[mChunkSize];
  if (!mChunk)
    rv = NS_ERROR_OUT_OF_MEMORY;

  return rv;
}
// static
PRBool xptiManifest::Read(xptiInterfaceInfoManager* aMgr,
                          xptiWorkingSet*           aWorkingSet)
{
    int i;
    char* whole = nsnull;
    PRBool succeeded = PR_FALSE;
    PRUint32 flen;
    nsManifestLineReader reader;
    xptiHashEntry* hashEntry;
    int headerCount = 0;
    int dirCount = 0;
    int fileCount = 0;
    int zipItemCount = -1;
    int interfaceCount = 0;
    int dir;
    int flags;
    char* values[6];    // 6 is currently the max items we need to parse
    int lengths[6];
    PRUint32 size32;
    PRInt64 size;
    PRInt64 date;

    whole = ReadManifestIntoMemory(aMgr, &flen);
    if(!whole)
        return PR_FALSE;

    reader.Init(whole, flen);

    // All exits from here on should be via 'goto out' 
    
    // Look for "Header" section

    // This version accepts only version 1,0. We also freak if the header
    // has more than one entry. The rationale is that we want to force an
    // autoreg if the xpti.dat file was written by *any* other version of
    // the software. Future versions may wish to support updating older
    // manifests in some interesting way.

    if(!ReadSectionHeader(reader, g_TOKEN_Header, 2, &headerCount))
        goto out;

    if(headerCount != 2)
        goto out;

    // Verify the version number

    if(!reader.NextLine())
        goto out;

    // index,VersionLiteral,major,minor
    if(4 != reader.ParseLine(values, lengths, 4))
        goto out;

    // index
    if(0 != atoi(values[0]))
        goto out;

    // VersionLiteral
    if(0 != PL_strcmp(values[1], g_TOKEN_Version))
        goto out;

    // major
    if(g_VERSION_MAJOR != atoi(values[2]))
        goto out;

    // minor
    if(g_VERSION_MINOR != atoi(values[3]))
        goto out;

    // Verify the application directory

    if(!reader.NextLine())
        goto out;

    // index,AppDirLiteral,directoryname
    if(3 != reader.ParseLine(values, lengths, 3))
        goto out;

    // index
    if(1 != atoi(values[0]))
        goto out;

    // AppDirLiteral
    if(0 != PL_strcmp(values[1], g_TOKEN_AppDir))
        goto out;

    if(!CurrentAppDirMatchesPersistentDescriptor(aMgr, values[2]))
        goto out;

    // Look for "Directories" section

    if(!ReadSectionHeader(reader, g_TOKEN_Directories, 1, &dirCount))
        goto out;
    else
    {
        // To validate that the directory list matches the current search path
        // we first confirm that the list lengths match.

        nsCOMPtr<nsISupportsArray> searchPath;
        aMgr->GetSearchPath(getter_AddRefs(searchPath));

        PRUint32 searchPathCount;
        searchPath->Count(&searchPathCount);
        
        if(dirCount != (int) searchPathCount)
            goto out;
    }

    // Read the directory records

    for(i = 0; i < dirCount; ++i)
    {
        if(!reader.NextLine())
            goto out;
       
        // index,directoryname
        if(2 != reader.ParseLine(values, lengths, 2))
            goto out;

        // index
        if(i != atoi(values[0]))
            goto out;

        // directoryname
        if(!aWorkingSet->DirectoryAtMatchesPersistentDescriptor(i, values[1]))
            goto out;    
    }

    // Look for "Files" section

    if(!ReadSectionHeader(reader, g_TOKEN_Files, 1, &fileCount))
        goto out;


    // Alloc room in the WorkingSet for the filearray.

    if(!aWorkingSet->NewFileArray(fileCount))   
        goto out;    

    // Read the file records

    for(i = 0; i < fileCount; ++i)
    {
        if(!reader.NextLine())
            goto out;

        // index,filename,dirIndex,dilesSize,filesDate
        if(5 != reader.ParseLine(values, lengths, 5))
            goto out;

        // index
        if(i != atoi(values[0]))
            goto out;

        // filename
        if(!*values[1])
            goto out;

        // dirIndex
        dir = atoi(values[2]);
        if(dir < 0 || dir > dirCount)
            goto out;

        // fileSize
        size32 = atoi(values[3]);
        if(size32 <= 0)
            goto out;
        LL_UI2L(size, size32);

        // fileDate
        date = nsCRT::atoll(values[4]);
        if(LL_IS_ZERO(date))
            goto out;
        
        // Append a new file record to the array.

        aWorkingSet->AppendFile(
            xptiFile(nsInt64(size), nsInt64(date), dir, values[1], aWorkingSet));
    }

    // Look for "ZipItems" section

    if(!ReadSectionHeader(reader, g_TOKEN_ArchiveItems, 0, &zipItemCount))
        goto out;

    // Alloc room in the WorkingSet for the zipItemarray.

    if(zipItemCount)
        if(!aWorkingSet->NewZipItemArray(zipItemCount))   
            goto out;    

    // Read the zipItem records

    for(i = 0; i < zipItemCount; ++i)
    {
        if(!reader.NextLine())
            goto out;

        // index,filename
        if(2 != reader.ParseLine(values, lengths, 2))
            goto out;

        // index
        if(i != atoi(values[0]))
            goto out;

        // filename
        if(!*values[1])
            goto out;
        
        // Append a new zipItem record to the array.

        aWorkingSet->AppendZipItem(xptiZipItem(values[1], aWorkingSet));
    }

    // Look for "Interfaces" section

    if(!ReadSectionHeader(reader, g_TOKEN_Interfaces, 1, &interfaceCount))
        goto out;

    // Read the interface records

    for(i = 0; i < interfaceCount; ++i)
    {
        int fileIndex;
        int zipItemIndex;
        nsIID iid;
        xptiInterfaceEntry* entry;
        xptiTypelib typelibRecord;

        if(!reader.NextLine())
            goto out;

        // index,interfaceName,iid,fileIndex,zipIndex,flags
        if(6 != reader.ParseLine(values, lengths, 6))
            goto out;

        // index
        if(i != atoi(values[0]))
            goto out;

        // interfaceName
        if(!*values[1])
            goto out;

        // iid
        if(!iid.Parse(values[2]))
            goto out;

        // fileIndex
        fileIndex = atoi(values[3]);
        if(fileIndex < 0 || fileIndex >= fileCount)
            goto out;

        // zipIndex (NOTE: -1 is a valid value)
        zipItemIndex = atoi(values[4]);
        if(zipItemIndex < -1 || zipItemIndex >= zipItemCount)
            goto out;

        // flags
        flags = atoi(values[5]);
        if(flags != 0 && flags != 1)
            goto out;
        
        // Build an InterfaceInfo and hook it in.

        if(zipItemIndex == -1)
            typelibRecord.Init(fileIndex);
        else
            typelibRecord.Init(fileIndex, zipItemIndex);
        
        entry = xptiInterfaceEntry::NewEntry(values[1], lengths[1],
                                             iid, typelibRecord, 
                                             aWorkingSet);
        if(!entry)
            goto out;    
        
        entry->SetScriptableFlag(flags==1);

        // Add our entry to the iid hashtable.

        hashEntry = (xptiHashEntry*)
            PL_DHashTableOperate(aWorkingSet->mNameTable, 
                                 entry->GetTheName(), PL_DHASH_ADD);
        if(hashEntry)
            hashEntry->value = entry;
    
        // Add our entry to the name hashtable.

        hashEntry = (xptiHashEntry*)
            PL_DHashTableOperate(aWorkingSet->mIIDTable, 
                                 entry->GetTheIID(), PL_DHASH_ADD);
        if(hashEntry)
            hashEntry->value = entry;
    }

    // success!

    succeeded = PR_TRUE;

 out:
    if(whole)
        delete [] whole;

    if(!succeeded)
    {
        // Cleanup the WorkingSet on failure.
        aWorkingSet->InvalidateInterfaceInfos();
        aWorkingSet->ClearHashTables();
        aWorkingSet->ClearFiles();
    }
    return succeeded;
}        
NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
                                                        nsISupports* aContext,
                                                        nsresult aStatus)
{
  nsresult rv = NS_OK;

  nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(request);
  if (!mp) {
    PRBool found = mRequests.RemoveObject(request);
    if (!found) {
      NS_ERROR("Received OnStopRequest for untracked request.");
    }
  }
  
  PLUGIN_LOG(PLUGIN_LOG_NOISY,
             ("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n",
              this, aStatus, request));
  
  // for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return.
  nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
  if (brr) {
    PRInt64 absoluteOffset64 = LL_ZERO;
    brr->GetStartRange(&absoluteOffset64);
    // XXX support 64-bit offsets
    PRInt32 absoluteOffset = (PRInt32)nsInt64(absoluteOffset64);
    
    nsPRUintKey key(absoluteOffset);
    
    // remove the request from our data forwarding count hash.
    mDataForwardToRequest->Remove(&key);
    
    
    PLUGIN_LOG(PLUGIN_LOG_NOISY,
               ("                          ::OnStopRequest for ByteRangeRequest Started=%d\n",
                absoluteOffset));
  } else {
    // if this is not byte range request and
    // if we are writting the stream to disk ourselves,
    // close & tear it down here
    mFileCacheOutputStream = nsnull;
  }
  
  // if we still have pending stuff to do, lets not close the plugin socket.
  if (--mPendingRequests > 0)
    return NS_OK;
  
  // we keep our connections around...
  nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
  if (container) {
    PRUint32 magicNumber = 0;  // set it to something that is not the magic number.
    container->GetData(&magicNumber);
    if (magicNumber == MAGIC_REQUEST_CONTEXT) {
      // this is one of our range requests
      return NS_OK;
    }
  }
  
  if (!mPStreamListener)
    return NS_ERROR_FAILURE;
  
  nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
  if (!channel)
    return NS_ERROR_FAILURE;
  // Set the content type to ensure we don't pass null to the plugin
  nsCAutoString aContentType;
  rv = channel->GetContentType(aContentType);
  if (NS_FAILED(rv) && !mRequestFailed)
    return rv;
  
  if (!aContentType.IsEmpty())
    mContentType = aContentType;
  
  // set error status if stream failed so we notify the plugin
  if (mRequestFailed)
    aStatus = NS_ERROR_FAILURE;
  
  if (NS_FAILED(aStatus)) {
    // on error status cleanup the stream
    // and return w/o OnFileAvailable()
    mPStreamListener->OnStopBinding(this, aStatus);
    return NS_OK;
  }
  
  // call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly
  if (mStreamType >= NP_ASFILE) {
    nsCOMPtr<nsIFile> localFile;
    if (mLocalCachedFileHolder)
      localFile = mLocalCachedFileHolder->file();
    else {
      nsCOMPtr<nsICachingChannel> cacheChannel = do_QueryInterface(request);
      if (cacheChannel) {
        cacheChannel->GetCacheFile(getter_AddRefs(localFile));
      } else {
        // see if it is a file channel.
        nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
        if (fileChannel) {
          fileChannel->GetFile(getter_AddRefs(localFile));
        }
      }
    }
    
    if (localFile) {
      OnFileAvailable(localFile);
    }
  }
  
  if (mStartBinding) {
    // On start binding has been called
    mPStreamListener->OnStopBinding(this, aStatus);
  } else {
    // OnStartBinding hasn't been called, so complete the action.
    mPStreamListener->OnStartBinding(this);
    mPStreamListener->OnStopBinding(this, aStatus);
  }
  
  if (NS_SUCCEEDED(aStatus)) {
    mStreamComplete = PR_TRUE;
  }
  
  return NS_OK;
}
NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
                                                          nsISupports* aContext,
                                                          nsIInputStream *aIStream,
                                                          PRUint32 sourceOffset,
                                                          PRUint32 aLength)
{
  NS_ASSERTION(mRequests.IndexOfObject(GetBaseRequest(request)) != -1,
               "Received OnDataAvailable for untracked request.");
  
  if (mRequestFailed)
    return NS_ERROR_FAILURE;
  
  if (mAbort) {
    PRUint32 magicNumber = 0;  // set it to something that is not the magic number.
    nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
    if (container)
      container->GetData(&magicNumber);
    
    if (magicNumber != MAGIC_REQUEST_CONTEXT) {
      // this is not one of our range requests
      mAbort = PR_FALSE;
      return NS_BINDING_ABORTED;
    }
  }
  
  nsresult rv = NS_OK;
  
  if (!mPStreamListener)
    return NS_ERROR_FAILURE;
  
  const char * url = nsnull;
  GetURL(&url);
  
  PLUGIN_LOG(PLUGIN_LOG_NOISY,
             ("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%d, length=%d, url=%s\n",
              this, request, sourceOffset, aLength, url ? url : "no url set"));
  
  // if the plugin has requested an AsFileOnly stream, then don't
  // call OnDataAvailable
  if (mStreamType != NP_ASFILEONLY) {
    // get the absolute offset of the request, if one exists.
    nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
    if (brr) {
      if (!mDataForwardToRequest)
        return NS_ERROR_FAILURE;
      
      PRInt64 absoluteOffset64 = LL_ZERO;
      brr->GetStartRange(&absoluteOffset64);
      
      // XXX handle 64-bit for real
      PRInt32 absoluteOffset = (PRInt32)nsInt64(absoluteOffset64);
      
      // we need to track how much data we have forwarded to the
      // plugin.
      
      // FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130
      //
      // Why couldn't this be tracked on the plugin info, and not in a
      // *hash table*?
      nsPRUintKey key(absoluteOffset);
      PRInt32 amtForwardToPlugin =
      NS_PTR_TO_INT32(mDataForwardToRequest->Get(&key));
      mDataForwardToRequest->Put(&key, NS_INT32_TO_PTR(amtForwardToPlugin + aLength));
      
      SetStreamOffset(absoluteOffset + amtForwardToPlugin);
    }
    
    nsCOMPtr<nsIInputStream> stream = aIStream;
    
    // if we are caching the file ourselves to disk, we want to 'tee' off
    // the data as the plugin read from the stream.  We do this by the magic
    // of an input stream tee.
    
    if (mFileCacheOutputStream) {
      rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream);
      if (NS_FAILED(rv))
        return rv;
    }
    
    rv =  mPStreamListener->OnDataAvailable(this,
                                            stream,
                                            aLength);
    
    // if a plugin returns an error, the peer must kill the stream
    //   else the stream and PluginStreamListener leak
    if (NS_FAILED(rv))
      request->Cancel(rv);
  }
  else
  {
    // if we don't read from the stream, OnStopRequest will never be called
    char* buffer = new char[aLength];
    PRUint32 amountRead, amountWrote = 0;
    rv = aIStream->Read(buffer, aLength, &amountRead);
    
    // if we are caching this to disk ourselves, lets write the bytes out.
    if (mFileCacheOutputStream) {
      while (amountWrote < amountRead && NS_SUCCEEDED(rv)) {
        rv = mFileCacheOutputStream->Write(buffer, amountRead, &amountWrote);
      }
    }
    delete [] buffer;
  }
  return rv;
}