Import from 1.9a8 tarball
[mozilla-extra.git] / extensions / webdav / src / nsOperationStreamListeners.cpp
bloba8502524e0e1265f0ea90e33fdf3a5c46886e251
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
14 * License.
16 * The Original Code is Mozilla.
18 * The Initial Developer of the Original Code is
19 * Oracle Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 2004
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
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"
45 #include "nsIURL.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"
60 #include "nsString.h"
62 #include "nsISupportsPrimitives.h"
64 class OperationStreamListener : public nsIStreamListener
66 public:
67 NS_DECL_ISUPPORTS
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,
78 nsISupports *closure,
79 nsIOutputStream *outstream,
80 PRUint32 mode) :
81 mResource(resource), mListener(listener), mClosure(closure),
82 mOutputStream(outstream), mOperation(mode) { }
84 virtual ~OperationStreamListener() { }
86 protected:
87 virtual nsresult SignalCompletion(PRUint32 status)
89 mListener->OnOperationComplete(status, mResource, mOperation,
90 mClosure);
91 if (mOutputStream)
92 return mOutputStream->Flush();
93 return NS_OK;
96 virtual void SignalDetail(PRUint32 statusCode, const nsACString &resource,
97 nsISupports *detail);
99 virtual nsresult ProcessResponse(nsIDOMElement *responseElt);
101 nsresult StatusAndHrefFromResponse(nsIDOMElement *responseElt,
102 nsACString &href,
103 PRUint32 *statusCode);
105 nsCOMPtr<nsIWebDAVResource> mResource;
106 nsCOMPtr<nsIWebDAVOperationListener> mListener;
107 nsCOMPtr<nsISupports> mClosure;
108 nsCOMPtr<nsIOutputStream> mOutputStream;
109 PRUint32 mOperation;
110 nsCString mBody;
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)
120 NS_INTERFACE_MAP_END
122 NS_IMETHODIMP
123 OperationStreamListener::OnStartRequest(nsIRequest *aRequest,
124 nsISupports *aContext)
126 LOG(("OperationStreamListener::OnStartRequest() entered"));
127 mBody.Truncate();
128 return NS_OK;
131 NS_IMETHODIMP
132 OperationStreamListener::OnStopRequest(nsIRequest *aRequest,
133 nsISupports *aContext,
134 nsresult aStatusCode)
136 PRUint32 status, rv;
137 nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aContext);
139 LOG(("OperationStreamListener::OnStopRequest() entered"));
141 rv = channel ? channel->GetResponseStatus(&status) : NS_ERROR_UNEXPECTED;
143 if (NS_FAILED(rv))
144 return SignalCompletion(rv);
146 if (status != 207)
147 return SignalCompletion(status);
149 // ZZZ Check content-length against mBody.Length()
151 // Now we parse!
152 nsCOMPtr<nsIDOMNodeList> responseList;
153 PRUint32 length;
154 rv = NS_WD_GetDocAndResponseListFromBuffer(mBody, getter_AddRefs(mXMLDoc),
155 getter_AddRefs(responseList),
156 &length);
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);
174 return NS_OK;
177 NS_METHOD
178 OperationStreamListener::StreamReaderCallback(nsIInputStream *aInputStream,
179 void *aClosure,
180 const char *aRawSegment,
181 PRUint32 aToOffset,
182 PRUint32 aCount,
183 PRUint32 *aWriteCount)
185 OperationStreamListener *osl = static_cast<OperationStreamListener *>
186 (aClosure);
187 osl->mBody.Append(aRawSegment, aCount);
188 *aWriteCount = aCount;
189 return NS_OK;
192 NS_IMETHODIMP
193 OperationStreamListener::OnDataAvailable(nsIRequest *aRequest,
194 nsISupports *aContext,
195 nsIInputStream *aInputStream,
196 PRUint32 offset, PRUint32 count)
198 LOG(("OperationStreamListener::OnDataAvailable() entered"));
199 PRUint32 result;
200 nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aContext);
201 if (!channel)
202 result = NS_ERROR_UNEXPECTED;
204 PRBool succeeded = PR_FALSE;
205 channel->GetRequestSucceeded(&succeeded);
206 if (!succeeded) {
207 aRequest->Cancel(NS_BINDING_ABORTED);
208 return NS_BINDING_ABORTED;
211 PRUint32 totalRead;
212 return aInputStream->ReadSegments(StreamReaderCallback, this, count,
213 &totalRead);
216 void
217 OperationStreamListener::SignalDetail(PRUint32 statusCode,
218 const nsACString &resource,
219 nsISupports *detail)
221 nsCOMPtr<nsIURL> resourceURL, detailURL;
222 nsCOMPtr<nsIURI> detailURI;
223 if (NS_FAILED(mResource->GetResourceURL(getter_AddRefs(resourceURL))))
224 return;
225 if (resource.IsEmpty()) {
226 detailURL = resourceURL;
227 } else {
228 // if this URL is relative, resolve it.
229 nsresult rv;
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))) {
239 return;
242 mListener->OnOperationDetail(statusCode, detailURL, mOperation, detail,
243 mClosure);
246 nsresult
247 OperationStreamListener::ProcessResponse(nsIDOMElement *responseElt)
249 nsCAutoString href;
250 PRUint32 statusCode;
251 nsresult rv = StatusAndHrefFromResponse(responseElt, href, &statusCode);
252 NS_ENSURE_SUCCESS(rv, rv);
254 SignalDetail(statusCode, href, nsnull);
255 return NS_OK;
258 nsresult
259 OperationStreamListener::StatusAndHrefFromResponse(nsIDOMElement *responseElt,
260 nsACString &href,
261 PRUint32 *statusCode)
263 nsAutoString hrefString;
264 nsresult rv = NS_WD_ElementTextChildValue(responseElt,
265 NS_LITERAL_STRING("href"),
266 hrefString);
267 NS_ENSURE_SUCCESS(rv, rv);
268 href = NS_ConvertUTF16toUTF8(hrefString);
270 nsAutoString statusString;
271 rv = NS_WD_ElementTextChildValue(responseElt, NS_LITERAL_STRING("status"),
272 statusString);
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);
278 PRInt32 res = 0;
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;
287 return NS_OK;
290 class PropfindStreamListener : public OperationStreamListener
292 public:
293 NS_DECL_ISUPPORTS_INHERITED
295 PropfindStreamListener(nsIWebDAVResource *resource,
296 nsIWebDAVOperationListener *listener,
297 nsISupports *closure,
298 PRBool isPropname) :
299 OperationStreamListener(resource, listener, closure, nsnull,
300 isPropname ?
301 (PRUint32)nsIWebDAVOperationListener::GET_PROPERTY_NAMES :
302 (PRUint32)nsIWebDAVOperationListener::GET_PROPERTIES) { }
303 virtual ~PropfindStreamListener() { }
304 protected:
306 virtual nsresult ProcessResponse(nsIDOMElement *responseElt);
307 virtual nsresult PropertiesFromPropElt(nsIDOMElement *elt,
308 nsIProperties **retProps);
311 NS_IMPL_ISUPPORTS_INHERITED0(PropfindStreamListener, OperationStreamListener)
313 nsresult
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);
326 PRUint32 length;
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);
340 PRUint16 type;
341 node->GetNodeType(&type);
342 if (type != nsIDOMNode::ELEMENT_NODE)
343 continue;
345 realProps++;
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);
354 nsString nsStr;
355 rv = node->GetNamespaceURI(nsStr);
356 NS_ENSURE_SUCCESS(rv, rv);
358 nsString propName;
359 rv = node->GetLocalName(propName);
360 NS_ENSURE_SUCCESS(rv, rv);
362 NS_ConvertUTF16toUTF8 propkey(nsStr + NS_LITERAL_STRING(" ") +
363 propName);
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);
368 continue;
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);
382 nsString valueStr;
383 encoder->EncodeToString(valueStr);
385 nsCOMPtr<nsISupportsString>
386 suppString(do_CreateInstance("@mozilla.org/supports-string;1",
387 &rv));
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));
399 return NS_OK;
402 nsresult
403 PropfindStreamListener::ProcessResponse(nsIDOMElement *responseElt)
405 nsCAutoString href;
406 PRUint32 statusCode;
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);
418 PRUint32 length;
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);
441 return NS_OK;
445 class ReportStreamListener : public OperationStreamListener
447 public:
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() { }
457 protected:
458 virtual nsresult ProcessResponse(nsIDOMElement *responseElt);
461 NS_IMPL_ISUPPORTS_INHERITED0(ReportStreamListener, OperationStreamListener)
463 nsresult
464 ReportStreamListener::ProcessResponse(nsIDOMElement *responseElt)
466 nsCAutoString href;
467 PRUint32 statusCode;
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);
474 return NS_OK;
478 class GetToStringStreamListener : public OperationStreamListener
480 public:
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() { }
491 protected:
492 NS_IMETHOD OnStopRequest(nsIRequest *aRequest,
493 nsISupports *aContext,
494 nsresult aStatusCode);
497 NS_IMPL_ISUPPORTS_INHERITED0(GetToStringStreamListener, OperationStreamListener)
499 NS_IMETHODIMP
500 GetToStringStreamListener::OnStopRequest(nsIRequest *aRequest,
501 nsISupports *aContext,
502 nsresult aStatusCode)
504 PRUint32 status, rv;
505 nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aContext);
507 LOG(("OperationStreamListener::OnStopRequest() entered"));
509 rv = channel ? channel->GetResponseStatus(&status) : NS_ERROR_UNEXPECTED;
511 if (NS_FAILED(rv))
512 return SignalCompletion(rv);
514 if (status != 200)
515 return SignalCompletion(status);
517 nsCOMPtr<nsISupportsCString>
518 suppString(do_CreateInstance("@mozilla.org/supports-cstring;1",
519 &rv));
520 NS_ENSURE_SUCCESS(rv, rv);
521 suppString->SetData(mBody);
523 SignalDetail(status, nsCAutoString(""), suppString);
524 SignalCompletion(status);
525 return NS_OK;
528 nsIStreamListener *
529 NS_WD_NewPropfindStreamListener(nsIWebDAVResource *resource,
530 nsIWebDAVOperationListener *listener,
531 nsISupports *closure,
532 PRBool isPropname)
534 return new PropfindStreamListener(resource, listener, closure, isPropname);
537 nsIStreamListener *
538 NS_WD_NewReportStreamListener(nsIWebDAVResource *resource,
539 nsIWebDAVOperationListener *listener,
540 nsISupports *closure)
542 return new ReportStreamListener(resource, listener, closure);
545 nsresult
546 NS_WD_NewOperationStreamListener(nsIWebDAVResource *resource,
547 nsIWebDAVOperationListener *listener,
548 nsISupports *closure,
549 PRUint32 operation,
550 nsIStreamListener **streamListener)
552 nsCOMPtr<nsIRequestObserver> osl =
553 new OperationStreamListener(resource, listener, closure, nsnull,
554 operation);
555 if (!osl)
556 return NS_ERROR_OUT_OF_MEMORY;
557 return CallQueryInterface(osl, streamListener);
560 nsresult
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);
570 if (!osl)
571 return NS_ERROR_OUT_OF_MEMORY;
572 return CallQueryInterface(osl, observer);
575 nsresult
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);
583 if (!osl)
584 return NS_ERROR_OUT_OF_MEMORY;
585 return CallQueryInterface(osl, streamListener);