gecko-dev/extensions/finger/nsFingerChannel.cpp

453 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Brian Ryner.
* Portions created by Brian Ryner are Copyright (C) 2000 Brian Ryner.
* All Rights Reserved.
*
* Contributor(s):
* Brian Ryner <bryner@uiuc.edu>
*/
// finger implementation
#include "nsFingerChannel.h"
#include "nsIServiceManager.h"
#include "nsILoadGroup.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsXPIDLString.h"
#include "nsISocketTransportService.h"
#include "nsIStringStream.h"
#include "nsMimeTypes.h"
#include "nsIStreamConverterService.h"
#include "nsITXTToHTMLConv.h"
#include "nsIProgressEventSink.h"
#include "nsNetUtil.h"
#include "nsCRT.h"
static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID);
#define BUFFER_SEG_SIZE (4*1024)
#define BUFFER_MAX_SIZE (64*1024)
// nsFingerChannel methods
nsFingerChannel::nsFingerChannel()
: mContentLength(-1),
mActAsObserver(PR_TRUE),
mPort(-1),
mStatus(NS_OK)
{
NS_INIT_ISUPPORTS();
}
nsFingerChannel::~nsFingerChannel() {
}
NS_IMPL_THREADSAFE_ISUPPORTS4(nsFingerChannel,
nsIChannel,
nsIRequest,
nsIStreamListener,
nsIRequestObserver)
nsresult
nsFingerChannel::Init(nsIURI* uri, nsIProxyInfo* proxyInfo)
{
nsresult rv;
nsCAutoString autoBuffer;
NS_ASSERTION(uri, "no uri");
mUrl = uri;
mProxyInfo = proxyInfo;
// For security reasons, we do not allow the user to specify a
// non-default port for finger: URL's.
mPort = FINGER_PORT;
rv = mUrl->GetPath(autoBuffer); // autoBuffer = user@host
if (NS_FAILED(rv)) return rv;
// Now parse out the user and host
const char* buf = autoBuffer.get();
const char* pos = strchr(buf, '@');
// Catch the case of just the host being given
if (!pos) {
mHost.Assign(buf);
} else {
mUser.Assign(buf,pos-buf);
mHost.Assign(pos+1); // ignore '@'
}
if (mHost.IsEmpty()) return NS_ERROR_NOT_INITIALIZED;
return NS_OK;
}
NS_METHOD
nsFingerChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
{
nsFingerChannel* fc = new nsFingerChannel();
if (fc == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(fc);
nsresult rv = fc->QueryInterface(aIID, aResult);
NS_RELEASE(fc);
return rv;
}
////////////////////////////////////////////////////////////////////////////////
// nsIRequest methods:
NS_IMETHODIMP
nsFingerChannel::GetName(nsACString &result)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFingerChannel::IsPending(PRBool *result)
{
NS_NOTREACHED("nsFingerChannel::IsPending");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFingerChannel::GetStatus(nsresult *status)
{
*status = mStatus;
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::Cancel(nsresult status)
{
NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
nsresult rv = NS_ERROR_FAILURE;
mStatus = status;
if (mTransportRequest) {
rv = mTransportRequest->Cancel(status);
}
return rv;
}
NS_IMETHODIMP
nsFingerChannel::Suspend(void)
{
NS_NOTREACHED("nsFingerChannel::Suspend");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFingerChannel::Resume(void)
{
NS_NOTREACHED("nsFingerChannel::Resume");
return NS_ERROR_NOT_IMPLEMENTED;
}
////////////////////////////////////////////////////////////////////////////////
// nsIChannel methods:
NS_IMETHODIMP
nsFingerChannel::GetOriginalURI(nsIURI* *aURI)
{
*aURI = mOriginalURI ? mOriginalURI : mUrl;
NS_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::SetOriginalURI(nsIURI* aURI)
{
mOriginalURI = aURI;
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::GetURI(nsIURI* *aURI)
{
*aURI = mUrl;
NS_IF_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::Open(nsIInputStream **_retval)
{
nsresult rv = NS_OK;
rv = NS_CheckPortSafety(mPort, "finger");
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsISocketTransportService> socketService =
do_GetService(kSocketTransportServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = socketService->CreateTransport(mHost.get(), mPort, mProxyInfo, BUFFER_SEG_SIZE,
BUFFER_MAX_SIZE, getter_AddRefs(mTransport));
if (NS_FAILED(rv)) return rv;
mTransport->SetNotificationCallbacks(mCallbacks,
(mLoadFlags & LOAD_BACKGROUND));
return mTransport->OpenInputStream(0, PRUint32(-1), 0, _retval);
}
NS_IMETHODIMP
nsFingerChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
{
nsresult rv = NS_OK;
rv = NS_CheckPortSafety(mPort, "finger");
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsISocketTransportService> socketService =
do_GetService(kSocketTransportServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = socketService->CreateTransport(mHost.get(), mPort, mProxyInfo, BUFFER_SEG_SIZE,
BUFFER_MAX_SIZE, getter_AddRefs(mTransport));
if (NS_FAILED(rv)) return rv;
mTransport->SetNotificationCallbacks(mCallbacks,
(mLoadFlags & LOAD_BACKGROUND));
mListener = aListener;
mResponseContext = ctxt;
return SendRequest(mTransport);
}
NS_IMETHODIMP
nsFingerChannel::GetLoadFlags(PRUint32 *aLoadFlags)
{
*aLoadFlags = mLoadFlags;
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::SetLoadFlags(PRUint32 aLoadFlags)
{
mLoadFlags = aLoadFlags;
return NS_OK;
}
#define FINGER_TYPE TEXT_HTML
NS_IMETHODIMP
nsFingerChannel::GetContentType(nsACString &aContentType)
{
aContentType = NS_LITERAL_CSTRING(FINGER_TYPE);
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::SetContentType(const nsACString &aContentType)
{
//It doesn't make sense to set the content-type on this type
// of channel...
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsFingerChannel::GetContentCharset(nsACString &aContentCharset)
{
aContentCharset.Truncate();
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::SetContentCharset(const nsACString &aContentCharset)
{
NS_NOTREACHED("nsFingerChannel::SetContentCharset");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFingerChannel::GetContentLength(PRInt32 *aContentLength)
{
*aContentLength = mContentLength;
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::SetContentLength(PRInt32 aContentLength)
{
NS_NOTREACHED("nsFingerChannel::SetContentLength");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFingerChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
*aLoadGroup = mLoadGroup;
NS_IF_ADDREF(*aLoadGroup);
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
mLoadGroup = aLoadGroup;
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get();
NS_IF_ADDREF(*aOwner);
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::SetOwner(nsISupports* aOwner)
{
mOwner = aOwner;
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
*aNotificationCallbacks = mCallbacks.get();
NS_IF_ADDREF(*aNotificationCallbacks);
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
mCallbacks = aNotificationCallbacks;
return NS_OK;
}
NS_IMETHODIMP
nsFingerChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
{
*aSecurityInfo = nsnull;
return NS_OK;
}
// nsIRequestObserver methods
NS_IMETHODIMP
nsFingerChannel::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) {
if (!mActAsObserver) {
// acting as a listener
return mListener->OnStartRequest(this, mResponseContext);
} else {
// we don't want to pass our AsyncWrite's OnStart through
// we just ignore this
return NS_OK;
}
}
NS_IMETHODIMP
nsFingerChannel::OnStopRequest(nsIRequest *aRequest, nsISupports* aContext,
nsresult aStatus)
{
nsresult rv = NS_OK;
if (NS_FAILED(aStatus) || !mActAsObserver) {
if (mLoadGroup) {
rv = mLoadGroup->RemoveRequest(this, nsnull, aStatus);
if (NS_FAILED(rv)) return rv;
}
rv = mListener->OnStopRequest(this, mResponseContext, aStatus);
mTransport = 0;
return rv;
} else {
// at this point we know the request has been sent.
// we're no longer acting as an observer.
mActAsObserver = PR_FALSE;
nsCOMPtr<nsIStreamListener> converterListener;
nsCOMPtr<nsIStreamConverterService> StreamConvService =
do_GetService(kStreamConverterServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
nsAutoString fromStr(NS_LITERAL_STRING("text/plain"));
nsAutoString toStr(NS_LITERAL_STRING("text/html"));
rv = StreamConvService->AsyncConvertData(fromStr.get(),
toStr.get(), this, mResponseContext,
getter_AddRefs(converterListener));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsITXTToHTMLConv> converter(do_QueryInterface(converterListener));
if (converter) {
nsAutoString title(NS_LITERAL_STRING("Finger information for "));
nsCAutoString userHost;
rv = mUrl->GetPath(userHost);
title.Append(NS_ConvertUTF8toUCS2(userHost));
converter->SetTitle(title.get());
converter->PreFormatHTML(PR_TRUE);
}
return mTransport->AsyncRead(converterListener, mResponseContext, 0, PRUint32(-1), 0,
getter_AddRefs(mTransportRequest));
}
}
// nsIStreamListener method
NS_IMETHODIMP
nsFingerChannel::OnDataAvailable(nsIRequest *aRequest, nsISupports* aContext,
nsIInputStream *aInputStream, PRUint32 aSourceOffset,
PRUint32 aLength) {
mContentLength = aLength;
return mListener->OnDataAvailable(this, mResponseContext, aInputStream, aSourceOffset, aLength);
}
nsresult
nsFingerChannel::SendRequest(nsITransport* aTransport) {
// The text to send should already be in mUser
nsresult rv = NS_OK;
nsCOMPtr<nsISupports> result;
nsCOMPtr<nsIInputStream> charstream;
nsCString requestBuffer(mUser);
if (mLoadGroup) {
mLoadGroup->AddRequest(this, nsnull);
}
requestBuffer.Append(CRLF);
mRequest.Assign(requestBuffer);
rv = NS_NewCharInputStream(getter_AddRefs(result), mRequest);
if (NS_FAILED(rv)) return rv;
charstream = do_QueryInterface(result, &rv);
if (NS_FAILED(rv)) return rv;
rv = NS_AsyncWriteFromStream(getter_AddRefs(mTransportRequest),
aTransport, charstream,
0, requestBuffer.Length(), 0,
this, nsnull);
return rv;
}