1999-09-03 00:39:55 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
|
|
*
|
1999-11-06 03:40:37 +00:00
|
|
|
* 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
|
|
|
*
|
1999-11-06 03:40:37 +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
|
|
|
*
|
1999-11-06 03:40:37 +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
|
1999-11-06 03:40:37 +00:00
|
|
|
* 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"
|
1999-11-30 04:50:42 +00:00
|
|
|
#include "nsNetUtil.h"
|
1999-09-03 00:39:55 +00:00
|
|
|
#include "nsILoadGroup.h"
|
|
|
|
#include "plbase64.h"
|
1999-11-18 07:36:41 +00:00
|
|
|
#include "nsIInterfaceRequestor.h"
|
1999-09-03 00:39:55 +00:00
|
|
|
#include "nsIPipe.h"
|
|
|
|
#include "nsIBufferInputStream.h"
|
|
|
|
#include "nsIBufferOutputStream.h"
|
1999-12-02 22:02:30 +00:00
|
|
|
#include "nsXPIDLString.h"
|
1999-09-03 00:39:55 +00:00
|
|
|
|
1999-12-02 22:02:30 +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();
|
1999-09-09 05:07:30 +00:00
|
|
|
|
|
|
|
mContentLength = -1;
|
1999-09-03 00:39:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsDataChannel::~nsDataChannel() {
|
|
|
|
}
|
|
|
|
|
1999-11-17 08:14:52 +00:00
|
|
|
NS_IMPL_ISUPPORTS3(nsDataChannel,
|
|
|
|
nsIDataChannel,
|
|
|
|
nsIChannel,
|
|
|
|
nsIRequest)
|
1999-09-03 00:39:55 +00:00
|
|
|
|
|
|
|
nsresult
|
1999-11-17 08:14:52 +00:00
|
|
|
nsDataChannel::Init(const char* verb,
|
|
|
|
nsIURI* uri,
|
|
|
|
nsILoadGroup* aLoadGroup,
|
1999-11-18 07:36:41 +00:00
|
|
|
nsIInterfaceRequestor* notificationCallbacks,
|
1999-11-17 08:14:52 +00:00
|
|
|
nsLoadFlags loadAttributes,
|
1999-12-04 10:01:32 +00:00
|
|
|
nsIURI* originalURI,
|
|
|
|
PRUint32 bufferSegmentSize,
|
|
|
|
PRUint32 bufferMaxSize)
|
1999-10-26 09:16:24 +00:00
|
|
|
{
|
1999-09-03 00:39:55 +00:00
|
|
|
// we don't care about event sinks in data
|
|
|
|
nsresult rv;
|
|
|
|
|
1999-11-17 08:14:52 +00:00
|
|
|
rv = SetLoadAttributes(loadAttributes);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
rv = SetLoadGroup(aLoadGroup);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
rv = SetNotificationCallbacks(notificationCallbacks);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
1999-09-03 00:39:55 +00:00
|
|
|
|
|
|
|
// Data urls contain all the data within the url string itself.
|
1999-10-26 09:16:24 +00:00
|
|
|
mOriginalURI = originalURI ? originalURI : uri;
|
1999-09-03 00:39:55 +00:00
|
|
|
mUrl = uri;
|
1999-12-04 10:01:32 +00:00
|
|
|
mBufferSegmentSize = bufferSegmentSize;
|
|
|
|
mBufferMaxSize = bufferMaxSize;
|
1999-09-03 00:39:55 +00:00
|
|
|
|
|
|
|
rv = ParseData();
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct _writeData {
|
|
|
|
PRUint32 dataLen;
|
|
|
|
char *data;
|
|
|
|
} writeData;
|
|
|
|
|
|
|
|
NS_METHOD
|
|
|
|
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;
|
|
|
|
|
1999-12-02 22:02:30 +00:00
|
|
|
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:"
|
1999-12-02 22:02:30 +00:00
|
|
|
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';
|
|
|
|
|
1999-09-14 19:42:09 +00:00
|
|
|
nsCAutoString cType(buffer);
|
|
|
|
cType.ToLowerCase();
|
|
|
|
mContentType = cType.GetBuffer();
|
1999-09-03 00:39:55 +00:00
|
|
|
|
|
|
|
if (semiColon)
|
|
|
|
*semiColon = ';';
|
|
|
|
}
|
|
|
|
|
1999-12-02 22:02:30 +00:00
|
|
|
nsCOMPtr<nsIBufferInputStream> bufInStream;
|
|
|
|
nsCOMPtr<nsIBufferOutputStream> bufOutStream;
|
1999-09-03 00:39:55 +00:00
|
|
|
|
1999-12-02 22:02:30 +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(comma+1);
|
|
|
|
PRUint32 wrote;
|
1999-11-18 20:05:15 +00:00
|
|
|
writeData *dataToWrite = (writeData*)nsAllocator::Alloc(sizeof(writeData));
|
1999-09-03 00:39:55 +00:00
|
|
|
if (!dataToWrite) return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
if (lBase64) {
|
|
|
|
*base64 = 'b';
|
|
|
|
char *decodedData = (char*)nsAllocator::Alloc((dataLen * 3)/4);
|
|
|
|
if (!decodedData) return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
decodedData = PL_Base64Decode(comma+1, dataLen, decodedData);
|
|
|
|
|
|
|
|
dataToWrite->dataLen = (dataLen * 3)/4;
|
|
|
|
dataToWrite->data = decodedData;
|
|
|
|
|
|
|
|
rv = bufOutStream->WriteSegments(nsReadData, dataToWrite, (dataLen * 3)/4, &wrote);
|
|
|
|
|
|
|
|
nsAllocator::Free(decodedData);
|
|
|
|
} else {
|
|
|
|
dataToWrite->dataLen = dataLen;
|
|
|
|
dataToWrite->data = comma + 1;
|
|
|
|
|
|
|
|
rv = bufOutStream->WriteSegments(nsReadData, dataToWrite, dataLen, &wrote);
|
|
|
|
}
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
1999-09-09 05:07:30 +00:00
|
|
|
// Initialize the content length of the data...
|
|
|
|
mContentLength = dataToWrite->dataLen;
|
1999-09-03 00:39:55 +00:00
|
|
|
|
1999-11-17 08:14:52 +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 = ',';
|
|
|
|
|
1999-09-09 05:07:30 +00:00
|
|
|
nsAllocator::Free(dataToWrite);
|
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::IsPending(PRBool *result)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::Cancel(void)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::Suspend(void)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::Resume(void)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIChannel methods:
|
|
|
|
|
1999-10-26 09:16:24 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::GetOriginalURI(nsIURI * *aURI)
|
|
|
|
{
|
|
|
|
*aURI = mOriginalURI;
|
|
|
|
NS_ADDREF(*aURI);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-09-03 00:39:55 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::GetURI(nsIURI * *aURI)
|
|
|
|
{
|
|
|
|
*aURI = mUrl;
|
1999-11-17 08:14:52 +00:00
|
|
|
NS_IF_ADDREF(*aURI);
|
1999-09-03 00:39:55 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This class
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::OpenInputStream(PRUint32 startPosition, PRInt32 readCount,
|
|
|
|
nsIInputStream **_retval)
|
|
|
|
{
|
|
|
|
// XXX we should honor the startPosition and count passed in.
|
|
|
|
|
|
|
|
*_retval = mDataStream;
|
|
|
|
NS_ADDREF(*_retval);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::OpenOutputStream(PRUint32 startPosition, nsIOutputStream **_retval)
|
|
|
|
{
|
|
|
|
// you can't write to a data url
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
1999-10-06 08:26:01 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::AsyncOpen(nsIStreamObserver *observer, nsISupports* ctxt)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
1999-09-03 00:39:55 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::AsyncRead(PRUint32 startPosition, PRInt32 readCount,
|
|
|
|
nsISupports *ctxt,
|
|
|
|
nsIStreamListener *aListener)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
1999-12-02 22:02:30 +00:00
|
|
|
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.
|
1999-12-02 22:02:30 +00:00
|
|
|
rv = NS_NewAsyncStreamListener(aListener, eventQ, getter_AddRefs(listener));
|
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,
|
|
|
|
PRUint32 startPosition,
|
|
|
|
PRInt32 writeCount,
|
|
|
|
nsISupports *ctxt,
|
|
|
|
nsIStreamObserver *observer)
|
|
|
|
{
|
|
|
|
// you can't write to a data url
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
1999-09-09 05:07:30 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::GetContentLength(PRInt32 *aContentLength)
|
|
|
|
{
|
|
|
|
*aContentLength = mContentLength;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-09-03 00:39:55 +00:00
|
|
|
NS_IMETHODIMP
|
1999-11-17 08:14:52 +00:00
|
|
|
nsDataChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
|
1999-09-03 00:39:55 +00:00
|
|
|
{
|
|
|
|
*aLoadGroup = mLoadGroup;
|
|
|
|
NS_IF_ADDREF(*aLoadGroup);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
1999-11-17 08:14:52 +00:00
|
|
|
nsDataChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
|
|
|
|
{
|
|
|
|
mLoadGroup = aLoadGroup;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDataChannel::GetOwner(nsISupports* *aOwner)
|
1999-09-03 00:39:55 +00:00
|
|
|
{
|
1999-09-11 19:43:06 +00:00
|
|
|
*aOwner = mOwner.get();
|
1999-09-11 18:45:36 +00:00
|
|
|
NS_IF_ADDREF(*aOwner);
|
1999-09-03 00:39:55 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
1999-11-17 08:14:52 +00:00
|
|
|
nsDataChannel::SetOwner(nsISupports* aOwner)
|
1999-09-03 00:39:55 +00:00
|
|
|
{
|
1999-09-11 18:45:36 +00:00
|
|
|
mOwner = aOwner;
|
1999-09-03 00:39:55 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-09-11 18:45:36 +00:00
|
|
|
|
1999-11-17 08:14:52 +00:00
|
|
|
NS_IMETHODIMP
|
1999-11-18 07:36:41 +00:00
|
|
|
nsDataChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
|
1999-11-17 08:14:52 +00:00
|
|
|
{
|
|
|
|
*aNotificationCallbacks = mCallbacks.get();
|
|
|
|
NS_IF_ADDREF(*aNotificationCallbacks);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
1999-11-18 07:36:41 +00:00
|
|
|
nsDataChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
|
1999-11-17 08:14:52 +00:00
|
|
|
{
|
|
|
|
mCallbacks = aNotificationCallbacks;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|