diff --git a/extensions/webdav/public/nsIWebDAVListener.idl b/extensions/webdav/public/nsIWebDAVListener.idl index 2b556a206e3..35f03db2940 100644 --- a/extensions/webdav/public/nsIWebDAVListener.idl +++ b/extensions/webdav/public/nsIWebDAVListener.idl @@ -44,9 +44,7 @@ interface nsIWebDAVMetadataListener : nsISupports { void onGetPropertyNamesResult(in unsigned long aStatusCode, in AUTF8String aResource, - in PRUint32 nameCount, - [array, size_is(nameCount)] - in string propertyNames); + in nsIProperties aPropertyNames); void onGetPropertiesResult(in unsigned long aStatusCode, in AUTF8String aResource, @@ -59,7 +57,7 @@ interface nsIWebDAVMetadataListener : nsISupports void onMetadataComplete(in unsigned long aStatusCode, in nsIWebDAVResource aResource, - in string method); + in ACString method); }; [scriptable,uuid(e198a0fd-9e62-4299-84e3-d93c0cd68881)] @@ -73,17 +71,8 @@ interface nsIWebDAVOperationListener : nsISupports in nsIWebDAVResource aResource); void onCopyResult(in unsigned long aStatusCode, in nsIWebDAVResource aResource); -}; - -[scriptable,uuid(c368540a-b4c3-45d7-b20f-1474c140c0dd)] -interface nsIWebDAVChildrenListener : nsISupports -{ - void hasChild(in unsigned long aStatusCode, - in AUTF8String aParent, - in AUTF8String aChild); - - void childListComplete(in AUTF8String aParent); - - void childListFailed(in unsigned long aStatusCode, - in AUTF8String aParent); + void onPutResult(in unsigned long aStatusCode, + in nsIWebDAVResource aResource); + void onGetResult(in unsigned long aStatusCode, + in nsIWebDAVResource aResource); }; diff --git a/extensions/webdav/public/nsIWebDAVService.idl b/extensions/webdav/public/nsIWebDAVService.idl index d74376854df..8c458454d50 100644 --- a/extensions/webdav/public/nsIWebDAVService.idl +++ b/extensions/webdav/public/nsIWebDAVService.idl @@ -39,6 +39,7 @@ #include "nsIWebDAVResource.idl" interface nsIStreamListener; +interface nsIOutputStream; [scriptable,uuid(173ef52e-2a2f-45ca-8f9f-abc7429163c2)] interface nsIWebDAVService : nsISupports @@ -67,13 +68,15 @@ interface nsIWebDAVService : nsISupports void getResourceOptions(in nsIWebDAVResource resource, in nsIWebDAVMetadataListener listener); - void getChildren(in nsIWebDAVResource resource, - in PRUint32 depth, in nsIWebDAVChildrenListener listener); - void get(in nsIWebDAVResource resource, in nsIStreamListener listener); + void getToOutputStream(in nsIWebDAVResource resource, + in nsIOutputStream stream, + in nsIWebDAVOperationListener listener); + void put(in nsIWebDAVResourceWithData resource, + in ACString contentType, in nsIWebDAVOperationListener listener); void remove(in nsIWebDAVResource resource, diff --git a/extensions/webdav/src/Makefile.in b/extensions/webdav/src/Makefile.in index f0523262a4b..5acc3f02646 100644 --- a/extensions/webdav/src/Makefile.in +++ b/extensions/webdav/src/Makefile.in @@ -58,9 +58,10 @@ REQUIRES = xpcom \ xmlextras \ $(NULL) -CPPSRCS = nsWebDAVService.cpp \ - nsPropfindStreamListener.cpp \ - nsWebDAVUtils.cpp \ +CPPSRCS = nsWebDAVService.cpp \ + nsPropfindStreamListener.cpp \ + nsOperationStreamListeners.cpp \ + nsWebDAVUtils.cpp \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/extensions/webdav/src/nsOperationStreamListeners.cpp b/extensions/webdav/src/nsOperationStreamListeners.cpp new file mode 100644 index 00000000000..df00accf206 --- /dev/null +++ b/extensions/webdav/src/nsOperationStreamListeners.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +// vim:expandtab:ts=4 sw=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. + * + * The Initial Developer of the Original Code is Oracle Corporation. + * Portions created by Oracle Corporation are Copyright (C) 2004 + * by Oracle Corporation. All Rights Reserved. + * + * Contributor(s): + * Mike Shaver (original author) + * + * 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 + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIHttpChannel.h" +#include "nsIIOService.h" +#include "nsNetUtil.h" + +#include "nsIWebDAVResource.h" +#include "nsIWebDAVListener.h" + +#include "nsWebDAVInternal.h" + +class OperationStreamListener : public nsIStreamListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + + enum OperationMode { + PUT, GET + }; + + OperationStreamListener(nsIWebDAVResource *resource, + nsIWebDAVOperationListener *listener, + OperationMode mode) : + mResource(resource), mListener(listener), mMode(mode) { } + virtual ~OperationStreamListener() { } + +protected: + + nsCOMPtr mResource; + nsCOMPtr mListener; + OperationMode mMode; + +}; + +NS_IMPL_ISUPPORTS1(OperationStreamListener, nsIStreamListener); + +NS_IMETHODIMP +OperationStreamListener::OnStartRequest(nsIRequest *aRequest, + nsISupports *aContext) +{ + return NS_OK; +} + +NS_IMETHODIMP +OperationStreamListener::OnStopRequest(nsIRequest *aRequest, + nsISupports *aContext, + nsresult aStatusCode) +{ + switch (mMode) { + case PUT: + mListener->OnPutResult(aStatusCode, mResource); + break; + case GET: + mListener->OnGetResult(aStatusCode, mResource); + break; + } + return NS_OK; +} + +NS_IMETHODIMP +OperationStreamListener::OnDataAvailable(nsIRequest *aRequest, + nsISupports *aContext, + nsIInputStream *aInputStream, + PRUint32 offset, PRUint32 count) +{ + aRequest->Cancel(NS_BINDING_ABORTED); + return NS_BINDING_ABORTED; +} + +nsIStreamListener * +NS_WD_NewPutOperationStreamListener(nsIWebDAVResource *resource, + nsIWebDAVOperationListener *listener) +{ + nsCOMPtr osl = + return new OperationStreamListener(resource, listener, + OperationStreamListener::PUT); +} + +nsIRequestObserver * +NS_WD_NewGetOperationRequestObserver(nsIWebDAVResource *resource, + nsIWebDAVOperationListener *listener) +{ + nsCOMPtr osl = + do_QueryInterface(new OperationStreamListener(resource, listener, + OperationStreamListener::GET)); + return osl.get(); +} diff --git a/extensions/webdav/src/nsPropfindStreamListener.cpp b/extensions/webdav/src/nsPropfindStreamListener.cpp index b70d4a255d5..8e34b1a52fb 100644 --- a/extensions/webdav/src/nsPropfindStreamListener.cpp +++ b/extensions/webdav/src/nsPropfindStreamListener.cpp @@ -19,6 +19,7 @@ * * Contributor(s): * Mike Shaver (original author) + * * 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 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), @@ -62,8 +63,9 @@ public: NS_DECL_NSISTREAMLISTENER PropfindStreamListener(nsIWebDAVResource *resource, - nsIWebDAVMetadataListener *listener) : - mResource(resource), mListener(listener) { } + nsIWebDAVMetadataListener *listener, + PRBool isPropname) : + mResource(resource), mListener(listener), mNameOnly(isPropname) { } virtual ~PropfindStreamListener() { } protected: @@ -79,7 +81,8 @@ protected: mResource->GetUrlSpec(spec); LOG(("PROPFIND completed for %s: %d", spec.get(), aStatusCode)); } - mListener->OnMetadataComplete(aStatusCode, mResource, "PROPFIND"); + mListener->OnMetadataComplete(aStatusCode, mResource, + NS_LITERAL_CSTRING("PROPFIND")); return NS_OK; } @@ -91,6 +94,7 @@ protected: nsCOMPtr mListener; nsCOMPtr mXMLDoc; nsCString mBody; + PRBool mNameOnly; }; NS_IMPL_ISUPPORTS1(PropfindStreamListener, nsIStreamListener) @@ -152,6 +156,15 @@ PropfindStreamListener::PropertiesFromPropElt(nsIDOMElement *propElt, rv = node->GetLocalName(propName); NS_ENSURE_SUCCESS(rv, rv); + NS_ConvertUTF16toUTF8 propkey(nsStr + NS_LITERAL_STRING(" ") + + propName); + if (mNameOnly) { + LOG((" propname: %s", propkey.get())); + rv = props->Set(propkey.get(), nsnull); + NS_ENSURE_SUCCESS(rv, rv); + continue; + } + nsCOMPtr encoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/xml", &rv); @@ -175,8 +188,6 @@ PropfindStreamListener::PropertiesFromPropElt(nsIDOMElement *propElt, NS_ENSURE_SUCCESS(rv, rv); suppString->SetData(valueStr); - NS_ConvertUTF16toUTF8 propkey(nsStr + NS_LITERAL_STRING(" ") + - propName); LOG((" %s = %s", propkey.get(), NS_ConvertUTF16toUTF8(valueStr).get())); rv = props->Set(propkey.get(), suppString); @@ -240,7 +251,14 @@ PropfindStreamListener::ProcessResponse(nsIDOMElement *responseElt) PRInt32 statusVal = nsCAutoString(Substring(statusUTF8, 8)).ToInteger(&res, 10); NS_ENSURE_SUCCESS(res, (nsresult)res); - mListener->OnGetPropertiesResult((PRUint32)statusVal, hrefUTF8, props); + + if (mNameOnly) { + mListener->OnGetPropertyNamesResult((PRUint32)statusVal, hrefUTF8, + props); + } else { + mListener->OnGetPropertiesResult((PRUint32)statusVal, hrefUTF8, + props); + } } return NS_OK; } @@ -346,8 +364,9 @@ PropfindStreamListener::OnDataAvailable(nsIRequest *aRequest, } nsIStreamListener * -NS_NewPropfindStreamListener(nsIWebDAVResource *resource, - nsIWebDAVMetadataListener *listener) +NS_WD_NewPropfindStreamListener(nsIWebDAVResource *resource, + nsIWebDAVMetadataListener *listener, + PRBool isPropname) { - return new PropfindStreamListener(resource, listener); + return new PropfindStreamListener(resource, listener, isPropname); } diff --git a/extensions/webdav/src/nsWebDAVInternal.h b/extensions/webdav/src/nsWebDAVInternal.h index ee81b5ef44e..1dbf74a756d 100644 --- a/extensions/webdav/src/nsWebDAVInternal.h +++ b/extensions/webdav/src/nsWebDAVInternal.h @@ -42,6 +42,7 @@ #include "nsString.h" class nsIStreamListener; +class nsIRequestObserver; #include "nsIDOMElement.h" #include "nsIWebDAVListener.h" @@ -53,8 +54,17 @@ class nsIStreamListener; extern PRLogModuleInfo *gDAVLog; nsIStreamListener * -NS_NewPropfindStreamListener(nsIWebDAVResource *resource, - nsIWebDAVMetadataListener *listener); +NS_WD_NewPropfindStreamListener(nsIWebDAVResource *resource, + nsIWebDAVMetadataListener *listener, + PRBool isPropname); + +nsIStreamListener * +NS_WD_NewPutOperationStreamListener(nsIWebDAVResource *resource, + nsIWebDAVOperationListener *listener); + +nsIRequestObserver * +NS_WD_NewGetOperationRequestObserver(nsIWebDAVResource *resource, + nsIWebDAVOperationListener *listener); nsresult NS_WD_GetElementByTagName(nsIDOMElement *parentElt, const nsAString &tagName, @@ -64,4 +74,9 @@ nsresult NS_WD_ElementTextChildValue(nsIDOMElement *elt, const nsAString &tagName, nsAString &value); +nsresult +NS_WD_AppendElementWithNS(nsIDOMDocument *doc, nsIDOMNode *parent, + const nsAString& ns, const nsAString& tagName, + nsIDOMElement **child); + #endif /* nsWebDAVInternal_h__ */ diff --git a/extensions/webdav/src/nsWebDAVService.cpp b/extensions/webdav/src/nsWebDAVService.cpp index 86631e16860..74ae766ae35 100644 --- a/extensions/webdav/src/nsWebDAVService.cpp +++ b/extensions/webdav/src/nsWebDAVService.cpp @@ -48,10 +48,20 @@ #include "nsIHttpChannel.h" #include "nsIIOService.h" #include "nsNetUtil.h" +#include "nsIStorageStream.h" +#include "nsIUploadChannel.h" +#include "nsContentCID.h" + +#include "nsIDOMXMLDocument.h" #include "nsIDOMElement.h" #include "nsIDOMNodeList.h" #include "nsIDOM3Node.h" +#include "nsIPrivateDOMImplementation.h" // I don't even pretend any more +#include "nsIDOMDOMImplementation.h" + +#include "nsIDocument.h" +#include "nsIDocumentEncoder.h" #include "nsIDOMParser.h" @@ -68,8 +78,28 @@ public: protected: nsresult EnsureIOService(); nsresult ChannelFromResource(nsIWebDAVResource *resource, - nsIHttpChannel** channel); + nsIHttpChannel** channel, + nsIURI ** resourceURI = 0); + + nsresult ChannelWithStreamFromResource(nsIWebDAVResource *resource, + nsIHttpChannel **channel, + nsIInputStream **stream); + + nsresult CreatePropfindDocument(nsIURI *resourceURI, + nsIDOMDocument **requestDoc, + nsIDOMElement **propfindElt); + + nsresult PropfindInternal(nsIWebDAVResource *resource, PRUint32 propCount, + const char **properties, PRBool withDepth, + nsIWebDAVMetadataListener *listener, + PRBool namesOnly); + + nsresult SendPropfindDocumentToChannel(nsIDocument *doc, + nsIHttpChannel *channel, + nsIStreamListener *listener, + PRBool withDepth); nsCOMPtr mIOService; // XXX weak? + nsAutoString mDAVNSString; // "DAV:" }; NS_IMPL_ISUPPORTS1_CI(nsWebDAVService, nsIWebDAVService) @@ -93,13 +123,149 @@ nsWebDAVService::EnsureIOService() return NS_OK; } +nsresult +nsWebDAVService::SendPropfindDocumentToChannel(nsIDocument *doc, + nsIHttpChannel *channel, + nsIStreamListener *listener, + PRBool withDepth) +{ + nsCOMPtr storageStream; + // Why do I have to pick values for these? I just want to store some data + // for stream access! (And how would script set these?) + nsresult rv = NS_NewStorageStream(4 * 1024, 256 * 1024, + getter_AddRefs(storageStream)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr storageOutputStream; + rv = storageStream->GetOutputStream(0, + getter_AddRefs(storageOutputStream)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr encoder = + do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/xml", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = encoder->Init(doc, NS_LITERAL_STRING("text/xml"), + nsIDocumentEncoder::OutputEncodeBasicEntities); + NS_ENSURE_SUCCESS(rv, rv); + + encoder->SetCharset(NS_LITERAL_CSTRING("UTF-8")); + rv = encoder->EncodeToStream(storageOutputStream); + NS_ENSURE_SUCCESS(rv, rv); + + storageOutputStream->Close(); + + // You gotta really want it. + if (PR_LOG_TEST(gDAVLog, 5)) { + nsCOMPtr logInputStream; + rv = storageStream->NewInputStream(0, getter_AddRefs(logInputStream)); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 len, read; + logInputStream->Available(&len); + + char *buf = new char[len+1]; + memset(buf, 0, len+1); + logInputStream->Read(buf, len, &read); + NS_ASSERTION(len == read, "short read on closed storage stream?"); + LOG(("XML:\n\n%*s\n\n", len, buf)); + + delete [] buf; + } + + nsCOMPtr inputStream; + rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr uploadChannel = do_QueryInterface(channel, &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = uploadChannel->SetUploadStream(inputStream, + NS_LITERAL_CSTRING("text/xml"), -1); + NS_ENSURE_SUCCESS(rv, rv); + + channel->SetRequestMethod(NS_LITERAL_CSTRING("PROPFIND")); + + // XXX I wonder how many compilers this will break... + const nsACString &depthValue = withDepth ? NS_LITERAL_CSTRING("1") : + NS_LITERAL_CSTRING("0"); + channel->SetRequestHeader(NS_LITERAL_CSTRING("Depth"), depthValue, false); + + if (LOG_ENABLED()) { + nsCOMPtr uri; + channel->GetURI(getter_AddRefs(uri)); + nsCAutoString spec; + uri->GetSpec(spec); + LOG(("PROPFIND starting for %s", spec.get())); + } + + return channel->AsyncOpen(listener, channel); +} + +nsresult +nsWebDAVService::CreatePropfindDocument(nsIURI *resourceURI, + nsIDOMDocument **requestDoc, + nsIDOMElement **propfindElt) +{ + nsresult rv; + static NS_DEFINE_CID(kDOMDOMDOMDOMImplementationCID, + NS_DOM_IMPLEMENTATION_CID); + nsCOMPtr + implementation(do_CreateInstance(kDOMDOMDOMDOMImplementationCID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr + privImpl(do_QueryInterface(implementation)); + privImpl->Init(resourceURI); + + nsCOMPtr doc; + nsAutoString emptyString; + rv = implementation->CreateDocument(mDAVNSString, emptyString, nsnull, + getter_AddRefs(doc)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr baseDoc = do_QueryInterface(doc); + baseDoc->SetXMLDeclaration(NS_LITERAL_STRING("1.0"), emptyString, + emptyString); + baseDoc->SetDocumentURI(resourceURI); + + nsCOMPtr elt; + rv = NS_WD_AppendElementWithNS(doc, doc, mDAVNSString, NS_LITERAL_STRING("propfind"), + getter_AddRefs(elt)); + elt->SetPrefix(NS_LITERAL_STRING("D")); + NS_ENSURE_SUCCESS(rv, rv); + + *requestDoc = doc.get(); + NS_ADDREF(*requestDoc); + *propfindElt = elt.get(); + NS_ADDREF(*propfindElt); + + return NS_OK; +} + +nsresult +nsWebDAVService::ChannelWithStreamFromResource(nsIWebDAVResource *resource, + nsIHttpChannel **channel, + nsIInputStream **stream) +{ + nsresult rv = ChannelFromResource(resource, channel); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr resWithData = + do_QueryInterface(resource, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + return resWithData->GetData(stream); +} + nsresult nsWebDAVService::ChannelFromResource(nsIWebDAVResource *resource, - nsIHttpChannel **channel) + nsIHttpChannel **channel, + nsIURI **resourceURI) { ENSURE_IO_SERVICE(); nsCAutoString spec; + nsresult rv = resource->GetUrlSpec(spec); NS_ENSURE_SUCCESS(rv, rv); if (spec.IsEmpty()) { @@ -115,10 +281,17 @@ nsWebDAVService::ChannelFromResource(nsIWebDAVResource *resource, rv = mIOService->NewChannelFromURI(uri, getter_AddRefs(baseChannel)); NS_ENSURE_SUCCESS(rv, rv); + if (resourceURI) { + *resourceURI = uri.get(); + NS_ADDREF(*resourceURI); + } + return CallQueryInterface(baseChannel, channel); } -nsWebDAVService::nsWebDAVService() +nsWebDAVService::nsWebDAVService() : + mDAVNSString(NS_LITERAL_STRING("DAV:")) + { gDAVLog = PR_NewLogModule("webdav"); } @@ -147,46 +320,114 @@ nsWebDAVService::GetResourcePropertyNames(nsIWebDAVResource *resource, PRBool withDepth, nsIWebDAVMetadataListener *listener) { - return NS_ERROR_NOT_IMPLEMENTED; + return PropfindInternal(resource, 0, nsnull, withDepth, + listener, PR_TRUE); } - NS_IMETHODIMP nsWebDAVService::GetResourceProperties(nsIWebDAVResource *resource, PRUint32 propCount, const char **properties, PRBool withDepth, nsIWebDAVMetadataListener *listener) +{ + return PropfindInternal(resource, propCount, properties, withDepth, + listener, PR_FALSE); +} + +nsresult +nsWebDAVService::PropfindInternal(nsIWebDAVResource *resource, + PRUint32 propCount, + const char **properties, + PRBool withDepth, + nsIWebDAVMetadataListener *listener, + PRBool namesOnly) { nsresult rv; NS_ENSURE_ARG(resource); NS_ENSURE_ARG(listener); + nsCOMPtr resourceURI; nsCOMPtr channel; - rv = ChannelFromResource(resource, getter_AddRefs(channel)); + rv = ChannelFromResource(resource, getter_AddRefs(channel), + getter_AddRefs(resourceURI)); if (NS_FAILED(rv)) return rv; - // XXX I wonder how many compilers this will break... - const nsACString &depthValue = withDepth ? NS_LITERAL_CSTRING("1") : - NS_LITERAL_CSTRING("0"); - channel->SetRequestMethod(NS_LITERAL_CSTRING("PROPFIND")); - channel->SetRequestHeader(NS_LITERAL_CSTRING("Depth"), depthValue, false); + nsCOMPtr requestDoc; + nsCOMPtr propfindElt; + rv = CreatePropfindDocument(resourceURI, getter_AddRefs(requestDoc), + getter_AddRefs(propfindElt)); + NS_ENSURE_SUCCESS(rv, rv); - if (LOG_ENABLED()) { - nsCAutoString spec; - resource->GetUrlSpec(spec); - LOG(("PROPFIND starting for %s", spec.get())); + if (namesOnly) { + nsCOMPtr allpropElt; + rv = NS_WD_AppendElementWithNS(requestDoc, propfindElt, + mDAVNSString, NS_LITERAL_STRING("propname"), + getter_AddRefs(allpropElt)); + NS_ENSURE_SUCCESS(rv, rv); + } else if (propCount == 0) { + nsCOMPtr allpropElt; + rv = NS_WD_AppendElementWithNS(requestDoc, propfindElt, + mDAVNSString, NS_LITERAL_STRING("allprop"), + getter_AddRefs(allpropElt)); + NS_ENSURE_SUCCESS(rv, rv); + } else { + nsCOMPtr propElt; + rv = NS_WD_AppendElementWithNS(requestDoc, propfindElt, + mDAVNSString, NS_LITERAL_STRING("prop"), + getter_AddRefs(propElt)); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRUint32 i = 0; i < propCount; i++) { + nsDependentCString fullpropName(properties[i]); + + // This string math is _ridiculous_. It better compile to a total of + // 5 instructions, or I'm ripping it all out and doing my own looping. + + nsACString::const_iterator start, saveStart, end, saveEnd; + fullpropName.BeginReading(start); + fullpropName.BeginReading(saveStart); + fullpropName.EndReading(end); + fullpropName.EndReading(saveEnd); + RFindInReadable(NS_LITERAL_CSTRING(" "), start, end); + + if (start == end) { + nsCAutoString msg(NS_LITERAL_CSTRING("Illegal property name ") + + fullpropName); + NS_WARNING(msg.get()); + return NS_ERROR_INVALID_ARG; + } + + if (LOG_ENABLED()) { + nsACString::const_iterator s = start; + + nsCAutoString propNamespace(nsDependentCSubstring(saveStart, s)); + nsCAutoString propName(nsDependentCSubstring(++s, saveEnd)); + + LOG(("prop ns: '%s', name: '%s'", propNamespace.get(), propName.get())); + } + + NS_ConvertASCIItoUTF16 propNamespace(nsDependentCSubstring(saveStart, start)), + propName(nsDependentCSubstring(++start, saveEnd)); + + nsCOMPtr junk; + rv = NS_WD_AppendElementWithNS(requestDoc, propElt, propNamespace, + propName, getter_AddRefs(junk)); + NS_ENSURE_SUCCESS(rv, rv); + } } nsCOMPtr streamListener = - NS_NewPropfindStreamListener(resource, listener); + NS_WD_NewPropfindStreamListener(resource, listener, namesOnly); if (!streamListener) return NS_ERROR_OUT_OF_MEMORY; - - return channel->AsyncOpen(streamListener, channel); + + nsCOMPtr requestBaseDoc = do_QueryInterface(requestDoc); + return SendPropfindDocumentToChannel(requestBaseDoc, channel, + streamListener, withDepth); } NS_IMETHODIMP @@ -196,13 +437,6 @@ nsWebDAVService::GetResourceOptions(nsIWebDAVResource *resource, return NS_ERROR_NOT_IMPLEMENTED; } -NS_IMETHODIMP -nsWebDAVService::GetChildren(nsIWebDAVResource *resource, PRUint32 depth, - nsIWebDAVChildrenListener *listener) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - NS_IMETHODIMP nsWebDAVService::Get(nsIWebDAVResource *resource, nsIStreamListener *listener) { @@ -215,11 +449,61 @@ nsWebDAVService::Get(nsIWebDAVResource *resource, nsIStreamListener *listener) return channel->AsyncOpen(listener, nsnull); } +NS_IMETHODIMP +nsWebDAVService::GetToOutputStream(nsIWebDAVResource *resource, + nsIOutputStream *stream, + nsIWebDAVOperationListener *listener) +{ + nsresult rv; + + nsCOMPtr getObserver = + NS_WD_NewGetOperationRequestObserver(resource, listener); + if (!getObserver) + return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr streamListener; + rv = NS_NewSimpleStreamListener(getter_AddRefs(streamListener), + stream, getObserver); + NS_ENSURE_SUCCESS(rv, rv); + + return Get(resource, streamListener); +} + NS_IMETHODIMP nsWebDAVService::Put(nsIWebDAVResourceWithData *resource, + const nsACString& contentType, nsIWebDAVOperationListener *listener) { - return NS_ERROR_NOT_IMPLEMENTED; + nsCOMPtr channel; + nsCOMPtr stream; + nsresult rv = ChannelWithStreamFromResource(resource, + getter_AddRefs(channel), + getter_AddRefs(stream)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr uploadChannel = do_QueryInterface(channel, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = uploadChannel->SetUploadStream(stream, contentType, -1); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr streamListener = + NS_WD_NewPutOperationStreamListener(resource, listener); + + if (!streamListener) + return NS_ERROR_OUT_OF_MEMORY; + + channel->SetRequestMethod(NS_LITERAL_CSTRING("PUT")); + + if (LOG_ENABLED()) { + nsCOMPtr uri; + channel->GetURI(getter_AddRefs(uri)); + nsCAutoString spec; + uri->GetSpec(spec); + LOG(("PUT starting for %s", spec.get())); + } + + return channel->AsyncOpen(streamListener, channel); } NS_IMETHODIMP diff --git a/extensions/webdav/src/nsWebDAVUtils.cpp b/extensions/webdav/src/nsWebDAVUtils.cpp index eb1be6c1afb..e1e15f51013 100644 --- a/extensions/webdav/src/nsWebDAVUtils.cpp +++ b/extensions/webdav/src/nsWebDAVUtils.cpp @@ -40,6 +40,7 @@ #include "nsIDOMNode.h" #include "nsIDOM3Node.h" #include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" #if defined(PR_LOGGING) PRLogModuleInfo *gDAVLog = nsnull; @@ -77,3 +78,24 @@ NS_WD_ElementTextChildValue(nsIDOMElement *elt, const nsAString &tagName, return node3->GetTextContent(value); } +nsresult +NS_WD_AppendElementWithNS(nsIDOMDocument *doc, nsIDOMNode *parent, + const nsAString& ns, const nsAString& tagName, + nsIDOMElement **child) +{ + nsresult rv; + nsCOMPtr childElt; + nsCOMPtr junk; + + rv = doc->CreateElementNS(ns, tagName, getter_AddRefs(childElt)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = parent->AppendChild(childElt, getter_AddRefs(junk)); + NS_ENSURE_SUCCESS(rv, rv); + + *child = childElt.get(); + NS_ADDREF(*child); + + return NS_OK; +} + diff --git a/extensions/webdav/tests/davshell.js b/extensions/webdav/tests/davshell.js new file mode 100644 index 00000000000..50e8ae1ae83 --- /dev/null +++ b/extensions/webdav/tests/davshell.js @@ -0,0 +1,182 @@ +const C = Components; +const CI = C.interfaces; + +function getService(contract, iface) +{ + return C.classes[contract].getService(CI[iface]); +} + +function createInstance(contract, iface) +{ + return C.classes[contract].createInstance(CI[iface]); +} + +const davSvc = getService("@mozilla.org/webdav/service;1", + "nsIWebDAVService"); + +function Resource(url) +{ + this.urlSpec = url; +} + +Resource.prototype = { + QueryInterface: function(outer, iid) { + if (iid.equals(CI.nsIWebDAVResource) || + iid.equals(CI.nsISupports)) { + return this; + } + + throw Components.interfaces.NS_NO_INTERFACE; + } +}; + +function ResourceWithFileData(url, filename) +{ + this.urlSpec = url; + this.mFilename = filename; +} + +ResourceWithFileData.prototype = { + QueryInterface: function(outer, iid) { + if (iid.equals(CI.nsIWebDAVResourceWithData)) + return this; + + return Resource.prototype.QueryInterface.call(this, outer, iid); + }, + + __proto__: Resource.prototype +}; + +function propertiesToKeyArray(props) +{ + return props.getKeys({ }); +} + +function propertiesToObject(props) +{ + var count = { }; + var keys = props.getKeys({ }); + + var propObj = { }; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var val = props.get(keys[i], CI.nsISupportsString); + val = val.data; + propObj[key] = val; + } + + return propObj; +} + +function OperationListener() +{ +} + +OperationListener.prototype = +{ + onPutResult: function (status, resource) + { + dump("PUT " + resource.urlSpec + " complete: " + status + "\n"); + stopEventPump(); + }, + + onGetResult: function (status, resource) + { + dump("GET " + resource.urlSpec + " complete: " + status + "\n"); + stopEventPump(); + } +} + +function PropfindListener() +{ +} + +PropfindListener.prototype = +{ + onGetPropertyNamesResult: function (status, URL, props) + { + var keys = propertiesToKeyArray(props); + dump(URL + " (" + status + "):\n"); + for (var i = 0; i < keys.length; i++ ) + dump(" " + keys[i] + "\n"); + }, + + onGetPropertiesResult: function (status, URL, props) + { + var propObj = propertiesToObject(props); + dump(URL + " (" + status + "):\n"); + + for (var i in propObj) { + dump(" " + i + " = " + propObj[i] + "\n"); + } + }, + + onMetadataComplete: function (status, resource, method) + { + dump(method + " on " + resource.urlSpec + " completed: " + status + + "\n"); + stopEventPump(); + } +} + +const evQSvc = getService("@mozilla.org/event-queue-service;1", + "nsIEventQueueService"); +const ioSvc = getService("@mozilla.org/network/io-service;1", + "nsIIOService"); + +const evQ = evQSvc.getSpecialEventQueue(CI.nsIEventQueueService.CURRENT_THREAD_EVENT_QUEUE); + +function runEventPump() +{ + pumpRunning = true; + while (pumpRunning) { + evQ.processPendingEvents(); + } +} + +function stopEventPump() +{ + pumpRunning = false; +} + +function PROPFIND(url, depth, props) +{ + var listener = new PropfindListener(); + + if (props) { + var length = props.length; + } else { + var length = 0; + props = null; + } + + davSvc.getResourceProperties(new Resource(url), length, props, depth, + listener); + runEventPump(); +} + +function PROPFIND_names(url, depth) +{ + var listener = new PropfindListener(); + davSvc.getResourcePropertyNames(new Resource(url), depth, listener); + runEventPump(); +} + +function makeFile(path) +{ + var file = createInstance("@mozilla.org/file/local;1", "nsILocalFile"); + file.initWithPath(path); + return file; +} + +function GET(url, filename) +{ + var file = makeFile(filename); + var outstream = createInstance("@mozilla.org/network/file-output-stream;1", + "nsIFileOutputStream"); + outstream.init(file, 0x02 | 0x08, 0644, 0); + + davSvc.getToOutputStream(new Resource(url), outstream, + new OperationListener()); + runEventPump(); +}