1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 // vim:expandtab:ts=4 sw=4:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Mozilla.
18 * The Initial Developer of the Original Code is
20 * Portions created by the Initial Developer are Copyright (C) 2004
21 * the Initial Developer. All Rights Reserved.
24 * Mike Shaver <shaver@off.net> (original author)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsWebDAVInternal.h"
42 #include "nsIHttpChannel.h"
43 #include "nsIIOService.h"
44 #include "nsNetUtil.h"
47 #include "nsIDOM3Node.h"
48 #include "nsIDOMDocument.h"
49 #include "nsIDOMElement.h"
50 #include "nsIDOMNodeList.h"
51 #include "nsIDOMRange.h"
53 #include "nsIDocument.h"
54 #include "nsIDocumentEncoder.h"
55 #include "nsContentCID.h"
57 #include "nsIWebDAVResource.h"
58 #include "nsIWebDAVListener.h"
62 #include "nsISupportsPrimitives.h"
64 class OperationStreamListener
: public nsIStreamListener
68 NS_DECL_NSIREQUESTOBSERVER
69 NS_DECL_NSISTREAMLISTENER
71 static NS_METHOD
StreamReaderCallback(nsIInputStream
*in
, void *closure
,
72 const char *fromRawSegment
,
73 PRUint32 toOffset
, PRUint32 count
,
74 PRUint32
*writeCount
);
76 OperationStreamListener(nsIWebDAVResource
*resource
,
77 nsIWebDAVOperationListener
*listener
,
79 nsIOutputStream
*outstream
,
81 mResource(resource
), mListener(listener
), mClosure(closure
),
82 mOutputStream(outstream
), mOperation(mode
) { }
84 virtual ~OperationStreamListener() { }
87 virtual nsresult
SignalCompletion(PRUint32 status
)
89 mListener
->OnOperationComplete(status
, mResource
, mOperation
,
92 return mOutputStream
->Flush();
96 virtual void SignalDetail(PRUint32 statusCode
, const nsACString
&resource
,
99 virtual nsresult
ProcessResponse(nsIDOMElement
*responseElt
);
101 nsresult
StatusAndHrefFromResponse(nsIDOMElement
*responseElt
,
103 PRUint32
*statusCode
);
105 nsCOMPtr
<nsIWebDAVResource
> mResource
;
106 nsCOMPtr
<nsIWebDAVOperationListener
> mListener
;
107 nsCOMPtr
<nsISupports
> mClosure
;
108 nsCOMPtr
<nsIOutputStream
> mOutputStream
;
111 nsCOMPtr
<nsIDOMDocument
> mXMLDoc
;
114 NS_IMPL_ADDREF(OperationStreamListener
)
115 NS_IMPL_RELEASE(OperationStreamListener
)
116 NS_INTERFACE_MAP_BEGIN(OperationStreamListener
)
117 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
118 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
119 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIStreamListener
)
123 OperationStreamListener::OnStartRequest(nsIRequest
*aRequest
,
124 nsISupports
*aContext
)
126 LOG(("OperationStreamListener::OnStartRequest() entered"));
132 OperationStreamListener::OnStopRequest(nsIRequest
*aRequest
,
133 nsISupports
*aContext
,
134 nsresult aStatusCode
)
137 nsCOMPtr
<nsIHttpChannel
> channel
= do_QueryInterface(aContext
);
139 LOG(("OperationStreamListener::OnStopRequest() entered"));
141 rv
= channel
? channel
->GetResponseStatus(&status
) : NS_ERROR_UNEXPECTED
;
144 return SignalCompletion(rv
);
147 return SignalCompletion(status
);
149 // ZZZ Check content-length against mBody.Length()
152 nsCOMPtr
<nsIDOMNodeList
> responseList
;
154 rv
= NS_WD_GetDocAndResponseListFromBuffer(mBody
, getter_AddRefs(mXMLDoc
),
155 getter_AddRefs(responseList
),
158 LOG(("found %d responses", length
));
160 for (PRUint32 i
= 0; i
< length
; i
++) {
161 nsCOMPtr
<nsIDOMNode
> responseNode
;
162 rv
= responseList
->Item(i
, getter_AddRefs(responseNode
));
163 NS_ENSURE_SUCCESS(rv
, SignalCompletion(rv
));
165 nsCOMPtr
<nsIDOMElement
> responseElt
=
166 do_QueryInterface(responseNode
, &rv
);
167 NS_ENSURE_SUCCESS(rv
, SignalCompletion(rv
));
169 rv
= ProcessResponse(responseElt
);
170 NS_ENSURE_SUCCESS(rv
, SignalCompletion(rv
));
173 SignalCompletion(status
);
178 OperationStreamListener::StreamReaderCallback(nsIInputStream
*aInputStream
,
180 const char *aRawSegment
,
183 PRUint32
*aWriteCount
)
185 OperationStreamListener
*osl
= static_cast<OperationStreamListener
*>
187 osl
->mBody
.Append(aRawSegment
, aCount
);
188 *aWriteCount
= aCount
;
193 OperationStreamListener::OnDataAvailable(nsIRequest
*aRequest
,
194 nsISupports
*aContext
,
195 nsIInputStream
*aInputStream
,
196 PRUint32 offset
, PRUint32 count
)
198 LOG(("OperationStreamListener::OnDataAvailable() entered"));
200 nsCOMPtr
<nsIHttpChannel
> channel
= do_QueryInterface(aContext
);
202 result
= NS_ERROR_UNEXPECTED
;
204 PRBool succeeded
= PR_FALSE
;
205 channel
->GetRequestSucceeded(&succeeded
);
207 aRequest
->Cancel(NS_BINDING_ABORTED
);
208 return NS_BINDING_ABORTED
;
212 return aInputStream
->ReadSegments(StreamReaderCallback
, this, count
,
217 OperationStreamListener::SignalDetail(PRUint32 statusCode
,
218 const nsACString
&resource
,
221 nsCOMPtr
<nsIURL
> resourceURL
, detailURL
;
222 nsCOMPtr
<nsIURI
> detailURI
;
223 if (NS_FAILED(mResource
->GetResourceURL(getter_AddRefs(resourceURL
))))
225 if (resource
.IsEmpty()) {
226 detailURL
= resourceURL
;
228 // if this URL is relative, resolve it.
230 nsCAutoString resolvedSpec
;
231 rv
= resourceURL
->Resolve(resource
, resolvedSpec
);
233 // XXX better error handling
234 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to resolve remote URL!");
236 if (NS_FAILED(resourceURL
->Clone(getter_AddRefs(detailURI
))) ||
237 !(detailURL
= do_QueryInterface(detailURI
)) ||
238 NS_FAILED(detailURI
->SetSpec(resolvedSpec
))) {
242 mListener
->OnOperationDetail(statusCode
, detailURL
, mOperation
, detail
,
247 OperationStreamListener::ProcessResponse(nsIDOMElement
*responseElt
)
251 nsresult rv
= StatusAndHrefFromResponse(responseElt
, href
, &statusCode
);
252 NS_ENSURE_SUCCESS(rv
, rv
);
254 SignalDetail(statusCode
, href
, nsnull
);
259 OperationStreamListener::StatusAndHrefFromResponse(nsIDOMElement
*responseElt
,
261 PRUint32
*statusCode
)
263 nsAutoString hrefString
;
264 nsresult rv
= NS_WD_ElementTextChildValue(responseElt
,
265 NS_LITERAL_STRING("href"),
267 NS_ENSURE_SUCCESS(rv
, rv
);
268 href
= NS_ConvertUTF16toUTF8(hrefString
);
270 nsAutoString statusString
;
271 rv
= NS_WD_ElementTextChildValue(responseElt
, NS_LITERAL_STRING("status"),
273 // XXX if we don't find a status element (or hit any other parse error,
274 // for that matter) we need to signal this back to the caller
276 NS_ENSURE_SUCCESS(rv
, rv
);
279 NS_ConvertUTF16toUTF8
statusUTF8(statusString
);
280 LOG(("status: %s", statusUTF8
.get()));
281 PRInt32 statusVal
= nsCAutoString(Substring(statusUTF8
,
282 8)).ToInteger(&res
, 10);
283 NS_ENSURE_SUCCESS(res
, (nsresult
)res
);
285 *statusCode
= (PRUint32
)statusVal
;
290 class PropfindStreamListener
: public OperationStreamListener
293 NS_DECL_ISUPPORTS_INHERITED
295 PropfindStreamListener(nsIWebDAVResource
*resource
,
296 nsIWebDAVOperationListener
*listener
,
297 nsISupports
*closure
,
299 OperationStreamListener(resource
, listener
, closure
, nsnull
,
301 (PRUint32
)nsIWebDAVOperationListener::GET_PROPERTY_NAMES
:
302 (PRUint32
)nsIWebDAVOperationListener::GET_PROPERTIES
) { }
303 virtual ~PropfindStreamListener() { }
306 virtual nsresult
ProcessResponse(nsIDOMElement
*responseElt
);
307 virtual nsresult
PropertiesFromPropElt(nsIDOMElement
*elt
,
308 nsIProperties
**retProps
);
311 NS_IMPL_ISUPPORTS_INHERITED0(PropfindStreamListener
, OperationStreamListener
)
314 PropfindStreamListener::PropertiesFromPropElt(nsIDOMElement
*propElt
,
315 nsIProperties
**retProps
)
317 nsresult rv
= CallCreateInstance(NS_PROPERTIES_CONTRACTID
, retProps
);
318 NS_ENSURE_SUCCESS(rv
, rv
);
320 nsIProperties
*props
= *retProps
;
322 nsCOMPtr
<nsIDOMNodeList
> list
;
323 rv
= propElt
->GetChildNodes(getter_AddRefs(list
));
324 NS_ENSURE_SUCCESS(rv
, rv
);
327 rv
= list
->GetLength(&length
);
328 NS_ENSURE_SUCCESS(rv
, rv
);
330 LOG(("%d properties found", length
));
332 PRUint32 realProps
= 0;
334 for (PRUint32 i
= 0; i
< length
; i
++) {
335 nsCOMPtr
<nsIDOMNode
> node
;
336 nsCOMPtr
<nsIDOM3Node
> node3
;
337 rv
= list
->Item(i
, getter_AddRefs(node
));
338 NS_ENSURE_SUCCESS(rv
, rv
);
341 node
->GetNodeType(&type
);
342 if (type
!= nsIDOMNode::ELEMENT_NODE
)
347 nsCOMPtr
<nsIDOMRange
> range
=
348 do_CreateInstance("@mozilla.org/content/range;1", &rv
);
349 NS_ENSURE_SUCCESS(rv
, rv
);
351 rv
= range
->SelectNodeContents(node
);
352 NS_ENSURE_SUCCESS(rv
, rv
);
355 rv
= node
->GetNamespaceURI(nsStr
);
356 NS_ENSURE_SUCCESS(rv
, rv
);
359 rv
= node
->GetLocalName(propName
);
360 NS_ENSURE_SUCCESS(rv
, rv
);
362 NS_ConvertUTF16toUTF8
propkey(nsStr
+ NS_LITERAL_STRING(" ") +
364 if (mOperation
== nsIWebDAVOperationListener::GET_PROPERTY_NAMES
) {
365 LOG((" propname: %s", propkey
.get()));
366 rv
= props
->Set(propkey
.get(), nsnull
);
367 NS_ENSURE_SUCCESS(rv
, rv
);
371 nsCOMPtr
<nsIDocumentEncoder
> encoder
=
372 do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE
"text/xml", &rv
);
374 // This method will fail if no document
375 rv
= encoder
->Init(mXMLDoc
, NS_LITERAL_STRING("text/xml"),
376 nsIDocumentEncoder::OutputEncodeBasicEntities
);
377 NS_ENSURE_SUCCESS(rv
, rv
);
379 rv
= encoder
->SetRange(range
);
380 NS_ENSURE_SUCCESS(rv
, rv
);
383 encoder
->EncodeToString(valueStr
);
385 nsCOMPtr
<nsISupportsString
>
386 suppString(do_CreateInstance("@mozilla.org/supports-string;1",
388 NS_ENSURE_SUCCESS(rv
, rv
);
389 suppString
->SetData(valueStr
);
391 LOG((" %s = %s", propkey
.get(),
392 NS_ConvertUTF16toUTF8(valueStr
).get()));
393 rv
= props
->Set(propkey
.get(), suppString
);
394 NS_ENSURE_SUCCESS(rv
, rv
);
397 LOG(("%d real properties added", realProps
));
403 PropfindStreamListener::ProcessResponse(nsIDOMElement
*responseElt
)
407 nsresult rv
= StatusAndHrefFromResponse(responseElt
, href
, &statusCode
);
408 NS_ENSURE_SUCCESS(rv
, rv
);
410 LOG(("response for %s: %d", href
.get(), statusCode
));
412 nsCOMPtr
<nsIDOMNodeList
> proplist
;
413 rv
= responseElt
->GetElementsByTagNameNS(NS_LITERAL_STRING("DAV:"),
414 NS_LITERAL_STRING("propstat"),
415 getter_AddRefs(proplist
));
416 NS_ENSURE_SUCCESS(rv
, rv
);
419 rv
= proplist
->GetLength(&length
);
420 NS_ENSURE_SUCCESS(rv
, rv
);
422 nsCOMPtr
<nsIDOMNode
> node
;
423 for (PRUint32 i
= 0; i
< length
; i
++) {
424 rv
= proplist
->Item(i
, getter_AddRefs(node
));
425 NS_ENSURE_SUCCESS(rv
, rv
);
427 nsCOMPtr
<nsIDOMElement
> propstatElt
= do_QueryInterface(node
, &rv
);
428 NS_ENSURE_SUCCESS(rv
, rv
);
430 nsCOMPtr
<nsIDOMElement
> elt
;
431 rv
= NS_WD_GetElementByTagName(propstatElt
, NS_LITERAL_STRING("prop"),
432 getter_AddRefs(elt
));
433 NS_ENSURE_SUCCESS(rv
, rv
);
435 nsCOMPtr
<nsIProperties
> props
;
436 rv
= PropertiesFromPropElt(elt
, getter_AddRefs(props
));
437 NS_ENSURE_SUCCESS(rv
, rv
);
439 SignalDetail(statusCode
, href
, props
);
445 class ReportStreamListener
: public OperationStreamListener
448 NS_DECL_ISUPPORTS_INHERITED
450 ReportStreamListener(nsIWebDAVResource
*resource
,
451 nsIWebDAVOperationListener
*listener
,
452 nsISupports
*closure
) :
453 OperationStreamListener(resource
, listener
, closure
, nsnull
,
454 nsIWebDAVOperationListener::REPORT
) { }
455 virtual ~ReportStreamListener() { }
458 virtual nsresult
ProcessResponse(nsIDOMElement
*responseElt
);
461 NS_IMPL_ISUPPORTS_INHERITED0(ReportStreamListener
, OperationStreamListener
)
464 ReportStreamListener::ProcessResponse(nsIDOMElement
*responseElt
)
468 nsresult rv
= StatusAndHrefFromResponse(responseElt
, href
, &statusCode
);
469 NS_ENSURE_SUCCESS(rv
, rv
);
471 LOG(("response for %s: %d", href
.get(), statusCode
));
473 SignalDetail(statusCode
, href
, responseElt
);
478 class GetToStringStreamListener
: public OperationStreamListener
481 NS_DECL_ISUPPORTS_INHERITED
483 GetToStringStreamListener(nsIWebDAVResource
*resource
,
484 nsIWebDAVOperationListener
*listener
,
485 nsISupports
*closure
) :
486 OperationStreamListener(resource
, listener
, closure
, nsnull
,
487 nsIWebDAVOperationListener::GET_TO_STRING
)
490 virtual ~GetToStringStreamListener() { }
492 NS_IMETHOD
OnStopRequest(nsIRequest
*aRequest
,
493 nsISupports
*aContext
,
494 nsresult aStatusCode
);
497 NS_IMPL_ISUPPORTS_INHERITED0(GetToStringStreamListener
, OperationStreamListener
)
500 GetToStringStreamListener::OnStopRequest(nsIRequest
*aRequest
,
501 nsISupports
*aContext
,
502 nsresult aStatusCode
)
505 nsCOMPtr
<nsIHttpChannel
> channel
= do_QueryInterface(aContext
);
507 LOG(("OperationStreamListener::OnStopRequest() entered"));
509 rv
= channel
? channel
->GetResponseStatus(&status
) : NS_ERROR_UNEXPECTED
;
512 return SignalCompletion(rv
);
515 return SignalCompletion(status
);
517 nsCOMPtr
<nsISupportsCString
>
518 suppString(do_CreateInstance("@mozilla.org/supports-cstring;1",
520 NS_ENSURE_SUCCESS(rv
, rv
);
521 suppString
->SetData(mBody
);
523 SignalDetail(status
, nsCAutoString(""), suppString
);
524 SignalCompletion(status
);
529 NS_WD_NewPropfindStreamListener(nsIWebDAVResource
*resource
,
530 nsIWebDAVOperationListener
*listener
,
531 nsISupports
*closure
,
534 return new PropfindStreamListener(resource
, listener
, closure
, isPropname
);
538 NS_WD_NewReportStreamListener(nsIWebDAVResource
*resource
,
539 nsIWebDAVOperationListener
*listener
,
540 nsISupports
*closure
)
542 return new ReportStreamListener(resource
, listener
, closure
);
546 NS_WD_NewOperationStreamListener(nsIWebDAVResource
*resource
,
547 nsIWebDAVOperationListener
*listener
,
548 nsISupports
*closure
,
550 nsIStreamListener
**streamListener
)
552 nsCOMPtr
<nsIRequestObserver
> osl
=
553 new OperationStreamListener(resource
, listener
, closure
, nsnull
,
556 return NS_ERROR_OUT_OF_MEMORY
;
557 return CallQueryInterface(osl
, streamListener
);
561 NS_WD_NewGetOperationRequestObserver(nsIWebDAVResource
*resource
,
562 nsIWebDAVOperationListener
*listener
,
563 nsISupports
*closure
,
564 nsIOutputStream
*outstream
,
565 nsIRequestObserver
**observer
)
567 nsCOMPtr
<nsIRequestObserver
> osl
=
568 new OperationStreamListener(resource
, listener
, closure
, outstream
,
569 nsIWebDAVOperationListener::GET
);
571 return NS_ERROR_OUT_OF_MEMORY
;
572 return CallQueryInterface(osl
, observer
);
576 NS_WD_NewGetToStringOperationRequestObserver(nsIWebDAVResource
*resource
,
577 nsIWebDAVOperationListener
*listener
,
578 nsISupports
*closure
,
579 nsIStreamListener
**streamListener
)
581 nsCOMPtr
<nsIRequestObserver
> osl
=
582 new GetToStringStreamListener(resource
, listener
, closure
);
584 return NS_ERROR_OUT_OF_MEMORY
;
585 return CallQueryInterface(osl
, streamListener
);