gecko-dev/netwerk/protocol/data/src/nsDataChannel.cpp

547 lines
13 KiB
C++
Raw Normal View History

1999-09-03 00:39:55 +00:00
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
1999-09-03 00:39:55 +00:00
*
* 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.
1999-09-03 00:39:55 +00:00
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
1999-09-03 00:39:55 +00:00
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
1999-09-03 00:39:55 +00:00
*/
// data implementation
#include "nsDataChannel.h"
#include "nsIServiceManager.h"
#include "nsIEventQueueService.h"
#include "nsNetUtil.h"
1999-09-03 00:39:55 +00:00
#include "nsILoadGroup.h"
#include "plbase64.h"
#include "nsIInterfaceRequestor.h"
1999-09-03 00:39:55 +00:00
#include "nsIPipe.h"
#include "nsIBufferInputStream.h"
#include "nsIBufferOutputStream.h"
#include "nsXPIDLString.h"
1999-09-03 00:39:55 +00:00
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
1999-09-03 00:39:55 +00:00
// nsDataChannel methods
nsDataChannel::nsDataChannel() {
NS_INIT_REFCNT();
mContentLength = -1;
1999-09-03 00:39:55 +00:00
}
nsDataChannel::~nsDataChannel() {
}
NS_IMPL_ISUPPORTS3(nsDataChannel,
nsIDataChannel,
nsIChannel,
nsIRequest)
1999-09-03 00:39:55 +00:00
nsresult
nsDataChannel::Init(nsIURI* uri)
{
1999-09-03 00:39:55 +00:00
// we don't care about event sinks in data
nsresult rv;
// Data urls contain all the data within the url string itself.
mUrl = uri;
rv = ParseData();
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
typedef struct _writeData {
PRUint32 dataLen;
char *data;
} writeData;
static NS_METHOD
1999-09-03 00:39:55 +00:00
nsReadData(void* closure, // the data from
1999-09-09 22:18:44 +00:00
char* toRawSegment, // where to put the data
PRUint32 offset, // where to start
PRUint32 count, // how much data is there
PRUint32 *readCount) { // how much data was read
1999-09-03 00:39:55 +00:00
nsresult rv = NS_OK;
writeData *dataToWrite = (writeData*)closure;
PRUint32 write = PR_MIN(count, dataToWrite->dataLen - offset);
*readCount = 0;
if (offset == dataToWrite->dataLen)
1999-09-09 22:18:44 +00:00
return NS_OK; // *readCount == 0 is EOF
1999-09-03 00:39:55 +00:00
nsCRT::memcpy(toRawSegment, dataToWrite->data + offset, write);
*readCount = write;
return rv;
}
nsresult
nsDataChannel::ParseData() {
nsresult rv;
PRBool lBase64 = PR_FALSE;
NS_ASSERTION(mUrl, "no url in the data channel");
if (!mUrl) return NS_ERROR_NULL_POINTER;
nsXPIDLCString spec;
rv = mUrl->GetSpec(getter_Copies(spec));
1999-09-03 00:39:55 +00:00
if (NS_FAILED(rv)) return rv;
// move past "data:"
char *buffer = PL_strstr((const char*)spec, "data:");
1999-09-03 00:39:55 +00:00
if (!buffer) {
// malfored url
return NS_ERROR_MALFORMED_URI;
}
buffer += 5;
// First, find the start of the data
char *comma = PL_strchr(buffer, ',');
if (!comma) return NS_ERROR_FAILURE;
*comma = '\0';
// determine if the data is base64 encoded.
char *base64 = PL_strstr(buffer, "base64");
if (base64) {
lBase64 = PR_TRUE;
*base64 = '\0';
}
if (comma == buffer) {
// nothing but data
mContentType = "text/plain;charset=US-ASCII";
} else {
// everything else is content type
char *semiColon = PL_strchr(buffer, ';');
if (semiColon)
*semiColon = '\0';
nsCAutoString cType(buffer);
cType.ToLowerCase();
mContentType = cType.GetBuffer();
1999-09-03 00:39:55 +00:00
if (semiColon)
*semiColon = ';';
}
char *dataBuffer = nsnull;
PRBool cleanup = PR_FALSE;
mContentType.StripWhitespace();
if (!mContentType.Find("text")) {
// it's text, don't compress spaces
dataBuffer = comma+1;
} else {
// it's ascii encoded binary, don't let any spaces in
nsCAutoString dataBuf(comma+1);
dataBuf.StripWhitespace();
dataBuffer = dataBuf.ToNewCString();
cleanup = PR_TRUE;
}
1999-09-03 00:39:55 +00:00
nsCOMPtr<nsIBufferInputStream> bufInStream;
nsCOMPtr<nsIBufferOutputStream> bufOutStream;
1999-09-03 00:39:55 +00:00
rv = NS_NewPipe(getter_AddRefs(bufInStream), getter_AddRefs(bufOutStream));
1999-09-03 00:39:55 +00:00
if (NS_FAILED(rv)) return rv;
PRUint32 dataLen = PL_strlen(dataBuffer);
1999-09-03 00:39:55 +00:00
PRUint32 wrote;
writeData *dataToWrite = (writeData*)nsMemory::Alloc(sizeof(writeData));
1999-09-03 00:39:55 +00:00
if (!dataToWrite) return NS_ERROR_OUT_OF_MEMORY;
if (lBase64) {
*base64 = 'b';
2000-03-15 23:20:38 +00:00
PRInt32 resultLen = 0;
if (dataBuffer[dataLen-1] == '=') {
if (dataBuffer[dataLen-2] == '=')
2000-03-15 23:20:38 +00:00
resultLen = dataLen-2;
else
resultLen = dataLen-1;
} else {
resultLen = dataLen;
}
1999-09-03 00:39:55 +00:00
char * decodedData = PL_Base64Decode(dataBuffer, dataLen, nsnull);
if (!decodedData) return NS_ERROR_OUT_OF_MEMORY;
1999-09-03 00:39:55 +00:00
2000-03-15 23:20:38 +00:00
dataToWrite->dataLen = resultLen;
1999-09-03 00:39:55 +00:00
dataToWrite->data = decodedData;
rv = bufOutStream->WriteSegments(nsReadData, dataToWrite, dataToWrite->dataLen, &wrote);
1999-09-03 00:39:55 +00:00
nsMemory::Free(decodedData);
1999-09-03 00:39:55 +00:00
} else {
dataToWrite->dataLen = dataLen;
dataToWrite->data = dataBuffer;
1999-09-03 00:39:55 +00:00
rv = bufOutStream->WriteSegments(nsReadData, dataToWrite, dataLen, &wrote);
}
if (NS_FAILED(rv)) return rv;
// Initialize the content length of the data...
mContentLength = dataToWrite->dataLen;
1999-09-03 00:39:55 +00:00
rv = bufInStream->QueryInterface(NS_GET_IID(nsIInputStream), getter_AddRefs(mDataStream));
1999-09-03 00:39:55 +00:00
if (NS_FAILED(rv)) return rv;
*comma = ',';
nsMemory::Free(dataToWrite);
if (cleanup) nsMemory::Free(dataBuffer);
1999-09-03 00:39:55 +00:00
return NS_OK;
}
NS_METHOD
nsDataChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
{
nsDataChannel* dc = new nsDataChannel();
if (dc == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(dc);
nsresult rv = dc->QueryInterface(aIID, aResult);
NS_RELEASE(dc);
return rv;
}
////////////////////////////////////////////////////////////////////////////////
// nsIRequest methods:
NS_IMETHODIMP
nsDataChannel::GetName(PRUnichar* *result)
{
NS_NOTREACHED("nsDataChannel::GetName");
return NS_ERROR_NOT_IMPLEMENTED;
}
1999-09-03 00:39:55 +00:00
NS_IMETHODIMP
nsDataChannel::IsPending(PRBool *result)
{
NS_NOTREACHED("nsDataChannel::IsPending");
1999-09-03 00:39:55 +00:00
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDataChannel::GetStatus(nsresult *status)
{
*status = NS_OK;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::Cancel(nsresult status)
1999-09-03 00:39:55 +00:00
{
NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
NS_NOTREACHED("nsDataChannel::Cancel");
1999-09-03 00:39:55 +00:00
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDataChannel::Suspend(void)
{
NS_NOTREACHED("nsDataChannel::Suspend");
1999-09-03 00:39:55 +00:00
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDataChannel::Resume(void)
{
NS_NOTREACHED("nsDataChannel::Resume");
1999-09-03 00:39:55 +00:00
return NS_ERROR_NOT_IMPLEMENTED;
}
////////////////////////////////////////////////////////////////////////////////
// nsIChannel methods:
NS_IMETHODIMP
nsDataChannel::GetOriginalURI(nsIURI* *aURI)
{
*aURI = mOriginalURI ? mOriginalURI : mUrl;
NS_ADDREF(*aURI);
return NS_OK;
}
1999-09-03 00:39:55 +00:00
NS_IMETHODIMP
nsDataChannel::SetOriginalURI(nsIURI* aURI)
{
mOriginalURI = aURI;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::GetURI(nsIURI* *aURI)
1999-09-03 00:39:55 +00:00
{
*aURI = mUrl;
NS_IF_ADDREF(*aURI);
1999-09-03 00:39:55 +00:00
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::SetURI(nsIURI* aURI)
{
mUrl = aURI;
return NS_OK;
}
1999-09-03 00:39:55 +00:00
// This class
NS_IMETHODIMP
nsDataChannel::OpenInputStream(nsIInputStream **_retval)
1999-09-03 00:39:55 +00:00
{
// XXX we should honor the startPosition and count passed in.
*_retval = mDataStream;
NS_ADDREF(*_retval);
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::OpenOutputStream(nsIOutputStream **_retval)
1999-09-03 00:39:55 +00:00
{
// you can't write to a data url
NS_NOTREACHED("nsDataChannel::OpenOutputStream");
1999-09-03 00:39:55 +00:00
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDataChannel::AsyncRead(nsIStreamListener *aListener, nsISupports *ctxt)
1999-09-03 00:39:55 +00:00
{
nsresult rv;
nsCOMPtr<nsIEventQueue> eventQ;
nsCOMPtr<nsIStreamListener> listener;
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = eventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(eventQ));
if (NS_FAILED(rv)) return rv;
1999-09-03 00:39:55 +00:00
// we'll just fire everything off at once because we've already got all
// the data.
rv = NS_NewAsyncStreamListener(getter_AddRefs(listener), aListener, eventQ);
1999-09-03 00:39:55 +00:00
if (NS_FAILED(rv)) return rv;
rv = listener->OnStartRequest(this, ctxt);
if (NS_FAILED(rv)) return rv;
PRUint32 streamLen;
1999-09-09 22:18:44 +00:00
rv = mDataStream->Available(&streamLen);
1999-09-03 00:39:55 +00:00
if (NS_FAILED(rv)) return rv;
rv = listener->OnDataAvailable(this, ctxt, mDataStream, 0, streamLen);
if (NS_FAILED(rv)) return rv;
rv = listener->OnStopRequest(this, ctxt, NS_OK, nsnull);
return rv;
}
NS_IMETHODIMP
nsDataChannel::AsyncWrite(nsIInputStream *fromStream,
nsIStreamObserver *observer,
nsISupports *ctxt)
1999-09-03 00:39:55 +00:00
{
// you can't write to a data url
NS_NOTREACHED("nsDataChannel::AsyncWrite");
1999-09-03 00:39:55 +00:00
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDataChannel::GetLoadAttributes(PRUint32 *aLoadAttributes)
{
*aLoadAttributes = mLoadAttributes;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::SetLoadAttributes(PRUint32 aLoadAttributes)
{
mLoadAttributes = aLoadAttributes;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::GetContentType(char* *aContentType) {
// Parameter validation...
if (!aContentType) return NS_ERROR_NULL_POINTER;
if (mContentType.Length()) {
*aContentType = mContentType.ToNewCString();
1999-09-08 07:16:10 +00:00
if (!*aContentType) return NS_ERROR_OUT_OF_MEMORY;
1999-09-03 00:39:55 +00:00
} else {
NS_ASSERTION(0, "data protocol should have content type by now");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::SetContentType(const char *aContentType)
{
mContentType = aContentType;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::GetContentLength(PRInt32 *aContentLength)
{
*aContentLength = mContentLength;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::SetContentLength(PRInt32 aContentLength)
{
mContentLength = aContentLength;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::GetTransferOffset(PRUint32 *aTransferOffset)
{
NS_NOTREACHED("nsDataChannel::GetTransferOffset");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDataChannel::SetTransferOffset(PRUint32 aTransferOffset)
{
NS_NOTREACHED("nsDataChannel::SetTransferOffset");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDataChannel::GetTransferCount(PRInt32 *aTransferCount)
{
NS_NOTREACHED("nsDataChannel::GetTransferCount");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDataChannel::SetTransferCount(PRInt32 aTransferCount)
{
NS_NOTREACHED("nsDataChannel::SetTransferCount");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDataChannel::GetBufferSegmentSize(PRUint32 *aBufferSegmentSize)
{
*aBufferSegmentSize = mBufferSegmentSize;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::SetBufferSegmentSize(PRUint32 aBufferSegmentSize)
{
mBufferSegmentSize = aBufferSegmentSize;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::GetBufferMaxSize(PRUint32 *aBufferMaxSize)
{
*aBufferMaxSize = mBufferMaxSize;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::SetBufferMaxSize(PRUint32 aBufferMaxSize)
{
mBufferMaxSize = aBufferMaxSize;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::GetLocalFile(nsIFile* *file)
{
*file = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::GetPipeliningAllowed(PRBool *aPipeliningAllowed)
{
*aPipeliningAllowed = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::SetPipeliningAllowed(PRBool aPipeliningAllowed)
{
NS_NOTREACHED("SetPipeliningAllowed");
return NS_ERROR_NOT_IMPLEMENTED;
}
1999-09-03 00:39:55 +00:00
NS_IMETHODIMP
nsDataChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
1999-09-03 00:39:55 +00:00
{
*aLoadGroup = mLoadGroup;
NS_IF_ADDREF(*aLoadGroup);
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
mLoadGroup = aLoadGroup;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::GetOwner(nsISupports* *aOwner)
1999-09-03 00:39:55 +00:00
{
*aOwner = mOwner.get();
NS_IF_ADDREF(*aOwner);
1999-09-03 00:39:55 +00:00
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::SetOwner(nsISupports* aOwner)
1999-09-03 00:39:55 +00:00
{
mOwner = aOwner;
1999-09-03 00:39:55 +00:00
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
*aNotificationCallbacks = mCallbacks.get();
NS_IF_ADDREF(*aNotificationCallbacks);
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
mCallbacks = aNotificationCallbacks;
return NS_OK;
}
NS_IMETHODIMP
nsDataChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
{
*aSecurityInfo = nsnull;
return NS_OK;
}