1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsXPIDLString.h"
39 #include "nsHTTPSOAPTransport.h"
40 #include "nsIComponentManager.h"
41 #include "nsIDOMDocument.h"
42 #include "nsIDOMText.h"
44 #include "nsNetUtil.h"
45 #include "nsIScriptSecurityManager.h"
46 #include "nsIPrincipal.h"
47 #include "nsIVariant.h"
49 #include "nsSOAPUtils.h"
50 #include "nsSOAPCall.h"
51 #include "nsSOAPException.h"
52 #include "nsSOAPResponse.h"
53 #include "nsISOAPCallCompletion.h"
54 #include "nsIDOMEventTarget.h"
55 #include "nsIDOMSerializer.h"
56 #include "nsIWebScriptsAccessService.h"
58 #include "nsIDocument.h"
59 #include "nsIClassInfoImpl.h"
61 nsHTTPSOAPTransport::nsHTTPSOAPTransport()
65 nsHTTPSOAPTransport::~nsHTTPSOAPTransport()
69 NS_IMPL_ISUPPORTS1_CI(nsHTTPSOAPTransport
, nsISOAPTransport
)
71 #define DEBUG_DUMP_DOCUMENT(message,doc) \
75 nsCOMPtr<nsIDOMSerializer> serializer(do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rcc));\
76 if (NS_FAILED(rcc)) return rcc; \
77 rcc = serializer->SerializeToString(doc, serial);\
78 if (NS_FAILED(rcc)) return rcc;\
79 printf(message ":\n%s\n", NS_ConvertUTF16toUTF8(serial).get());\
81 // Availble from the debugger...
82 nsresult
DebugPrintDOM(nsIDOMNode
* node
)
84 DEBUG_DUMP_DOCUMENT("DOM", node
) return NS_OK
;
87 #define DEBUG_DUMP_DOCUMENT(message,doc)
91 * This method will replace the target document's
92 * codebase principal with the subject codebase to
93 * override cross-domain checks. So use caution
94 * because this might lead to a serious security breach
96 * @param aDocument - The target/response document.
99 nsresult
ChangePrincipal(nsIDOMDocument
* aDocument
)
105 nsCOMPtr
<nsIScriptSecurityManager
> secMgr
=
106 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID
, &rv
);
107 NS_ENSURE_SUCCESS(rv
, rv
);
109 nsCOMPtr
<nsIDocument
> targetDoc(do_QueryInterface(aDocument
, &rv
));
110 NS_ENSURE_SUCCESS(rv
, rv
);
112 rv
= secMgr
->CheckSameOrigin(nsnull
, targetDoc
->GetDocumentURI());
113 // change the principal only if the script security
114 // manager has denied access.
116 nsCOMPtr
<nsIPrincipal
> subjectPrincipal
;
117 rv
= secMgr
->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal
));
118 if (NS_SUCCEEDED(rv
))
119 targetDoc
->SetPrincipal(subjectPrincipal
);
125 * Get and check the transport URI for accessibility. In the future,
126 * this might also attempt to automatically add a mustUnderstand
127 * header to messages for untrusted sources and send them anyway.
129 static nsresult
GetTransportURI(nsISOAPCall
* aCall
, nsAString
& aURI
)
131 nsresult rc
= aCall
->GetTransportURI(aURI
);
136 nsCOMPtr
<nsIURI
> uri
;
137 rc
= NS_NewURI(getter_AddRefs(uri
), aURI
, nsnull
);
141 nsCOMPtr
<nsIWebScriptsAccessService
> wsa_service
=
142 do_GetService(NS_WEBSCRIPTSACCESSSERVICE_CONTRACTID
, &rc
);
146 PRBool safe
= PR_FALSE
;
147 rc
= aCall
->GetVerifySourceHeader(&safe
);
151 nsCOMPtr
<nsIScriptSecurityManager
> secMan
;
152 PRBool accessGranted
;
154 rc
= wsa_service
->CanAccess(uri
, NS_LITERAL_STRING("soap"), &accessGranted
);
157 if (!accessGranted
) {
158 // Get security manager, check to see if we're allowed to call this URI.
159 secMan
= do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID
, &rc
);
162 if (NS_FAILED(secMan
->CheckConnect(nsnull
, uri
, "SOAPCall", "invoke")))
163 return SOAP_EXCEPTION(NS_ERROR_FAILURE
,
164 "SOAP_INVOKE_DISABLED",
165 "SOAPCall.invoke not enabled by client");
169 // Get security manager, check to see if we're allowed to call this URI.
170 secMan
= do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID
, &rc
);
173 rc
= wsa_service
->CanAccess(uri
, NS_LITERAL_STRING("soapv"), &accessGranted
);
176 if (!accessGranted
) {
177 if (NS_FAILED(secMan
->CheckConnect(nsnull
, uri
, "SOAPCall", "invokeVerifySourceHeader")))
178 return SOAP_EXCEPTION(NS_ERROR_FAILURE
,
179 "SOAP_INVOKE_VERIFY_DISABLED",
180 "SOAPCall.invokeVerifySourceHeader not enabled by client");
183 nsAutoString sourceURI
;
187 nsCOMPtr
<nsIPrincipal
> principal
;
188 rc
= secMan
->GetSubjectPrincipal(getter_AddRefs(principal
));
192 return SOAP_EXCEPTION(NS_ERROR_FAILURE
,
193 "SOAP_INVOKE_VERIFY_PRINCIPAL",
194 "Source-verified message cannot be sent without principal.");
197 nsCOMPtr
<nsIURI
> uri
;
198 principal
->GetURI(getter_AddRefs(uri
));
200 return SOAP_EXCEPTION(NS_ERROR_FAILURE
,
201 "SOAP_INVOKE_VERIFY_URI",
202 "Source-verified message cannot be sent without URI.");
206 rc
= uri
->GetSpec(spec
);
209 CopyASCIItoUTF16(spec
, sourceURI
);
212 // Adding a header to tell the server that it must understand and verify the source of the call
214 nsCOMPtr
<nsIDOMElement
> element
;
215 rc
= aCall
->GetHeader(getter_AddRefs(element
));
219 return SOAP_EXCEPTION(NS_ERROR_FAILURE
,"SOAP_INVOKE_VERIFY_HEADER", "Source-verified message cannot be sent without a header.");
220 // Node ignored on remove / append calls
221 nsCOMPtr
<nsIDOMNode
> ignore
;
222 // Remove any existing elements that may conflict with this verifySource identification
223 nsCOMPtr
<nsIDOMElement
> verifySource
;
225 nsSOAPUtils::GetSpecificChildElement(nsnull
, element
, gSOAPStrings
->kVerifySourceNamespaceURI
,
226 gSOAPStrings
->kVerifySourceHeader
, getter_AddRefs(verifySource
));
228 rc
= element
->RemoveChild(verifySource
, getter_AddRefs(ignore
));
235 // Document as factory
236 nsCOMPtr
<nsIDOMDocument
> document
;
237 rc
= element
->GetOwnerDocument(getter_AddRefs(document
));
240 // Proper version to use of SOAP
242 rc
= aCall
->GetVersion(&version
);
245 // Proper schema to use for types
248 nsAutoString SOAPEncURI
;
249 if (version
== nsISOAPMessage::VERSION_1_1
) {
250 XSURI
.Assign(gSOAPStrings
->kXSURI1999
);
251 XSIURI
.Assign(gSOAPStrings
->kXSIURI1999
);
252 SOAPEncURI
.Assign(gSOAPStrings
->kSOAPEncURI11
);
255 XSURI
.Assign(gSOAPStrings
->kXSURI
);
256 XSIURI
.Assign(gSOAPStrings
->kXSIURI
);
257 SOAPEncURI
.Assign(gSOAPStrings
->kSOAPEncURI
);
259 // Create the header and append it with mustUnderstand and normal encoding.
260 rc
= document
->CreateElementNS(gSOAPStrings
->kVerifySourceNamespaceURI
,
261 gSOAPStrings
->kVerifySourceHeader
,
262 getter_AddRefs(verifySource
));
265 rc
= element
->AppendChild(verifySource
, getter_AddRefs(ignore
));
268 rc
= verifySource
->SetAttributeNS(*gSOAPStrings
->kSOAPEnvURI
[version
],
269 gSOAPStrings
->kMustUnderstandAttribute
,gSOAPStrings
->kTrueA
);// mustUnderstand
272 rc
= verifySource
->SetAttributeNS(*gSOAPStrings
->kSOAPEnvURI
[version
],
273 gSOAPStrings
->kEncodingStyleAttribute
, SOAPEncURI
);// 1.2 encoding
277 // Prefixed string for xsi:string
278 nsAutoString stringType
;
281 rc
= nsSOAPUtils::MakeNamespacePrefix(nsnull
, verifySource
, XSURI
, stringType
);
284 stringType
.Append(gSOAPStrings
->kQualifiedSeparator
);
285 stringType
.AppendLiteral("anyURI");
288 // If it is available, add the sourceURI
289 if (!sourceURI
.IsEmpty()) {
290 rc
= document
->CreateElementNS(gSOAPStrings
->kVerifySourceNamespaceURI
,
291 gSOAPStrings
->kVerifySourceURI
,getter_AddRefs(element
));
294 rc
= verifySource
->AppendChild(element
, getter_AddRefs(ignore
));
297 rc
= element
->SetAttributeNS(XSIURI
,
298 gSOAPStrings
->kXSITypeAttribute
,stringType
);
301 nsCOMPtr
<nsIDOMText
> text
;
302 rc
= document
->CreateTextNode(sourceURI
, getter_AddRefs(text
));
305 rc
= element
->AppendChild(text
, getter_AddRefs(ignore
));
314 nsHTTPSOAPTransport::SetupRequest(nsISOAPCall
* aCall
, PRBool async
,
315 nsIXMLHttpRequest
** ret
)
318 nsCOMPtr
<nsIXMLHttpRequest
> request
= do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID
, &rv
);
323 rv
= GetTransportURI(aCall
, uri
);
326 if (AStringIsNull(uri
))
327 return SOAP_EXCEPTION(NS_ERROR_NOT_INITIALIZED
,"SOAP_TRANSPORT_URI", "No transport URI was specified.");
329 rv
= request
->OverrideMimeType(NS_LITERAL_CSTRING("application/xml"));
333 const nsAString
& empty
= EmptyString();
334 rv
= request
->OpenRequest(NS_LITERAL_CSTRING("POST"),
335 NS_ConvertUTF16toUTF8(uri
), async
, empty
, empty
);
340 rv
= aCall
->GetActionURI(action
);
344 // nsXMLHttpRequest sends nsIDOMDocument payloads encoded as UTF-8,
345 // but it doesn't say so in the Content-Type header. For SOAP
346 // requests, we prefer to make this explicit. Some implementations
349 // XXX : Use application/soap+xml for SOAP 1.2 once it gets
350 // registered by IANA.
351 rv
= request
->SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
352 NS_LITERAL_CSTRING("text/xml; charset=UTF-8"));
356 // Apache Axis web services WSDL files say to set soapAction to "" and require it to be sent.
357 // So only check if its not void instead of using AStringIsNull.
358 if (!action
.IsVoid()) {
360 //XXXdoron necko doesn't allow empty header values, so set it to " "
361 if (action
.IsEmpty())
362 action
.AssignLiteral(" ");
364 rv
= request
->SetRequestHeader(NS_LITERAL_CSTRING("SOAPAction"),
365 NS_ConvertUTF16toUTF8(action
));
371 NS_ADDREF(*ret
= request
);
376 /* void syncCall (in nsISOAPCall aCall, in nsISOAPResponse aResponse); */
378 nsHTTPSOAPTransport::SyncCall(nsISOAPCall
* aCall
, nsISOAPResponse
* aResponse
)
380 NS_ENSURE_ARG(aCall
);
382 nsCOMPtr
<nsIDOMDocument
> messageDocument
;
383 nsresult rv
= aCall
->GetMessage(getter_AddRefs(messageDocument
));
386 if (!messageDocument
)
387 return SOAP_EXCEPTION(NS_ERROR_NOT_INITIALIZED
,"SOAP_MESSAGE_DOCUMENT", "No message document is present.");
388 DEBUG_DUMP_DOCUMENT("Synchronous Request", messageDocument
)
390 nsCOMPtr
<nsIXMLHttpRequest
> request
;
391 rv
= SetupRequest(aCall
, PR_FALSE
, getter_AddRefs(request
));
395 nsCOMPtr
<nsIWritableVariant
> variant
=
396 do_CreateInstance(NS_VARIANT_CONTRACTID
, &rv
);
400 rv
= variant
->SetAsInterface(NS_GET_IID(nsIDOMDocument
),
406 rv
= request
->Send(variant
);
412 rv
= request
->GetStatus(&status
);
413 if (NS_SUCCEEDED(rv
) && (status
< 200 || status
>= 300))
414 rv
= NS_ERROR_FAILURE
;
420 nsCOMPtr
<nsIDOMDocument
> response
;
421 rv
= request
->GetResponseXML(getter_AddRefs(response
));
425 DEBUG_DUMP_DOCUMENT("Asynchronous Response", response
)}
426 rv
= aResponse
->SetMessage(response
);
434 NS_IMPL_ISUPPORTS2_CI(nsHTTPSOAPTransportCompletion
, nsIDOMEventListener
,
435 nsISOAPCallCompletion
)
436 nsHTTPSOAPTransportCompletion::nsHTTPSOAPTransportCompletion()
440 nsHTTPSOAPTransportCompletion::nsHTTPSOAPTransportCompletion(nsISOAPCall
* call
, nsISOAPResponse
* response
, nsIXMLHttpRequest
* request
, nsISOAPResponseListener
* listener
) :
441 mCall(call
), mResponse(response
), mRequest(request
), mListener(listener
)
445 nsHTTPSOAPTransportCompletion::~nsHTTPSOAPTransportCompletion()
449 /* readonly attribute nsISOAPCall call; */
450 NS_IMETHODIMP
nsHTTPSOAPTransportCompletion::GetCall(nsISOAPCall
* *aCall
)
452 NS_ENSURE_ARG(aCall
);
454 NS_IF_ADDREF(*aCall
);
458 /* readonly attribute nsISOAPResponse response; */
460 nsHTTPSOAPTransportCompletion::GetResponse(nsISOAPResponse
*
463 NS_ENSURE_ARG(aResponse
);
465 mRequest
? nsnull
: mResponse
.get();
466 NS_IF_ADDREF(*aResponse
);
470 /* readonly attribute nsISOAPResponseListener listener; */
472 nsHTTPSOAPTransportCompletion::GetListener(nsISOAPResponseListener
*
475 NS_ENSURE_ARG(aListener
);
476 *aListener
= mListener
;
477 NS_IF_ADDREF(*aListener
);
481 /* readonly attribute boolean isComplete; */
483 nsHTTPSOAPTransportCompletion::GetIsComplete(PRBool
* aIsComplete
)
485 NS_ENSURE_ARG(aIsComplete
);
486 *aIsComplete
= mRequest
== nsnull
;
490 /* boolean abort (); */
491 NS_IMETHODIMP
nsHTTPSOAPTransportCompletion::Abort(PRBool
* _retval
)
493 NS_ENSURE_ARG(_retval
);
495 if (NS_SUCCEEDED(mRequest
->Abort())) {
506 nsHTTPSOAPTransportCompletion::HandleEvent(nsIDOMEvent
* aEvent
)
508 NS_ENSURE_ARG(aEvent
);
511 if (mRequest
) { // Avoid if it has been aborted.
513 rv
= mRequest
->GetStatus(&status
);
514 if (NS_SUCCEEDED(rv
) && (status
< 200 || status
>= 300))
515 rv
= NS_ERROR_FAILURE
;
517 if (mResponse
) { // && NS_SUCCEEDED(rv)) {
518 nsCOMPtr
<nsIDOMDocument
> document
;
519 rv
= mRequest
->GetResponseXML(getter_AddRefs(document
));
520 if (NS_SUCCEEDED(rv
) && document
) {
521 rv
= mResponse
->SetMessage(document
);
522 ChangePrincipal(document
);
523 DEBUG_DUMP_DOCUMENT("Asynchronous Response", document
)
531 nsCOMPtr
<nsISOAPCallCompletion
> kungFuDeathGrip
= this;
532 mRequest
= nsnull
; // Break cycle of references by releasing the request.
533 PRBool c
; // In other transports, this may signal to stop returning if multiple returns
534 mListener
->HandleResponse(mResponse
, mCall
, rv
, PR_TRUE
, &c
);
539 /* void asyncCall (in nsISOAPCall aCall, in nsISOAPResponseListener aListener, in nsISOAPResponse aResponse); */
541 nsHTTPSOAPTransport::AsyncCall(nsISOAPCall
* aCall
,
542 nsISOAPResponseListener
* aListener
,
543 nsISOAPResponse
* aResponse
,
544 nsISOAPCallCompletion
** aCompletion
)
546 NS_ENSURE_ARG(aCall
);
547 NS_ENSURE_ARG(aCompletion
);
550 nsCOMPtr
<nsIDOMDocument
> messageDocument
;
551 nsresult rv
= aCall
->GetMessage(getter_AddRefs(messageDocument
));
554 if (!messageDocument
)
555 return SOAP_EXCEPTION(NS_ERROR_NOT_INITIALIZED
,"SOAP_MESSAGE_DOCUMENT", "No message document is present.");
556 DEBUG_DUMP_DOCUMENT("Asynchronous Request", messageDocument
)
558 nsCOMPtr
<nsIXMLHttpRequest
> request
;
559 rv
= SetupRequest(aCall
, PR_TRUE
, getter_AddRefs(request
));
563 nsCOMPtr
<nsIDOMEventTarget
> eventTarget
=
564 do_QueryInterface(request
, &rv
);
568 nsCOMPtr
<nsIWritableVariant
> variant
=
569 do_CreateInstance(NS_VARIANT_CONTRACTID
, &rv
);
573 rv
= variant
->SetAsInterface(NS_GET_IID(nsIDOMDocument
),
578 nsCOMPtr
<nsISOAPCallCompletion
> completion
;
582 new nsHTTPSOAPTransportCompletion(aCall
, aResponse
, request
,
585 return NS_ERROR_OUT_OF_MEMORY
;
587 nsCOMPtr
<nsIDOMEventListener
> listener
=
588 do_QueryInterface(completion
);
589 rv
= eventTarget
->AddEventListener(NS_LITERAL_STRING("load"), listener
,
593 rv
= eventTarget
->AddEventListener(NS_LITERAL_STRING("error"),
598 rv
= request
->Send(variant
);
602 *aCompletion
= completion
;
603 NS_IF_ADDREF(*aCompletion
);
608 /* void addListener (in nsISOAPTransportListener aListener, in boolean aCapture); */
610 nsHTTPSOAPTransport::AddListener(nsISOAPTransportListener
* aListener
,
613 return NS_ERROR_NOT_IMPLEMENTED
;
616 /* void removeListener (in nsISOAPTransportListener aListener, in boolean aCapture); */
618 nsHTTPSOAPTransport::RemoveListener(nsISOAPTransportListener
*
619 aListener
, PRBool aCapture
)
621 return NS_ERROR_NOT_IMPLEMENTED
;
624 nsHTTPSSOAPTransport::nsHTTPSSOAPTransport()
628 nsHTTPSSOAPTransport::~nsHTTPSSOAPTransport()
632 NS_IMPL_ISUPPORTS1_CI(nsHTTPSSOAPTransport
, nsISOAPTransport
)